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