]>
git.proxmox.com Git - ceph.git/blob - ceph/src/script/ceph-release-notes
2 # Originally modified from A. Israel's script seen at
3 # https://gist.github.com/aisrael/b2b78d9dfdd176a232b9
4 """To run this script first install the dependencies
9 pip install githubpy GitPython requests
11 Generate a github access token; this is needed as the anonymous access
12 to Github's API will easily hit the limit even with a single invocation.
14 https://help.github.com/articles/creating-an-access-token-for-command-line-use/
16 Next either set the github token as an env variable
17 `GITHUB_ACCESS_TOKEN` or alternatively invoke the script with
22 ceph-release-notes -r tags/v0.87..origin/giant \
23 $(git rev-parse --show-toplevel)
27 from __future__
import print_function
38 fixes_re
= re
.compile(r
"Fixes\:? #(\d+)")
39 reviewed_by_re
= re
.compile(r
"Rev(.*)By", re
.IGNORECASE
)
40 # labels is the list of relevant labels defined for github.com/ceph/ceph
41 labels
= ['bluestore', 'build/ops', 'cephfs', 'common', 'core', 'mgr',
42 'mon', 'performance', 'pybind', 'rdma', 'rgw', 'rbd', 'tests',
44 merge_re
= re
.compile("Merge pull request #(\d+).*")
45 # prefixes is the list of commit description prefixes we recognize
46 prefixes
= ['bluestore', 'build/ops', 'cephfs', 'cephx', 'cli', 'cmake',
47 'common', 'core', 'crush', 'doc', 'fs', 'librados', 'librbd',
48 'log', 'mds', 'mgr', 'mon', 'msg', 'objecter', 'osd', 'pybind',
49 'rbd', 'rbd-mirror', 'rbd-nbd', 'rgw', 'tests', 'tools']
50 signed_off_re
= re
.compile("Signed-off-by: (.+) <")
51 tracker_re
= re
.compile("http://tracker.ceph.com/issues/(\d+)")
52 rst_link_re
= re
.compile(r
"([a-zA-Z0-9])_(\W)")
53 tracker_uri
= "http://tracker.ceph.com/issues/{0}.json"
56 def get_original_issue(issue
, verbose
):
57 r
= requests
.get(tracker_uri
.format(issue
),
58 params
={"include": "relations"}).json()
60 # looking up for the original issue only makes sense
61 # when dealing with an issue in the Backport tracker
62 if r
["issue"]["tracker"]["name"] != "Backport":
64 print ("http://tracker.ceph.com/issues/" + issue
+
65 " is from the tracker " + r
["issue"]["tracker"]["name"] +
66 ", do not look for the original issue")
69 # if a Backport issue does not have a relation, keep it
70 if "relations" not in r
["issue"]:
72 print ("http://tracker.ceph.com/issues/" + issue
+
73 " has no relations, do not look for the original issue")
77 str(i
['issue_id']) for i
in r
["issue"]["relations"]
78 if i
["relation_type"] == "copied_to"
81 if len(copied_to
) > 1:
83 print ("ERROR: http://tracker.ceph.com/issues/" + issue
+
84 " has more than one Copied To relation")
87 print ("http://tracker.ceph.com/issues/" + issue
+
88 " is the backport of http://tracker.ceph.com/issues/" +
93 print ("http://tracker.ceph.com/issues/" + issue
+
94 " has no copied_to relations; do not look for the" +
99 def split_component(title
, gh
, number
):
100 title_re
= '(' + '|'.join(prefixes
) + ')(:.*)'
101 match
= re
.match(title_re
, title
)
103 return match
.group(1)+match
.group(2)
105 issue
= gh
.repos("ceph")("ceph").issues(number
).get()
106 issue_labels
= {it
['name'] for it
in issue
['labels']}
107 if 'documentation' in issue_labels
:
108 return 'doc: ' + title
109 item
= labels
.intersection(issue_labels
)
111 return ",".join(item
) + ': ' + title
113 return 'UNKNOWN: ' + title
115 def _title_message(commit
, pr
, strict
):
117 message_lines
= commit
.message
.split('\n')
118 if strict
or len(message_lines
) < 1:
121 for line
in message_lines
[1:]:
122 if reviewed_by_re
.match(line
):
129 duplicates_pr_title
= lines
[0] == pr
['title'].strip()
130 if duplicates_pr_title
:
132 assert len(lines
) > 0, "missing message content"
134 # assume that a single line means the intention is to
135 # re-write the PR title
136 return (lines
[0], None)
137 message
= " " + "\n ".join(lines
)
138 return (title
, message
)
140 def make_release_notes(gh
, repo
, ref
, plaintext
, verbose
, strict
, use_tags
):
146 for commit
in repo
.iter_commits(ref
, merges
=True):
147 merge
= merge_re
.match(commit
.summary
)
150 number
= merge
.group(1)
151 print ("Considering PR#" + number
)
152 # do not pick up ceph/ceph-qa-suite.git PRs
153 if int(number
) < 1311:
154 print ("Ignoring low-numbered PR, probably picked up from"
155 " ceph/ceph-qa-suite.git")
157 pr
= gh
.repos("ceph")("ceph").pulls(number
).get()
158 (title
, message
) = _title_message(commit
, pr
, strict
)
161 issues
= fixes_re
.findall(pr
['body']) + tracker_re
.findall(
166 for c
in repo
.iter_commits(
167 "{sha1}^1..{sha1}^2".format(sha1
=commit
.hexsha
)
169 for author
in re
.findall(
170 "Signed-off-by:\s*(.*?)\s*<", c
.message
173 issues
.extend(fixes_re
.findall(c
.message
) +
174 tracker_re
.findall(c
.message
))
176 author
= ", ".join(authors
.keys())
178 author
= commit
.parents
[-1].author
.name
180 if strict
and not issues
:
181 print ("ERROR: https://github.com/ceph/ceph/pull/" +
182 str(number
) + " has no associated issue")
187 '^(?:hammer|infernalis|jewel|kraken):\s+(' +
191 match
= re
.match(title_re
, title
)
193 print ("ERROR: https://github.com/ceph/ceph/pull/" +
194 str(number
) + " title " + title
.encode("utf-8") +
195 " does not match " + title_re
)
197 title
= match
.group(1) + match
.group(2)
199 title
= split_component(title
, gh
, number
)
201 title
= title
.strip(' \t\n\r\f\v\.\,\;\:\-\=')
202 # escape asterisks, which is used by reStructuredTextrst for inline
204 title
= title
.replace('*', '\*')
205 # and escape the underscores for noting a link
206 title
= rst_link_re
.sub(r
'\1\_\2', title
)
207 pr2info
[number
] = (author
, title
, message
)
209 for issue
in set(issues
):
211 issue
= get_original_issue(issue
, verbose
)
212 issue2prs
.setdefault(issue
, set([])).add(number
)
213 pr2issues
.setdefault(number
, set([])).add(issue
)
214 sys
.stdout
.write('.')
216 print (" done collecting merges.")
219 for (issue
, prs
) in issue2prs
.items():
221 print (">>>>>>> " + str(len(prs
)) + " pr for issue " +
222 issue
+ " " + str(prs
))
224 for (pr
, (author
, title
, message
)) in sorted(
225 pr2info
.items(), key
=lambda title
: title
[1][1]
229 issues
= map(lambda issue
: '#' + str(issue
), pr2issues
[pr
])
231 issues
= map(lambda issue
: (
232 '`issue#{issue} <http://tracker.ceph.com/issues/{issue}>`_'
233 ).format(issue
=issue
), pr2issues
[pr
]
235 issues
= ", ".join(issues
) + ", "
239 print ("* {title} ({issues}{author})".format(
240 title
=title
.encode("utf-8"),
242 author
=author
.encode("utf-8")
248 "* {title} ({issues}`pr#{pr} <"
249 "https://github.com/ceph/ceph/pull/{pr}"
252 title
=title
.encode("utf-8"),
254 author
=author
.encode("utf-8"), pr
=pr
261 if __name__
== "__main__":
263 Make ceph release notes for a given revision. Eg usage:
265 $ ceph-release-notes -r tags/v0.87..origin/giant \
266 $(git rev-parse --show-toplevel)
268 It is recommended to set the github env. token in order to avoid
269 hitting the api rate limits.
272 parser
= argparse
.ArgumentParser(
274 formatter_class
=argparse
.RawTextHelpFormatter
277 parser
.add_argument("--rev", "-r",
278 help="git revision range for creating release notes")
279 parser
.add_argument("--text", "-t",
280 action
='store_true', default
=None,
281 help="output plain text only, no links")
282 parser
.add_argument("--verbose", "-v",
283 action
='store_true', default
=None,
285 parser
.add_argument("--strict",
286 action
='store_true', default
=None,
287 help="strict, recommended only for backport releases")
288 parser
.add_argument("repo", metavar
="repo",
289 help="path to ceph git repo")
292 default
=os
.getenv("GITHUB_ACCESS_TOKEN"),
293 help="Github Access Token ($GITHUB_ACCESS_TOKEN otherwise)",
295 parser
.add_argument("--use-tags", default
=False,
296 help="Use github tags to guess the component")
298 args
= parser
.parse_args()
300 access_token
=args
.token
)