1*cc02d7e2SAndroid Build Coastguard Worker# Copyright 2019 gRPC authors. 2*cc02d7e2SAndroid Build Coastguard Worker# 3*cc02d7e2SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*cc02d7e2SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*cc02d7e2SAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*cc02d7e2SAndroid Build Coastguard Worker# 7*cc02d7e2SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*cc02d7e2SAndroid Build Coastguard Worker# 9*cc02d7e2SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*cc02d7e2SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*cc02d7e2SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*cc02d7e2SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*cc02d7e2SAndroid Build Coastguard Worker# limitations under the License. 14*cc02d7e2SAndroid Build Coastguard Worker"""Generate draft and release notes in Markdown from Github PRs. 15*cc02d7e2SAndroid Build Coastguard Worker 16*cc02d7e2SAndroid Build Coastguard WorkerYou'll need a github API token to avoid being rate-limited. See 17*cc02d7e2SAndroid Build Coastguard Workerhttps://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ 18*cc02d7e2SAndroid Build Coastguard Worker 19*cc02d7e2SAndroid Build Coastguard WorkerThis script collects PRs using "git log X..Y" from local repo where X and Y are 20*cc02d7e2SAndroid Build Coastguard Workertags or release branch names of previous and current releases respectively. 21*cc02d7e2SAndroid Build Coastguard WorkerTypically, notes are generated before the release branch is labelled so Y is 22*cc02d7e2SAndroid Build Coastguard Workeralmost always the name of the release branch. X is the previous release branch 23*cc02d7e2SAndroid Build Coastguard Workerif this is not a patch release. Otherwise, it is the previous release tag. 24*cc02d7e2SAndroid Build Coastguard WorkerFor example, for release v1.17.0, X will be origin/v1.16.x and for release v1.17.3, 25*cc02d7e2SAndroid Build Coastguard WorkerX will be v1.17.2. In both cases Y will be origin/v1.17.x. 26*cc02d7e2SAndroid Build Coastguard Worker 27*cc02d7e2SAndroid Build Coastguard Worker""" 28*cc02d7e2SAndroid Build Coastguard Worker 29*cc02d7e2SAndroid Build Coastguard Workerfrom collections import defaultdict 30*cc02d7e2SAndroid Build Coastguard Workerimport json 31*cc02d7e2SAndroid Build Coastguard Workerimport logging 32*cc02d7e2SAndroid Build Coastguard Workerimport re 33*cc02d7e2SAndroid Build Coastguard Workerimport subprocess 34*cc02d7e2SAndroid Build Coastguard Worker 35*cc02d7e2SAndroid Build Coastguard Workerimport urllib3 36*cc02d7e2SAndroid Build Coastguard Worker 37*cc02d7e2SAndroid Build Coastguard Workerlogging.basicConfig(level=logging.WARNING) 38*cc02d7e2SAndroid Build Coastguard Worker 39*cc02d7e2SAndroid Build Coastguard Workercontent_header = """Draft Release Notes For {version} 40*cc02d7e2SAndroid Build Coastguard Worker-- 41*cc02d7e2SAndroid Build Coastguard WorkerFinal release notes will be generated from the PR titles that have *"release notes:yes"* label. If you have any additional notes please add them below. These will be appended to auto generated release notes. Previous release notes are [here](https://github.com/grpc/grpc/releases). 42*cc02d7e2SAndroid Build Coastguard Worker 43*cc02d7e2SAndroid Build Coastguard Worker**Also, look at the PRs listed below against your name.** Please apply the missing labels and make necessary corrections (like fixing the title) to the PR in Github. Final release notes will be generated just before the release on {date}. 44*cc02d7e2SAndroid Build Coastguard Worker 45*cc02d7e2SAndroid Build Coastguard WorkerAdd additional notes not in PRs 46*cc02d7e2SAndroid Build Coastguard Worker-- 47*cc02d7e2SAndroid Build Coastguard Worker 48*cc02d7e2SAndroid Build Coastguard WorkerCore 49*cc02d7e2SAndroid Build Coastguard Worker- 50*cc02d7e2SAndroid Build Coastguard Worker 51*cc02d7e2SAndroid Build Coastguard Worker 52*cc02d7e2SAndroid Build Coastguard WorkerC++ 53*cc02d7e2SAndroid Build Coastguard Worker- 54*cc02d7e2SAndroid Build Coastguard Worker 55*cc02d7e2SAndroid Build Coastguard Worker 56*cc02d7e2SAndroid Build Coastguard WorkerC# 57*cc02d7e2SAndroid Build Coastguard Worker- 58*cc02d7e2SAndroid Build Coastguard Worker 59*cc02d7e2SAndroid Build Coastguard Worker 60*cc02d7e2SAndroid Build Coastguard WorkerObjective-C 61*cc02d7e2SAndroid Build Coastguard Worker- 62*cc02d7e2SAndroid Build Coastguard Worker 63*cc02d7e2SAndroid Build Coastguard Worker 64*cc02d7e2SAndroid Build Coastguard WorkerPHP 65*cc02d7e2SAndroid Build Coastguard Worker- 66*cc02d7e2SAndroid Build Coastguard Worker 67*cc02d7e2SAndroid Build Coastguard Worker 68*cc02d7e2SAndroid Build Coastguard WorkerPython 69*cc02d7e2SAndroid Build Coastguard Worker- 70*cc02d7e2SAndroid Build Coastguard Worker 71*cc02d7e2SAndroid Build Coastguard Worker 72*cc02d7e2SAndroid Build Coastguard WorkerRuby 73*cc02d7e2SAndroid Build Coastguard Worker- 74*cc02d7e2SAndroid Build Coastguard Worker 75*cc02d7e2SAndroid Build Coastguard Worker 76*cc02d7e2SAndroid Build Coastguard Worker""" 77*cc02d7e2SAndroid Build Coastguard Worker 78*cc02d7e2SAndroid Build Coastguard Workerrl_header = """This is release {version} ([{name}](https://github.com/grpc/grpc/blob/master/doc/g_stands_for.md)) of gRPC Core. 79*cc02d7e2SAndroid Build Coastguard Worker 80*cc02d7e2SAndroid Build Coastguard WorkerFor gRPC documentation, see [grpc.io](https://grpc.io/). For previous releases, see [Releases](https://github.com/grpc/grpc/releases). 81*cc02d7e2SAndroid Build Coastguard Worker 82*cc02d7e2SAndroid Build Coastguard WorkerThis release contains refinements, improvements, and bug fixes, with highlights listed below. 83*cc02d7e2SAndroid Build Coastguard Worker 84*cc02d7e2SAndroid Build Coastguard Worker 85*cc02d7e2SAndroid Build Coastguard Worker""" 86*cc02d7e2SAndroid Build Coastguard Worker 87*cc02d7e2SAndroid Build Coastguard WorkerHTML_URL = "https://github.com/grpc/grpc/pull/" 88*cc02d7e2SAndroid Build Coastguard WorkerAPI_URL = "https://api.github.com/repos/grpc/grpc/pulls/" 89*cc02d7e2SAndroid Build Coastguard Worker 90*cc02d7e2SAndroid Build Coastguard Worker 91*cc02d7e2SAndroid Build Coastguard Workerdef get_commit_detail(commit): 92*cc02d7e2SAndroid Build Coastguard Worker """Print commit and CL info for the commits that are submitted with CL-first workflow and warn the release manager to check manually.""" 93*cc02d7e2SAndroid Build Coastguard Worker glg_command = [ 94*cc02d7e2SAndroid Build Coastguard Worker "git", 95*cc02d7e2SAndroid Build Coastguard Worker "log", 96*cc02d7e2SAndroid Build Coastguard Worker "-n 1", 97*cc02d7e2SAndroid Build Coastguard Worker "%s" % commit, 98*cc02d7e2SAndroid Build Coastguard Worker ] 99*cc02d7e2SAndroid Build Coastguard Worker output = subprocess.check_output(glg_command).decode("utf-8", "ignore") 100*cc02d7e2SAndroid Build Coastguard Worker matches = re.search("Author:.*<(.*@).*>", output) 101*cc02d7e2SAndroid Build Coastguard Worker author = matches.group(1) 102*cc02d7e2SAndroid Build Coastguard Worker detail = "- " + author + " " 103*cc02d7e2SAndroid Build Coastguard Worker title = output.splitlines()[4].strip() 104*cc02d7e2SAndroid Build Coastguard Worker detail += "- " + title 105*cc02d7e2SAndroid Build Coastguard Worker if not title.endswith("."): 106*cc02d7e2SAndroid Build Coastguard Worker detail += "." 107*cc02d7e2SAndroid Build Coastguard Worker matches = re.search("PiperOrigin-RevId: ([0-9]+)$", output) 108*cc02d7e2SAndroid Build Coastguard Worker cl_num = matches.group(1) 109*cc02d7e2SAndroid Build Coastguard Worker detail += ( 110*cc02d7e2SAndroid Build Coastguard Worker " ([commit](https://github.com/grpc/grpc/commit/" 111*cc02d7e2SAndroid Build Coastguard Worker + commit 112*cc02d7e2SAndroid Build Coastguard Worker + ")) ([CL](https://critique.corp.google.com/cl/" 113*cc02d7e2SAndroid Build Coastguard Worker + cl_num 114*cc02d7e2SAndroid Build Coastguard Worker + "))" 115*cc02d7e2SAndroid Build Coastguard Worker ) 116*cc02d7e2SAndroid Build Coastguard Worker return detail 117*cc02d7e2SAndroid Build Coastguard Worker 118*cc02d7e2SAndroid Build Coastguard Worker 119*cc02d7e2SAndroid Build Coastguard Workerdef get_commit_log(prevRelLabel, relBranch): 120*cc02d7e2SAndroid Build Coastguard Worker """Return the output of 'git log prevRelLabel..relBranch'""" 121*cc02d7e2SAndroid Build Coastguard Worker 122*cc02d7e2SAndroid Build Coastguard Worker import subprocess 123*cc02d7e2SAndroid Build Coastguard Worker 124*cc02d7e2SAndroid Build Coastguard Worker glg_command = [ 125*cc02d7e2SAndroid Build Coastguard Worker "git", 126*cc02d7e2SAndroid Build Coastguard Worker "log", 127*cc02d7e2SAndroid Build Coastguard Worker "--pretty=oneline", 128*cc02d7e2SAndroid Build Coastguard Worker "%s..%s" % (prevRelLabel, relBranch), 129*cc02d7e2SAndroid Build Coastguard Worker ] 130*cc02d7e2SAndroid Build Coastguard Worker print(("Running ", " ".join(glg_command))) 131*cc02d7e2SAndroid Build Coastguard Worker return subprocess.check_output(glg_command).decode("utf-8", "ignore") 132*cc02d7e2SAndroid Build Coastguard Worker 133*cc02d7e2SAndroid Build Coastguard Worker 134*cc02d7e2SAndroid Build Coastguard Workerdef get_pr_data(pr_num): 135*cc02d7e2SAndroid Build Coastguard Worker """Get the PR data from github. Return 'error' on exception""" 136*cc02d7e2SAndroid Build Coastguard Worker http = urllib3.PoolManager( 137*cc02d7e2SAndroid Build Coastguard Worker retries=urllib3.Retry(total=7, backoff_factor=1), timeout=4.0 138*cc02d7e2SAndroid Build Coastguard Worker ) 139*cc02d7e2SAndroid Build Coastguard Worker url = API_URL + pr_num 140*cc02d7e2SAndroid Build Coastguard Worker try: 141*cc02d7e2SAndroid Build Coastguard Worker response = http.request( 142*cc02d7e2SAndroid Build Coastguard Worker "GET", url, headers={"Authorization": "token %s" % TOKEN} 143*cc02d7e2SAndroid Build Coastguard Worker ) 144*cc02d7e2SAndroid Build Coastguard Worker except urllib3.exceptions.HTTPError as e: 145*cc02d7e2SAndroid Build Coastguard Worker print("Request error:", e.reason) 146*cc02d7e2SAndroid Build Coastguard Worker return "error" 147*cc02d7e2SAndroid Build Coastguard Worker return json.loads(response.data.decode("utf-8")) 148*cc02d7e2SAndroid Build Coastguard Worker 149*cc02d7e2SAndroid Build Coastguard Worker 150*cc02d7e2SAndroid Build Coastguard Workerdef get_pr_titles(gitLogs): 151*cc02d7e2SAndroid Build Coastguard Worker import re 152*cc02d7e2SAndroid Build Coastguard Worker 153*cc02d7e2SAndroid Build Coastguard Worker # All commits 154*cc02d7e2SAndroid Build Coastguard Worker match_commit = "^([a-fA-F0-9]+) " 155*cc02d7e2SAndroid Build Coastguard Worker all_commits_set = set(re.findall(match_commit, gitLogs, re.MULTILINE)) 156*cc02d7e2SAndroid Build Coastguard Worker 157*cc02d7e2SAndroid Build Coastguard Worker error_count = 0 158*cc02d7e2SAndroid Build Coastguard Worker # PRs with merge commits 159*cc02d7e2SAndroid Build Coastguard Worker match_merge_pr = "^([a-fA-F0-9]+) .*Merge pull request #(\d+)" 160*cc02d7e2SAndroid Build Coastguard Worker matches = re.findall(match_merge_pr, gitLogs, re.MULTILINE) 161*cc02d7e2SAndroid Build Coastguard Worker merge_commits = [] 162*cc02d7e2SAndroid Build Coastguard Worker prlist_merge_pr = [] 163*cc02d7e2SAndroid Build Coastguard Worker if matches: 164*cc02d7e2SAndroid Build Coastguard Worker merge_commits, prlist_merge_pr = zip(*matches) 165*cc02d7e2SAndroid Build Coastguard Worker merge_commits_set = set(merge_commits) 166*cc02d7e2SAndroid Build Coastguard Worker print("\nPRs matching 'Merge pull request #<num>':") 167*cc02d7e2SAndroid Build Coastguard Worker print(prlist_merge_pr) 168*cc02d7e2SAndroid Build Coastguard Worker print("\n") 169*cc02d7e2SAndroid Build Coastguard Worker 170*cc02d7e2SAndroid Build Coastguard Worker # PRs using Github's squash & merge feature 171*cc02d7e2SAndroid Build Coastguard Worker match_sq = "^([a-fA-F0-9]+) .*\(#(\d+)\)$" 172*cc02d7e2SAndroid Build Coastguard Worker matches = re.findall(match_sq, gitLogs, re.MULTILINE) 173*cc02d7e2SAndroid Build Coastguard Worker if matches: 174*cc02d7e2SAndroid Build Coastguard Worker sq_commits, prlist_sq = zip(*matches) 175*cc02d7e2SAndroid Build Coastguard Worker sq_commits_set = set(sq_commits) 176*cc02d7e2SAndroid Build Coastguard Worker print("\nPRs matching '[PR Description](#<num>)$'") 177*cc02d7e2SAndroid Build Coastguard Worker print(prlist_sq) 178*cc02d7e2SAndroid Build Coastguard Worker print("\n") 179*cc02d7e2SAndroid Build Coastguard Worker prlist = list(prlist_merge_pr) + list(prlist_sq) 180*cc02d7e2SAndroid Build Coastguard Worker langs_pr = defaultdict(list) 181*cc02d7e2SAndroid Build Coastguard Worker for pr_num in prlist: 182*cc02d7e2SAndroid Build Coastguard Worker pr_num = str(pr_num) 183*cc02d7e2SAndroid Build Coastguard Worker print(("---------- getting data for PR " + pr_num)) 184*cc02d7e2SAndroid Build Coastguard Worker pr = get_pr_data(pr_num) 185*cc02d7e2SAndroid Build Coastguard Worker if pr == "error": 186*cc02d7e2SAndroid Build Coastguard Worker print( 187*cc02d7e2SAndroid Build Coastguard Worker ("\n***ERROR*** Error in getting data for PR " + pr_num + "\n") 188*cc02d7e2SAndroid Build Coastguard Worker ) 189*cc02d7e2SAndroid Build Coastguard Worker error_count += 1 190*cc02d7e2SAndroid Build Coastguard Worker continue 191*cc02d7e2SAndroid Build Coastguard Worker rl_no_found = False 192*cc02d7e2SAndroid Build Coastguard Worker rl_yes_found = False 193*cc02d7e2SAndroid Build Coastguard Worker lang_found = False 194*cc02d7e2SAndroid Build Coastguard Worker for label in pr["labels"]: 195*cc02d7e2SAndroid Build Coastguard Worker if label["name"] == "release notes: yes": 196*cc02d7e2SAndroid Build Coastguard Worker rl_yes_found = True 197*cc02d7e2SAndroid Build Coastguard Worker elif label["name"] == "release notes: no": 198*cc02d7e2SAndroid Build Coastguard Worker rl_no_found = True 199*cc02d7e2SAndroid Build Coastguard Worker elif label["name"].startswith("lang/"): 200*cc02d7e2SAndroid Build Coastguard Worker lang_found = True 201*cc02d7e2SAndroid Build Coastguard Worker lang = label["name"].split("/")[1].lower() 202*cc02d7e2SAndroid Build Coastguard Worker # lang = lang[0].upper() + lang[1:] 203*cc02d7e2SAndroid Build Coastguard Worker body = pr["title"] 204*cc02d7e2SAndroid Build Coastguard Worker if not body.endswith("."): 205*cc02d7e2SAndroid Build Coastguard Worker body = body + "." 206*cc02d7e2SAndroid Build Coastguard Worker 207*cc02d7e2SAndroid Build Coastguard Worker prline = ( 208*cc02d7e2SAndroid Build Coastguard Worker "- " + body + " ([#" + pr_num + "](" + HTML_URL + pr_num + "))" 209*cc02d7e2SAndroid Build Coastguard Worker ) 210*cc02d7e2SAndroid Build Coastguard Worker detail = "- " + pr["user"]["login"] + "@ " + prline 211*cc02d7e2SAndroid Build Coastguard Worker print(detail) 212*cc02d7e2SAndroid Build Coastguard Worker # if no RL label 213*cc02d7e2SAndroid Build Coastguard Worker if not rl_no_found and not rl_yes_found: 214*cc02d7e2SAndroid Build Coastguard Worker print(("Release notes label missing for " + pr_num)) 215*cc02d7e2SAndroid Build Coastguard Worker langs_pr["nolabel"].append(detail) 216*cc02d7e2SAndroid Build Coastguard Worker elif rl_yes_found and not lang_found: 217*cc02d7e2SAndroid Build Coastguard Worker print(("Lang label missing for " + pr_num)) 218*cc02d7e2SAndroid Build Coastguard Worker langs_pr["nolang"].append(detail) 219*cc02d7e2SAndroid Build Coastguard Worker elif rl_no_found: 220*cc02d7e2SAndroid Build Coastguard Worker print(("'Release notes:no' found for " + pr_num)) 221*cc02d7e2SAndroid Build Coastguard Worker langs_pr["notinrel"].append(detail) 222*cc02d7e2SAndroid Build Coastguard Worker elif rl_yes_found: 223*cc02d7e2SAndroid Build Coastguard Worker print( 224*cc02d7e2SAndroid Build Coastguard Worker ( 225*cc02d7e2SAndroid Build Coastguard Worker "'Release notes:yes' found for " 226*cc02d7e2SAndroid Build Coastguard Worker + pr_num 227*cc02d7e2SAndroid Build Coastguard Worker + " with lang " 228*cc02d7e2SAndroid Build Coastguard Worker + lang 229*cc02d7e2SAndroid Build Coastguard Worker ) 230*cc02d7e2SAndroid Build Coastguard Worker ) 231*cc02d7e2SAndroid Build Coastguard Worker langs_pr["inrel"].append(detail) 232*cc02d7e2SAndroid Build Coastguard Worker langs_pr[lang].append(prline) 233*cc02d7e2SAndroid Build Coastguard Worker commits_wo_pr = all_commits_set - merge_commits_set - sq_commits_set 234*cc02d7e2SAndroid Build Coastguard Worker for commit in commits_wo_pr: 235*cc02d7e2SAndroid Build Coastguard Worker langs_pr["nopr"].append(get_commit_detail(commit)) 236*cc02d7e2SAndroid Build Coastguard Worker 237*cc02d7e2SAndroid Build Coastguard Worker return langs_pr, error_count 238*cc02d7e2SAndroid Build Coastguard Worker 239*cc02d7e2SAndroid Build Coastguard Worker 240*cc02d7e2SAndroid Build Coastguard Workerdef write_draft(langs_pr, file, version, date): 241*cc02d7e2SAndroid Build Coastguard Worker file.write(content_header.format(version=version, date=date)) 242*cc02d7e2SAndroid Build Coastguard Worker file.write( 243*cc02d7e2SAndroid Build Coastguard Worker "Commits with missing PR number - please lookup the PR info in the corresponding CL and add to the additional notes if necessary.\n" 244*cc02d7e2SAndroid Build Coastguard Worker ) 245*cc02d7e2SAndroid Build Coastguard Worker file.write("---\n") 246*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 247*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["nopr"]: 248*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["nopr"])) 249*cc02d7e2SAndroid Build Coastguard Worker else: 250*cc02d7e2SAndroid Build Coastguard Worker file.write("- None") 251*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 252*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 253*cc02d7e2SAndroid Build Coastguard Worker file.write("PRs with missing release notes label - please fix in Github\n") 254*cc02d7e2SAndroid Build Coastguard Worker file.write("---\n") 255*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 256*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["nolabel"]: 257*cc02d7e2SAndroid Build Coastguard Worker langs_pr["nolabel"].sort() 258*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["nolabel"])) 259*cc02d7e2SAndroid Build Coastguard Worker else: 260*cc02d7e2SAndroid Build Coastguard Worker file.write("- None") 261*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 262*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 263*cc02d7e2SAndroid Build Coastguard Worker file.write("PRs with missing lang label - please fix in Github\n") 264*cc02d7e2SAndroid Build Coastguard Worker file.write("---\n") 265*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 266*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["nolang"]: 267*cc02d7e2SAndroid Build Coastguard Worker langs_pr["nolang"].sort() 268*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["nolang"])) 269*cc02d7e2SAndroid Build Coastguard Worker else: 270*cc02d7e2SAndroid Build Coastguard Worker file.write("- None") 271*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 272*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 273*cc02d7e2SAndroid Build Coastguard Worker file.write( 274*cc02d7e2SAndroid Build Coastguard Worker "PRs going into release notes - please check title and fix in Github." 275*cc02d7e2SAndroid Build Coastguard Worker " Do not edit here.\n" 276*cc02d7e2SAndroid Build Coastguard Worker ) 277*cc02d7e2SAndroid Build Coastguard Worker file.write("---\n") 278*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 279*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["inrel"]: 280*cc02d7e2SAndroid Build Coastguard Worker langs_pr["inrel"].sort() 281*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["inrel"])) 282*cc02d7e2SAndroid Build Coastguard Worker else: 283*cc02d7e2SAndroid Build Coastguard Worker file.write("- None") 284*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 285*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 286*cc02d7e2SAndroid Build Coastguard Worker file.write("PRs not going into release notes\n") 287*cc02d7e2SAndroid Build Coastguard Worker file.write("---\n") 288*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 289*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["notinrel"]: 290*cc02d7e2SAndroid Build Coastguard Worker langs_pr["notinrel"].sort() 291*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["notinrel"])) 292*cc02d7e2SAndroid Build Coastguard Worker else: 293*cc02d7e2SAndroid Build Coastguard Worker file.write("- None") 294*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 295*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 296*cc02d7e2SAndroid Build Coastguard Worker 297*cc02d7e2SAndroid Build Coastguard Worker 298*cc02d7e2SAndroid Build Coastguard Workerdef write_rel_notes(langs_pr, file, version, name): 299*cc02d7e2SAndroid Build Coastguard Worker file.write(rl_header.format(version=version, name=name)) 300*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["core"]: 301*cc02d7e2SAndroid Build Coastguard Worker file.write("Core\n---\n\n") 302*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["core"])) 303*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 304*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 305*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["c++"]: 306*cc02d7e2SAndroid Build Coastguard Worker file.write("C++\n---\n\n") 307*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["c++"])) 308*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 309*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 310*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["c#"]: 311*cc02d7e2SAndroid Build Coastguard Worker file.write("C#\n---\n\n") 312*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["c#"])) 313*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 314*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 315*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["go"]: 316*cc02d7e2SAndroid Build Coastguard Worker file.write("Go\n---\n\n") 317*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["go"])) 318*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 319*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 320*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["Java"]: 321*cc02d7e2SAndroid Build Coastguard Worker file.write("Java\n---\n\n") 322*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["Java"])) 323*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 324*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 325*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["node"]: 326*cc02d7e2SAndroid Build Coastguard Worker file.write("Node\n---\n\n") 327*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["node"])) 328*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 329*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 330*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["objc"]: 331*cc02d7e2SAndroid Build Coastguard Worker file.write("Objective-C\n---\n\n") 332*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["objc"])) 333*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 334*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 335*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["php"]: 336*cc02d7e2SAndroid Build Coastguard Worker file.write("PHP\n---\n\n") 337*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["php"])) 338*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 339*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 340*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["python"]: 341*cc02d7e2SAndroid Build Coastguard Worker file.write("Python\n---\n\n") 342*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["python"])) 343*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 344*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 345*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["ruby"]: 346*cc02d7e2SAndroid Build Coastguard Worker file.write("Ruby\n---\n\n") 347*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["ruby"])) 348*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 349*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 350*cc02d7e2SAndroid Build Coastguard Worker if langs_pr["other"]: 351*cc02d7e2SAndroid Build Coastguard Worker file.write("Other\n---\n\n") 352*cc02d7e2SAndroid Build Coastguard Worker file.write("\n".join(langs_pr["other"])) 353*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 354*cc02d7e2SAndroid Build Coastguard Worker file.write("\n") 355*cc02d7e2SAndroid Build Coastguard Worker 356*cc02d7e2SAndroid Build Coastguard Worker 357*cc02d7e2SAndroid Build Coastguard Workerdef build_args_parser(): 358*cc02d7e2SAndroid Build Coastguard Worker import argparse 359*cc02d7e2SAndroid Build Coastguard Worker 360*cc02d7e2SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 361*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 362*cc02d7e2SAndroid Build Coastguard Worker "release_version", type=str, help="New release version e.g. 1.14.0" 363*cc02d7e2SAndroid Build Coastguard Worker ) 364*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 365*cc02d7e2SAndroid Build Coastguard Worker "release_name", type=str, help="New release name e.g. gladiolus" 366*cc02d7e2SAndroid Build Coastguard Worker ) 367*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 368*cc02d7e2SAndroid Build Coastguard Worker "release_date", type=str, help="Release date e.g. 7/30/18" 369*cc02d7e2SAndroid Build Coastguard Worker ) 370*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 371*cc02d7e2SAndroid Build Coastguard Worker "previous_release_label", 372*cc02d7e2SAndroid Build Coastguard Worker type=str, 373*cc02d7e2SAndroid Build Coastguard Worker help="Previous release branch/tag e.g. v1.13.x", 374*cc02d7e2SAndroid Build Coastguard Worker ) 375*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 376*cc02d7e2SAndroid Build Coastguard Worker "release_branch", 377*cc02d7e2SAndroid Build Coastguard Worker type=str, 378*cc02d7e2SAndroid Build Coastguard Worker help="Current release branch e.g. origin/v1.14.x", 379*cc02d7e2SAndroid Build Coastguard Worker ) 380*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 381*cc02d7e2SAndroid Build Coastguard Worker "draft_filename", type=str, help="Name of the draft file e.g. draft.md" 382*cc02d7e2SAndroid Build Coastguard Worker ) 383*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 384*cc02d7e2SAndroid Build Coastguard Worker "release_notes_filename", 385*cc02d7e2SAndroid Build Coastguard Worker type=str, 386*cc02d7e2SAndroid Build Coastguard Worker help="Name of the release notes file e.g. relnotes.md", 387*cc02d7e2SAndroid Build Coastguard Worker ) 388*cc02d7e2SAndroid Build Coastguard Worker parser.add_argument( 389*cc02d7e2SAndroid Build Coastguard Worker "--token", 390*cc02d7e2SAndroid Build Coastguard Worker type=str, 391*cc02d7e2SAndroid Build Coastguard Worker default="", 392*cc02d7e2SAndroid Build Coastguard Worker help="GitHub API token to avoid being rate limited", 393*cc02d7e2SAndroid Build Coastguard Worker ) 394*cc02d7e2SAndroid Build Coastguard Worker return parser 395*cc02d7e2SAndroid Build Coastguard Worker 396*cc02d7e2SAndroid Build Coastguard Worker 397*cc02d7e2SAndroid Build Coastguard Workerdef main(): 398*cc02d7e2SAndroid Build Coastguard Worker import os 399*cc02d7e2SAndroid Build Coastguard Worker 400*cc02d7e2SAndroid Build Coastguard Worker global TOKEN 401*cc02d7e2SAndroid Build Coastguard Worker 402*cc02d7e2SAndroid Build Coastguard Worker parser = build_args_parser() 403*cc02d7e2SAndroid Build Coastguard Worker args = parser.parse_args() 404*cc02d7e2SAndroid Build Coastguard Worker version, name, date = ( 405*cc02d7e2SAndroid Build Coastguard Worker args.release_version, 406*cc02d7e2SAndroid Build Coastguard Worker args.release_name, 407*cc02d7e2SAndroid Build Coastguard Worker args.release_date, 408*cc02d7e2SAndroid Build Coastguard Worker ) 409*cc02d7e2SAndroid Build Coastguard Worker start, end = args.previous_release_label, args.release_branch 410*cc02d7e2SAndroid Build Coastguard Worker 411*cc02d7e2SAndroid Build Coastguard Worker TOKEN = args.token 412*cc02d7e2SAndroid Build Coastguard Worker if TOKEN == "": 413*cc02d7e2SAndroid Build Coastguard Worker try: 414*cc02d7e2SAndroid Build Coastguard Worker TOKEN = os.environ["GITHUB_TOKEN"] 415*cc02d7e2SAndroid Build Coastguard Worker except: 416*cc02d7e2SAndroid Build Coastguard Worker pass 417*cc02d7e2SAndroid Build Coastguard Worker if TOKEN == "": 418*cc02d7e2SAndroid Build Coastguard Worker print( 419*cc02d7e2SAndroid Build Coastguard Worker "Error: Github API token required. Either include param" 420*cc02d7e2SAndroid Build Coastguard Worker " --token=<your github token> or set environment variable" 421*cc02d7e2SAndroid Build Coastguard Worker " GITHUB_TOKEN to your github token" 422*cc02d7e2SAndroid Build Coastguard Worker ) 423*cc02d7e2SAndroid Build Coastguard Worker return 424*cc02d7e2SAndroid Build Coastguard Worker 425*cc02d7e2SAndroid Build Coastguard Worker langs_pr, error_count = get_pr_titles(get_commit_log(start, end)) 426*cc02d7e2SAndroid Build Coastguard Worker 427*cc02d7e2SAndroid Build Coastguard Worker draft_file, rel_file = args.draft_filename, args.release_notes_filename 428*cc02d7e2SAndroid Build Coastguard Worker filename = os.path.abspath(draft_file) 429*cc02d7e2SAndroid Build Coastguard Worker if os.path.exists(filename): 430*cc02d7e2SAndroid Build Coastguard Worker file = open(filename, "r+") 431*cc02d7e2SAndroid Build Coastguard Worker else: 432*cc02d7e2SAndroid Build Coastguard Worker file = open(filename, "w") 433*cc02d7e2SAndroid Build Coastguard Worker 434*cc02d7e2SAndroid Build Coastguard Worker file.seek(0) 435*cc02d7e2SAndroid Build Coastguard Worker write_draft(langs_pr, file, version, date) 436*cc02d7e2SAndroid Build Coastguard Worker file.truncate() 437*cc02d7e2SAndroid Build Coastguard Worker file.close() 438*cc02d7e2SAndroid Build Coastguard Worker print(("\nDraft notes written to " + filename)) 439*cc02d7e2SAndroid Build Coastguard Worker 440*cc02d7e2SAndroid Build Coastguard Worker filename = os.path.abspath(rel_file) 441*cc02d7e2SAndroid Build Coastguard Worker if os.path.exists(filename): 442*cc02d7e2SAndroid Build Coastguard Worker file = open(filename, "r+") 443*cc02d7e2SAndroid Build Coastguard Worker else: 444*cc02d7e2SAndroid Build Coastguard Worker file = open(filename, "w") 445*cc02d7e2SAndroid Build Coastguard Worker 446*cc02d7e2SAndroid Build Coastguard Worker file.seek(0) 447*cc02d7e2SAndroid Build Coastguard Worker write_rel_notes(langs_pr, file, version, name) 448*cc02d7e2SAndroid Build Coastguard Worker file.truncate() 449*cc02d7e2SAndroid Build Coastguard Worker file.close() 450*cc02d7e2SAndroid Build Coastguard Worker print(("\nRelease notes written to " + filename)) 451*cc02d7e2SAndroid Build Coastguard Worker if error_count > 0: 452*cc02d7e2SAndroid Build Coastguard Worker print("\n\n*** Errors were encountered. See log. *********\n") 453*cc02d7e2SAndroid Build Coastguard Worker 454*cc02d7e2SAndroid Build Coastguard Worker 455*cc02d7e2SAndroid Build Coastguard Workerif __name__ == "__main__": 456*cc02d7e2SAndroid Build Coastguard Worker main() 457