github.com/imran-kn/cilium-fork@v1.6.9/contrib/release/relnotes (about)

     1  #!/bin/bash
     2  #
     3  # Copyright 2016 The Kubernetes Authors All rights reserved.
     4  #
     5  # Licensed under the Apache License, Version 2.0 (the "License");
     6  # you may not use this file except in compliance with the License.
     7  # You may obtain a copy of the License at
     8  #
     9  #     http://www.apache.org/licenses/LICENSE-2.0
    10  #
    11  # Unless required by applicable law or agreed to in writing, software
    12  # distributed under the License is distributed on an "AS IS" BASIS,
    13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  # See the License for the specific language governing permissions and
    15  # limitations under the License.
    16  #
    17  # Set PROGram name
    18  PROG=${0##*/}
    19  ########################################################################
    20  #+
    21  #+ NAME
    22  #+     $PROG - Generate release notes for K8S releases
    23  #+
    24  #+ SYNOPSIS
    25  #+     $PROG  [--quiet] [[starttag..]endtag] [--htmlize-md] [--full]
    26  #+            [--github-token=<token>] [--branch=<branch>]
    27  #+            [--markdown-file=<file>] [--html-file=<file>]
    28  #+            [--preview] [--verbose]
    29  #+     $PROG  [--helpshort|--usage|-?]
    30  #+     $PROG  [--help|-man]
    31  #+
    32  #+ DESCRIPTION
    33  #+      $PROG scans 'git log' for 'merge pull's and collects and displays
    34  #+      release notes based on release-note-* labels from PR titles and PR
    35  #+      release-note blocks (in the body).
    36  #+
    37  #+      By default, $PROG produces release notes for the last github release
    38  #+      to the HEAD of current branch and uses a standard git range otherwise.
    39  #+      You may omit the end of the range to terminate the range at the HEAD
    40  #+      of the current branch.
    41  #+
    42  #+      The default output is pure markdown and unless [--markdown-file=] is
    43  #+      specified, the output file is in /tmp/$PROG-release-notes.md.
    44  #+      If [--html-file=] is set, $PROG will also produce a pure html version
    45  #+      of the notes at that location.
    46  #+
    47  #+      If [--quiet] is not specified, the output to stdout will always be
    48  #+      only the markdown version of the output.
    49  #+
    50  #+      [--branch=] is used to specify a branch other than the current one.
    51  #+
    52  #+      Other options detailed in EXAMPLES below.
    53  #+
    54  #+ OPTIONS
    55  #+     --quiet                   - Don't display the notes when done
    56  #+     --htmlize-md              - Output markdown with html for PRs and
    57  #+                                 contributors (for use in CHANGELOG-x.y.md)
    58  #+     --full                    - Force 'full' release format to show all
    59  #+                                 sections of release notes. (This is the
    60  #+                                 *default* for new branch X.Y.0 notes)
    61  #+     --markdown-file=          - Specify an alt file to use to store notes
    62  #+     --html-file=              - Produce a html version of the notes
    63  #+     --preview                 - Report additional branch statistics (used for
    64  #+                                 reporting outside of releases)
    65  #+     --github-token=           - Must be specified if GITHUB_TOKEN not set
    66  #+     --branch=                 - Specify a branch other than the current one
    67  #+     [--help | -man]           - Display man page for this script
    68  #+     [--usage | -?]            - Display in-line usage
    69  #+
    70  #+ EXAMPLES
    71  #+     $PROG                     - Notes for last release to HEAD
    72  #+                                 on current branch
    73  #+     $PROG v1.1.4..            - Notes for v1.1.4 to HEAD on current branch
    74  #+     $PROG v1.1.4..v1.1.7      - Notes for v1.1.4..v1.1.7
    75  #+     $PROG v1.1.7              - Notes for last release
    76  #+                                 on current branch to v1.1.7
    77  #+
    78  #+ FILES
    79  #+     /tmp/$PROG-release-htmls.md
    80  #+     /tmp/$PROG-release-htmls.html
    81  #+
    82  #+ SEE ALSO
    83  #+     common.sh                 - Base function definitions
    84  #+     gitlib.sh                 - git-related function definitions
    85  #+     https://stedolan.github.io/jq - JSON CLI
    86  #+
    87  #+ BUGS/TODO
    88  #+
    89  ########################################################################
    90  # If NO ARGUMENTS should return *usage*, uncomment the following line:
    91  #usage=${1-yes}
    92  
    93  source $(dirname $(readlink -ne $BASH_SOURCE))/lib/common.sh
    94  source $TOOL_LIB_PATH/gitlib.sh
    95  
    96  # Validate command-line
    97  common::argc_validate 2
    98  
    99  ###############################################################################
   100  # FUNCTIONS
   101  ###############################################################################
   102  ###############################################################################
   103  # Get titles from a list of PRs
   104  # @param prs - A space separated list of PRs to extract
   105  #
   106  extract_pr_title () {
   107    local prs="$*"
   108    local pr
   109    local content
   110    local body
   111    local author
   112    local pull_json
   113  
   114    for pr in $prs; do
   115      pull_json="$($GHCURL $CILIUM_GITHUB_API/pulls/$pr)"
   116      [[ -z "$pull_json" ]] && return 1
   117      body="$(echo "$pull_json" |jq -r '.body' |tr -d '\r')"
   118  
   119      # Look for a body release note first and default to title
   120      # * indent lines >1
   121      # Try to account for and adjust user-entered formatting
   122      # * This is somewhat complicated by the fact that we convert this to
   123      #   html, for things like email, group posts, dashboards, and there
   124      #   is a disconnect between pandoc and github's markdown for lists.
   125      #   https://github.com/jgm/pandoc/issues/2210
   126      content=$(echo "$body" |\
   127                sed -n '/^``` *release-note/,/^```/{/^```/!p;/^```$/q}' |\
   128                sed -e '/^$/d' -e '2,$s/^\( *\* \)/        \1/g' \
   129                    -e '2,$s/^\( *[^ \*]\)/    * \1/g')
   130  
   131      # if the release-note block is empty (unchanged from the template), use title
   132      if [[ -z "$content" ]]; then
   133        content=$(echo "$pull_json" | jq -r '.title')
   134      fi
   135  
   136      author=$(echo "$pull_json" | jq -r '.user.login')
   137      content=$(echo "$content" |sed -e '1s/^ *\** */* /g' \
   138                                     -e "1s/$/ (#$pr, @$author)/g")
   139  
   140      logecho -r "$content"
   141    done
   142  }
   143  
   144  ###############################################################################
   145  # Get merged PRs that match a specified label
   146  get_prs () {
   147    local label=$1
   148    local -a prs
   149    local total
   150    local current_page
   151    local num_pages
   152    local prs_per_page
   153    local url
   154  
   155    prs_per_page=100
   156  
   157    url="https://api.github.com/search/issues?per_page=1&q=is:pr%20repo:cilium/cilium%20is:closed%20label:$label"
   158    total="$($GHCURL $url | jq -r '.total_count')"
   159  
   160    # Calculate number of pages, rounding up
   161    num_pages=$(((total + prs_per_page - 1) / prs_per_page ))
   162  
   163    for current_page in `seq 1 $num_pages`; do
   164      prs+=($($GHCURL "${CILIUM_GITHUB_SEARCHAPI}label:${label}&page=$current_page" | jq -r '.items[] | (.number | tostring)'))
   165    done
   166  
   167    echo "${prs[@]}"
   168  }
   169  
   170  
   171  ###############################################################################
   172  # Create the release note markdown body
   173  # @param release_tars - A directory containing tarballs to link to on GCS
   174  # @param start_tag - The start tag of range
   175  # @param release_tag - The release tag of range
   176  #
   177  create_body () {
   178    local release_tars=$1
   179    local start_tag=$2
   180    local release_tag=$3
   181    local title
   182  
   183    ((FLAGS_preview)) && title="Branch "
   184    # Show a more useful header if release_tag == HEAD
   185    if [[ "$release_tag" == "HEAD" ]]; then
   186      title+=$CURRENT_BRANCH
   187    else
   188      title+=$release_tag
   189    fi
   190  
   191    ((FLAGS_preview)) && echo "**Release Note Preview - generated on $(date)**"
   192    echo
   193    echo "# $title"
   194    echo
   195    cat $PR_NOTES
   196  }
   197  
   198  
   199  ###############################################################################
   200  # Returns true if the SHA is within the git revision range
   201  commit_in_release() {
   202    local sha="$1"
   203    local range="$2"
   204  
   205    echo $sha $range
   206  
   207    git log --pretty=oneline "$range" 2> /dev/null | grep "$sha" > /dev/null 2>&1
   208  }
   209  
   210  ###############################################################################
   211  # Generates the release notes for one particular PR
   212  generate_release_note_for_pr () {
   213    local pr=$1
   214    local url
   215    local pr_json
   216    local merge_sha
   217    local merged_at
   218  
   219    url="https://api.github.com/repos/cilium/cilium/pulls/$pr"
   220    pr_json="$($GHCURL $url)"
   221    merge_sha="$(echo -n $pr_json | jq -r '.merge_commit_sha')"
   222    merged_at="$(echo -n $pr_json | jq -r '.merged_at')"
   223  
   224    if ((FLAGS_verbose)); then
   225      logecho "PR: $pr SHA: $merge_sha Merged-at: $merged_at"
   226    fi
   227  
   228    if [[ "$merge_sha" = "null" ]]; then
   229      if ((FLAGS_verbose)); then
   230        logecho "  No merge SHA"
   231      fi
   232      return
   233    fi
   234  
   235    if [[ "$merged_at" = "null" ]]; then
   236      if ((FLAGS_verbose)); then
   237        logecho "  Never merged"
   238      fi
   239      return
   240    fi
   241  
   242    if ! commit_in_release $merge_sha "$range"; then
   243      if ((FLAGS_verbose)); then
   244        logecho "  Not in release ($range)"
   245      fi
   246      return
   247    fi
   248  
   249    extract_pr_title "$pr" >> $PR_NOTES
   250  }
   251  
   252  ###############################################################################
   253  # Scan PRs for release-note-* labels and generate markdown for the actual
   254  # release notes section of the report
   255  # Uses global LAST_RELEASE CURRENT_BRANCH
   256  generate_notes () {
   257    local branch_head
   258    local range
   259    local start_tag
   260    local release_tag
   261    local labels
   262    local body
   263    local tempcss=/tmp/$PROG-ca.$$
   264    local tempfile=/tmp/$PROG-file-1.$$
   265    local anchor
   266    local -a normal_prs
   267    local -a action_prs
   268    local -a notes_normal
   269    local -a notes_action
   270    local -a prs
   271  
   272    branch_head=$(git rev-parse refs/remotes/origin/$CURRENT_BRANCH 2>/dev/null)
   273  
   274    # If ${LAST_RELEASE[$CURRENT_BRANCH]} is unset attempt to get the last
   275    # release from the parent branch and then master
   276    : ${LAST_RELEASE[$CURRENT_BRANCH]:=${LAST_RELEASE[${CURRENT_BRANCH%.*}]}}
   277    : ${LAST_RELEASE[$CURRENT_BRANCH]:=${LAST_RELEASE[master]}}
   278  
   279    # Default
   280    release_tag="${POSITIONAL_ARGV[0]}"
   281    range="${POSITIONAL_ARGV[1]}"
   282  
   283    [[ "$range" =~ (.*)\.\.(.*) ]] &&
   284      start_tag=${BASH_REMATCH[1]}
   285  
   286    if [[ -z "$start_tag" ]]; then
   287      common::exit 1 "Unable to set beginning of range automatically." \
   288                     "Specify on the command-line. Exiting..."
   289    fi
   290  
   291    #range="$start_tag..$release_tag"
   292    logecho "Release tag range: $range"
   293  
   294    # If range is unterminated, finish it with $branch_head
   295    #[[ $range =~ \.\.$ ]] && range+=$branch_head
   296  
   297    # Validate range
   298    if ! git rev-parse $range &>/dev/null; then
   299      logecho
   300      logecho "Invalid tags/range $range !"
   301      return 1
   302    fi
   303  
   304    logecho
   305  
   306    logecho "Scanning for all merged PRs in the repository requiring release notes on the $CURRENT_BRANCH branch..."
   307    major_prs=($(get_prs "release-note/major"))
   308    echo "Major PRs: ${#major_prs[@]}"
   309  
   310    minor_prs=($(get_prs "release-note/minor"))
   311    echo "Minor PRs: ${#minor_prs[@]}"
   312  
   313    bug_prs=($(get_prs "release-note/bug"))
   314    echo "Bugfix PRs: ${#bug_prs[@]}"
   315  
   316    logecho
   317    logecho "Generating release notes..."
   318  
   319    if [[ ${#major_prs[@]} > 0 ]]; then
   320      echo "Major Changes:" >> $PR_NOTES
   321      for pr in "${major_prs[@]}"; do
   322        generate_release_note_for_pr $pr
   323      done
   324    fi
   325  
   326    if [[ ${#bug_prs[@]} > 0 ]]; then
   327      echo >> $PR_NOTES
   328      echo "Bugfixes Changes:" >> $PR_NOTES
   329      for pr in "${bug_prs[@]}"; do
   330        generate_release_note_for_pr $pr
   331      done
   332    fi
   333  
   334    if [[ ${#minor_prs[@]} > 0 ]]; then
   335      echo >> $PR_NOTES
   336      echo "Other Changes:" >> $PR_NOTES
   337      for pr in "${minor_prs[@]}"; do
   338        generate_release_note_for_pr $pr
   339      done
   340    fi
   341  
   342    echo >> $PR_NOTES
   343  
   344    logecho "Preparing layout..."
   345    create_body ${FLAGS_release_tars:-""} $start_tag ${release_tag:-HEAD} \
   346     > $RELEASE_NOTES_MD
   347  
   348    if ((FLAGS_htmlize_md)) && [[ ! $release_tag =~ ${VER_REGEX[dotzero]} ]]; then
   349      # Make users and PRs linkable
   350      # Also, expand anchors (needed for email announce())
   351      sed -i -e "s,#\([0-9]\{5\,\}\),[#\1]($CILIUM_GITHUB_URL/pull/\1),g" \
   352          -e "s,\(#v[0-9]\{3\}-\),$CILIUM_GITHUB_URL/blob/master/$changelog_file\1,g" \
   353          -e "s,@\([a-zA-Z0-9-]*\),[@\1](https://github.com/\1),g" \
   354       $RELEASE_NOTES_MD
   355    fi
   356  
   357    if [[ -n "$RELEASE_NOTES_HTML" ]]; then
   358      echo "<style type=text/css>" \
   359           "table,th,tr,td {border: 1px solid gray;" \
   360           "border-collapse: collapse;padding: 5px;}" \
   361           "</style>" > $tempcss
   362  
   363      pandoc -H $tempcss --from markdown_github --to html \
   364       $RELEASE_NOTES_MD > $RELEASE_NOTES_HTML
   365  
   366      # Remove temp file
   367      logrun rm -f $tempcss
   368    fi
   369  }
   370  
   371  ##############################################################################
   372  # CONSTANTS
   373  ##############################################################################
   374  CURRENT_BRANCH=${FLAGS_branch:-$(gitlib::current_branch)} \
   375   || common::exit 1
   376  
   377  PR_NOTES=/tmp/$PROG-$CURRENT_BRANCH-prnotes
   378  # Initialize new PR_NOTES for session
   379  >$PR_NOTES
   380  RELEASE_NOTES_MD=$(common::absolute_path \
   381                     ${FLAGS_markdown_file:-/tmp/$PROG-$CURRENT_BRANCH.md})
   382  RELEASE_NOTES_HTML=$(common::absolute_path $FLAGS_html_file)
   383  ANNOUNCEMENT_TEXT=/tmp/$PROG-announcement
   384  
   385  ###############################################################################
   386  # MAIN
   387  ###############################################################################
   388  # Initialize and save up to 10 (rotated logs)
   389  MYLOG=/tmp/$PROG.log
   390  common::logfileinit $MYLOG 10
   391  
   392  # BEGIN script
   393  common::timestamp begin
   394  
   395  # Check token
   396  gitlib::github_api_token
   397  
   398  # Check for packages
   399  common::check_packages jq pandoc
   400  
   401  # Build LAST_RELEASE dictionary
   402  gitlib::last_releases
   403  
   404  generate_notes || common::exit 1
   405  
   406  common::timestamp end