-
Notifications
You must be signed in to change notification settings - Fork 25
/
changelog.py
executable file
·128 lines (103 loc) · 3.91 KB
/
changelog.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python3
# Copyright Contributors to the Packit project.
# SPDX-License-Identifier: MIT
import os
import re
from typing import Iterable, Optional
import click
from git import Commit, Repo
from pathlib import Path
from ogr import GithubService
NOT_IMPORTANT_VALUES = ["n/a", "none", "none.", ""]
RELEASE_NOTES_TAG = "RELEASE NOTES"
RELEASE_NOTES_RE = f"{RELEASE_NOTES_TAG} BEGIN\r?\n(.+)\r?\n{RELEASE_NOTES_TAG} END"
PRE_COMMIT_CI_MESSAGE = "pre-commit autoupdate"
def get_relevant_commits(
repository: Repo, ref: Optional[str] = None
) -> Iterable[Commit]:
if not ref:
tags = sorted(repository.tags, key=lambda t: t.commit.committed_datetime)
if not tags:
raise click.UsageError(
"No REF was specified and the repo contains no tags, "
"the REF must be specified manually."
)
ref = tags[-1]
ref_range = f"{ref}..HEAD"
return repository.iter_commits(rev=ref_range, merges=True)
def get_pr_id_old(message: str) -> str:
# Merge pull request #1483 from majamassarini/fix/1357
first_line = message.split("\n")[0]
fourth_word = first_line.split(" ")[3]
return fourth_word.lstrip("#")
def get_pr_id_new(message: str) -> str:
# Sanitize changelog entry when updating dist-git spec file (#1841)
first_line = message.split("\n")[0]
if match := re.match(r"^.*\(\#(\d+)\)$", first_line):
return match.group(1)
return ""
def get_pr_id(message: str) -> str:
"""
obtain PR ID
"""
pr_id = get_pr_id_new(message)
try:
int(pr_id)
except (ValueError, TypeError):
pr_id = get_pr_id_old(message)
return pr_id
def convert_message(message: str) -> Optional[str]:
"""Extract release note from the commit message,
return None if there is no release note"""
if RELEASE_NOTES_TAG in message:
# new
if found := re.findall(RELEASE_NOTES_RE, message, re.DOTALL):
return found[0].strip()
else:
return None
return None
def get_message_from_pr(repo: str, pr_id: str) -> str:
service = GithubService(token=os.getenv("GITHUB_TOKEN"))
project = service.get_project(namespace="packit", repo=repo)
pr = project.get_pr(pr_id=int(pr_id))
return pr.description
def get_changelog(commits: Iterable[Commit], repo: str, make_link: bool = False) -> str:
changelog = ""
for commit in commits:
if PRE_COMMIT_CI_MESSAGE in commit.message:
continue
message = convert_message(commit.message)
if message and message.lower() not in NOT_IMPORTANT_VALUES:
pr_id = get_pr_id(commit.message)
message = convert_message(get_message_from_pr(repo, pr_id))
if make_link:
url = f"https://github.com/packit/{repo}/pull/{pr_id}"
pr_id = f"[{repo}#{pr_id}]({url})"
else:
pr_id = "#" + pr_id
changelog += f"- {message} ({pr_id})\n"
return changelog
@click.command(
short_help="Get the changelog from the merge commits",
help="""Get the changelog from the merge commits
The script goes through the merge commits since the specified REF
and gets the changelog entry from the commit message.
In case no REF is specified, the latest tag is used.
Currently, the changelog entry in the message is detected based on
explicit marks of the beginning and the end denoted by
`RELEASE NOTES BEGIN` and `RELEASE NOTES END` separators.
""",
)
@click.option(
"--git-repo",
default=".",
type=click.Path(dir_okay=True, file_okay=False),
help="Git repository used for getting the changelog. "
"Current directory is used by default.",
)
@click.argument("ref", type=click.STRING, required=False)
def changelog(git_repo, ref):
repo = Repo(git_repo)
print(get_changelog(get_relevant_commits(repo, ref), Path(repo.working_dir).name))
if __name__ == "__main__":
changelog()