121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
"""
|
|
Changelog parser
|
|
================
|
|
|
|
This generates a changelog from a json file of the PRs of a given milestone,
|
|
dumped to json, using the [GitHub CLI](https://github.com/cli/cli).
|
|
|
|
First, in the command line, create the following alias::
|
|
|
|
gh alias set --shell viewMilestone "gh api graphql -F owner='kivy' \
|
|
-F name='kivy' -F number=\\$1 -f query='
|
|
query GetMilestones(\\$name: String!, \\$owner: String!, \\$number: \
|
|
Int!) {
|
|
repository(owner: \\$owner, name: \\$name) {
|
|
milestone(number: \\$number) {
|
|
pullRequests(states: MERGED, first: 1000) {
|
|
nodes {
|
|
number
|
|
title
|
|
labels (first: 25) {
|
|
nodes {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
'"
|
|
|
|
Then, log in using ``gh`` and run::
|
|
|
|
gh viewMilestone 26 > prs.json
|
|
|
|
This will generate ``prs.json``. Then, to generate the changelog, run::
|
|
|
|
python -m kivy.tools.changelog_parser prs.json changelog.md
|
|
|
|
to generate a markdown changelog at ``changelog.md``. Then, edit as desired
|
|
and paste into the
|
|
[changelog here](https://github.com/kivy/kivy/blob/master/doc/sources\
|
|
/changelog.rst).
|
|
"""
|
|
from os.path import exists
|
|
import sys
|
|
from collections import defaultdict
|
|
import json
|
|
|
|
__all__ = ('process_changelog', )
|
|
|
|
|
|
def write_special_section(fh, items, header):
|
|
items = sorted(items, key=lambda x: x[0])
|
|
if items:
|
|
fh.write(f'{header}\n{"-" * len(header)}\n\n')
|
|
for n, title in items:
|
|
fh.write(f'- [:repo:`{n}`]: {title}\n')
|
|
fh.write('\n')
|
|
|
|
|
|
def process_changelog(filename_in, filename_out):
|
|
if exists(filename_out):
|
|
raise ValueError(
|
|
'{} already exists and would be overwritten'.format(filename_out))
|
|
|
|
with open(filename_in, 'r') as fh:
|
|
data = json.load(fh)
|
|
prs = data["data"]["repository"]["milestone"]["pullRequests"]["nodes"]
|
|
|
|
bad_pr = False
|
|
grouped = defaultdict(list)
|
|
highlighted = []
|
|
api_breaks = []
|
|
deprecates = []
|
|
for item in prs:
|
|
n = item['number']
|
|
title = item['title']
|
|
labels = [label['name'] for label in item['labels']['nodes']]
|
|
api_break = 'Notes: API-break' in labels
|
|
highlight = 'Notes: Release-highlight' in labels
|
|
deprecated = 'Notes: API-deprecation' in labels
|
|
component_str = 'Component: '
|
|
components = [
|
|
label[len(component_str):]
|
|
for label in labels if label.startswith(component_str)
|
|
]
|
|
|
|
if not components:
|
|
print(f'Found no component label for #{n}')
|
|
bad_pr = True
|
|
continue
|
|
if len(components) > 1:
|
|
print(f'Found more than one component label for #{n}')
|
|
bad_pr = True
|
|
continue
|
|
|
|
grouped[components[0]].append((n, title))
|
|
if highlight:
|
|
highlighted.append((n, title))
|
|
if api_break:
|
|
api_breaks.append((n, title))
|
|
if deprecated:
|
|
deprecates.append((n, title))
|
|
|
|
if bad_pr:
|
|
raise ValueError(
|
|
'One or more PRs have no, or more than one component label')
|
|
|
|
with open(filename_out, 'w') as fh:
|
|
write_special_section(fh, highlighted, 'Highlights')
|
|
write_special_section(fh, deprecates, 'Deprecated')
|
|
write_special_section(fh, api_breaks, 'Breaking changes')
|
|
|
|
for group, items in sorted(grouped.items(), key=lambda x: x[0]):
|
|
write_special_section(fh, items, group.capitalize())
|
|
|
|
|
|
if __name__ == '__main__':
|
|
process_changelog(*sys.argv[1:])
|