Skip to content

Commit

Permalink
Improve release notes script (#3379)
Browse files Browse the repository at this point in the history
* Improve release notes script

Signed-off-by: albertteoh <[email protected]>

* Add --token option

Signed-off-by: albertteoh <[email protected]>

* Better plural handling

Signed-off-by: albertteoh <[email protected]>

* Read from token file

Signed-off-by: albertteoh <[email protected]>

* Place trunk name in variable

Signed-off-by: albertteoh <[email protected]>
  • Loading branch information
albertteoh authored Nov 9, 2021
1 parent eac737b commit 745587a
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 53 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,7 @@ build-crossdock-fresh: build-crossdock-linux

.PHONY: changelog
changelog:
@echo "Set env variable OAUTH_TOKEN before invoking, https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token. The token only needs default permissions."
docker run --rm -v "${PWD}:/app" pavolloffay/gch:latest --oauth-token ${OAUTH_TOKEN} --owner jaegertracing --repo jaeger
python ./scripts/release-notes.py

.PHONY: install-tools
install-tools:
Expand Down
169 changes: 118 additions & 51 deletions scripts/release-notes.py
Original file line number Diff line number Diff line change
@@ -1,67 +1,134 @@

# This script can read N latest commits from one of Jaeger repos
# and output them in the release notes format:
# {title} ({author} in {pull_request})
# * {title} ({author} in {pull_request})
#
# Requires personal GitHub token with default permissions:
# https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token
# https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token
#
# Usage: python release-notes.py <github_token> <jaeger-repo> <num-commits>
# Usage: python release-notes.py --help
#

import argparse
import json
import os.path
import urllib.parse
from os.path import expanduser
import sys
from urllib.request import (
urlopen,
Request
)
import urllib.parse
import sys


def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)

if len(sys.argv) < 4:
eprint("Usage: python release-notes.py <github_token> <jaeger-repo> <num-commits>")
sys.exit(1)

token=sys.argv[1]
repo=sys.argv[2]
num_commits=sys.argv[3]

accept_header="application/vnd.github.groot-preview+json"
commits_url='https://api.github.com/repos/jaegertracing/{repo}/commits'.format(repo=repo)
pulls_url="{commits_url}/{commit_sha}/pulls"

# load commits
data = urllib.parse.urlencode({'per_page': num_commits})
req=Request(commits_url + '?' + data)
print(req.full_url)
req.add_header('Authorization', 'token {token}'.format(token=token))
commits=json.loads(urlopen(req).read())
print('Retrieved', len(commits), 'commits')

# load PR for each commit and print summary
for commit in commits:
sha = commit['sha']
msg_lines = commit['commit']['message'].split('\n')
msg = msg_lines[0]
req = Request(pulls_url.format(commits_url=commits_url,commit_sha=sha))
req.add_header('accept', accept_header)
req.add_header('Authorization', 'token {token}'.format(token=token))
pulls = json.loads(urlopen(req).read())
if len(pulls) > 1:
print("More than one pull request for commit {commit}".format(commit=sha))
# TODO handle commits without pull requests
pull = pulls[0]
pull_no = pull['number']
pull_url = pull['html_url']
author = pull['user']['login']
author_url = pull['user']['html_url']
msg = msg.replace('(#{pull})'.format(pull=pull_no), '').strip()
print('* {msg} ([@{author}]({author_url}) in [#{pull}]({pull_url}))'.format(
msg=msg,
author=author,
author_url=author_url,
pull=pull_no,
pull_url=pull_url,
))

def num_commits_since_prev_tag(token, base_url):
tags_url = f"{base_url}/tags"
trunk = "master"

req = Request(tags_url)
req.add_header("Authorization", f"token {token}")
tags = json.loads(urlopen(req).read())
prev_release_tag = tags[0]['name']
compare_url = f"{base_url}/compare/{trunk}...{prev_release_tag}"
req = Request(compare_url)
req.add_header("Authorization", f"token {token}")
compare_results = json.loads(urlopen(req).read())
num_commits = compare_results['behind_by']

print(f"There are {num_commits} new commits since {prev_release_tag}")
return num_commits


def main(token, repo, num_commits, exclude_dependabot):
accept_header = "application/vnd.github.groot-preview+json"
base_url = f"https://api.github.com/repos/jaegertracing/{repo}"
commits_url = f"{base_url}/commits"
skipped_dependabot = 0

# If num_commits isn't set, get the number of commits made since the previous release tag.
if not num_commits:
num_commits = num_commits_since_prev_tag(token, base_url)

if not num_commits:
return

# Load commits
data = urllib.parse.urlencode({'per_page': num_commits})
req = Request(commits_url + '?' + data)
print(req.full_url)
req.add_header('Authorization', f'token {token}')
commits = json.loads(urlopen(req).read())
print('Retrieved', len(commits), 'commits')

# Load PR for each commit and print summary
for commit in commits:
sha = commit['sha']
author = commit['author']['login']

if exclude_dependabot and author == "dependabot[bot]":
skipped_dependabot += 1
continue

author_url = commit['author']['html_url']
msg_lines = commit['commit']['message'].split('\n')
msg = msg_lines[0]
req = Request(f"{commits_url}/{sha}/pulls")
req.add_header('accept', accept_header)
req.add_header('Authorization', f'token {token}')
pulls = json.loads(urlopen(req).read())
if len(pulls) > 1:
print(f"WARNING: More than one pull request for commit {sha}")

# Handle commits without pull requests.
if not pulls:
short_sha = sha[:7]
commit_url = commit['html_url']
print(f'* {msg} ([@{author}]({author_url}) in [{short_sha}]({commit_url}))')
continue

pull = pulls[0]
pull_id = pull['number']
pull_url = pull['html_url']
msg = msg.replace(f'(#{pull_id})', '').strip()
print(f'* {msg} ([@{author}]({author_url}) in [#{pull_id}]({pull_url}))')

if skipped_dependabot:
print(f"\n(Skipped {skipped_dependabot} dependabot commit{'' if skipped_dependabot == 1 else 's'})")


if __name__ == "__main__":
parser = argparse.ArgumentParser(description='List changes based on git log for release notes.')

parser.add_argument('--token-file', type=str, default="~/.github_token",
help='The file containing your personal github token to access the github API. ' +
'(default: ~/.github_token)')
parser.add_argument('--repo', type=str, default='jaeger',
help='The repository name to fetch commit logs from. (default: jaeger)')
parser.add_argument('--exclude-dependabot', action='store_true',
help='Excludes dependabot commits. (default: false)')
parser.add_argument('--num-commits', type=int,
help='Print this number of commits from git log. ' +
'(default: number of commits before the previous tag)')

args = parser.parse_args()
generate_token_url = "https://github.com/settings/tokens/new?description=GitHub%20Changelog%20Generator%20token"
generate_err_msg = (f"Please generate a token from this URL: {generate_token_url} and "
f"place it in the token-file. Protect the file so only you can read it: chmod 0600 <file>.")

token_file = expanduser(args.token_file)

if not os.path.exists(token_file):
eprint(f"No such token-file: {token_file}.\n{generate_err_msg}")
sys.exit(1)

with open(token_file, 'r') as file:
token = file.read().replace('\n', '')

if not token:
eprint(f"{token_file} is missing your personal github token.\n{generate_err_msg}")
sys.exit(1)

main(token, args.repo, args.num_commits, args.exclude_dependabot)

0 comments on commit 745587a

Please sign in to comment.