Example:
- ceph-release-notes -r tags/v0.87..origin/giant $(git rev-parse --show-toplevel)
+ ceph-release-notes -r tags/v0.87..origin/giant \
+ $(git rev-parse --show-toplevel)
"""
fixes_re = re.compile(r"Fixes\:? #(\d+)")
# labels is the list of relevant labels defined for github.com/ceph/ceph
-labels = [ 'bluestore', 'build/ops', 'cephfs', 'common', 'core', 'mgr',
- 'mon', 'performance', 'pybind', 'rdma', 'rgw', 'rbd', 'tests',
- 'tools' ]
+labels = ['bluestore', 'build/ops', 'cephfs', 'common', 'core', 'mgr',
+ 'mon', 'performance', 'pybind', 'rdma', 'rgw', 'rbd', 'tests',
+ 'tools']
merge_re = re.compile("Merge pull request #(\d+).*")
# prefixes is the list of commit description prefixes we recognize
-prefixes = [ 'bluestore', 'build/ops', 'cephfs', 'cephx', 'cli', 'cmake',
- 'common', 'core', 'crush', 'doc', 'fs', 'librados', 'librbd',
- 'log', 'mds', 'mgr', 'mon', 'msg', 'objecter', 'osd', 'pybind',
- 'rbd', 'rbd-mirror', 'rbd-nbd', 'rgw', 'tests', 'tools' ]
+prefixes = ['bluestore', 'build/ops', 'cephfs', 'cephx', 'cli', 'cmake',
+ 'common', 'core', 'crush', 'doc', 'fs', 'librados', 'librbd',
+ 'log', 'mds', 'mgr', 'mon', 'msg', 'objecter', 'osd', 'pybind',
+ 'rbd', 'rbd-mirror', 'rbd-nbd', 'rgw', 'tests', 'tools']
signed_off_re = re.compile("Signed-off-by: (.+) <")
tracker_re = re.compile("http://tracker.ceph.com/issues/(\d+)")
tracker_uri = "http://tracker.ceph.com/issues/{0}.json"
+
def get_original_issue(issue, verbose):
- r = requests.get(tracker_uri.format(issue),params={"include":"relations"}).json()
+ r = requests.get(tracker_uri.format(issue),
+ params={"include": "relations"}).json()
# looking up for the original issue only makes sense
# when dealing with an issue in the Backport tracker
if r["issue"]["tracker"]["name"] != "Backport":
if verbose:
- print ("http://tracker.ceph.com/issues/" + issue + " is from the tracker " + r["issue"]["tracker"]["name"] + ", do not look for the original issue")
+ print ("http://tracker.ceph.com/issues/" + issue +
+ " is from the tracker " + r["issue"]["tracker"]["name"] +
+ ", do not look for the original issue")
return issue
# if a Backport issue does not have a relation, keep it
if "relations" not in r["issue"]:
if verbose:
- print ("http://tracker.ceph.com/issues/" + issue + " has no relations, do not look for the original issue")
+ print ("http://tracker.ceph.com/issues/" + issue +
+ " has no relations, do not look for the original issue")
return issue
-
- copied_to = [str(i['issue_id']) for i in r["issue"]["relations"] if i["relation_type"] == "copied_to"]
+
+ copied_to = [
+ str(i['issue_id']) for i in r["issue"]["relations"]
+ if i["relation_type"] == "copied_to"
+ ]
if copied_to:
if len(copied_to) > 1:
if verbose:
- print ("ERROR: http://tracker.ceph.com/issues/" + issue + " has more than one Copied To relation")
+ print ("ERROR: http://tracker.ceph.com/issues/" + issue +
+ " has more than one Copied To relation")
return issue
if verbose:
- print ("http://tracker.ceph.com/issues/" + issue + " is the backport of http://tracker.ceph.com/issues/" + copied_to[0])
+ print ("http://tracker.ceph.com/issues/" + issue +
+ " is the backport of http://tracker.ceph.com/issues/" +
+ copied_to[0])
return copied_to[0]
else:
if verbose:
- print ("http://tracker.ceph.com/issues/" + issue + " has no copied_to relations, do not look for the original issue")
+ print ("http://tracker.ceph.com/issues/" + issue +
+ " has no copied_to relations; do not look for the" +
+ " original issue")
return issue
-def split_component(title,gh,number):
+
+def split_component(title, gh, number):
title_re = '(' + '|'.join(prefixes) + ')(:.*)'
match = re.match(title_re, title)
if match:
issue = gh.repos("ceph")("ceph").issues(number).get()
issue_labels = {it['name'] for it in issue['labels']}
if 'documentation' in issue_labels:
- return 'doc: '+ title
+ return 'doc: ' + title
item = labels.intersection(issue_labels)
if item:
- return ",".join(item)+': '+title
+ return ",".join(item) + ': ' + title
else:
- return 'UNKNOWN: '+ title
+ return 'UNKNOWN: ' + title
def make_release_notes(gh, repo, ref, plaintext, verbose, strict, use_tags):
print ("Considering PR#" + number)
# do not pick up ceph/ceph-qa-suite.git PRs
if int(number) < 1311:
- print ("Ignoring low-numbered PR, probably picked up from ceph/ceph-qa-suite.git")
+ print ("Ignoring low-numbered PR, probably picked up from"
+ " ceph/ceph-qa-suite.git")
continue
pr = gh.repos("ceph")("ceph").pulls(number).get()
title = pr['title']
message = None
issues = []
if pr['body']:
- issues = fixes_re.findall(pr['body']) + tracker_re.findall(pr['body'])
+ issues = fixes_re.findall(pr['body']) + tracker_re.findall(
+ pr['body']
+ )
authors = {}
- for c in repo.iter_commits("{sha1}^1..{sha1}^2".format(sha1=commit.hexsha)):
- for author in re.findall("Signed-off-by:\s*(.*?)\s*<", c.message):
+ for c in repo.iter_commits(
+ "{sha1}^1..{sha1}^2".format(sha1=commit.hexsha)
+ ):
+ for author in re.findall(
+ "Signed-off-by:\s*(.*?)\s*<", c.message
+ ):
authors[author] = 1
- issues.extend(fixes_re.findall(c.message) + tracker_re.findall(c.message))
+ issues.extend(fixes_re.findall(c.message) +
+ tracker_re.findall(c.message))
if authors:
author = ", ".join(authors.keys())
else:
author = commit.parents[-1].author.name
if strict and not issues:
- print ("ERROR: https://github.com/ceph/ceph/pull/" + str(number) + " has no associated issue")
- continue
-
+ print ("ERROR: https://github.com/ceph/ceph/pull/" +
+ str(number) + " has no associated issue")
+ continue
+
if strict:
- title_re = '^(?:hammer|infernalis|jewel|kraken):\s+(' + '|'.join(prefixes) + ')(:.*)'
+ title_re = (
+ '^(?:hammer|infernalis|jewel|kraken):\s+(' +
+ '|'.join(prefixes) +
+ ')(:.*)'
+ )
match = re.match(title_re, title)
if not match:
- print ("ERROR: https://github.com/ceph/ceph/pull/" + str(number) + " title " + title.encode("utf-8") + " does not match " + title_re)
+ print ("ERROR: https://github.com/ceph/ceph/pull/" +
+ str(number) + " title " + title.encode("utf-8") +
+ " does not match " + title_re)
else:
title = match.group(1) + match.group(2)
if use_tags:
- title = split_component(title,gh,number)
+ title = split_component(title, gh, number)
pr2info[number] = (author, title, message)
if strict:
for (issue, prs) in issue2prs.iteritems():
if len(prs) > 1:
- print (">>>>>>> " + str(len(prs)) + " pr for issue " + issue + " " + str(prs))
+ print (">>>>>>> " + str(len(prs)) + " pr for issue " +
+ issue + " " + str(prs))
- for (pr, (author, title, message)) in sorted(pr2info.iteritems(), key=lambda (k,v): (v[2], v[1])):
+ for (pr, (author, title, message)) in sorted(
+ pr2info.iteritems(), key=lambda (k, v): (v[2], v[1])
+ ):
if pr in pr2issues:
if plaintext:
issues = map(lambda issue: '#' + str(issue), pr2issues[pr])
else:
- issues = map(lambda issue: '`issue#{issue} <http://tracker.ceph.com/issues/{issue}>`_'.format(issue=issue), pr2issues[pr])
+ issues = map(lambda issue: (
+ '`issue#{issue} <http://tracker.ceph.com/issues/{issue}>`_'
+ ).format(issue=issue), pr2issues[pr]
+ )
issues = ", ".join(issues) + ", "
else:
issues = ''
if plaintext:
- print ("* {title} ({issues}{author})".format(title=title.encode("utf-8"), issues=issues, author=author.encode("utf-8")))
+ print ("* {title} ({issues}{author})".format(
+ title=title.encode("utf-8"),
+ issues=issues,
+ author=author.encode("utf-8")
+ )
+ )
else:
- print ("* {title} ({issues}`pr#{pr} <https://github.com/ceph/ceph/pull/{pr}>`_, {author})".format(title=title.encode("utf-8"), issues=issues, author=author.encode("utf-8"), pr=pr))
+ print (
+ (
+ "* {title} ({issues}`pr#{pr} <"
+ "https://github.com/ceph/ceph/pull/{pr}"
+ ">`_, {author})"
+ ).format(
+ title=title.encode("utf-8"),
+ issues=issues,
+ author=author.encode("utf-8"), pr=pr
+ )
+ )
if message:
print (message)
desc = '''
Make ceph release notes for a given revision. Eg usage:
- $ ceph-release-notes -r tags/v0.87..origin/giant $(git rev-parse --show-toplevel)
+ $ ceph-release-notes -r tags/v0.87..origin/giant \
+ $(git rev-parse --show-toplevel)
It is recommended to set the github env. token in order to avoid
hitting the api rate limits.
'''
- parser = argparse.ArgumentParser(description=desc,
- formatter_class=argparse.RawTextHelpFormatter)
+ parser = argparse.ArgumentParser(
+ description=desc,
+ formatter_class=argparse.RawTextHelpFormatter
+ )
parser.add_argument("--rev", "-r",
help="git revision range for creating release notes")
help="strict, recommended only for backport releases")
parser.add_argument("repo", metavar="repo",
help="path to ceph git repo")
- parser.add_argument("--token", default=os.getenv("GITHUB_ACCESS_TOKEN"),
- help="Github Access Token ($GITHUB_ACCESS_TOKEN otherwise)")
+ parser.add_argument(
+ "--token",
+ default=os.getenv("GITHUB_ACCESS_TOKEN"),
+ help="Github Access Token ($GITHUB_ACCESS_TOKEN otherwise)",
+ )
parser.add_argument("--use-tags", default=False,
help="Use github tags to guess the component")
gh = github.GitHub(
access_token=args.token)
- make_release_notes(gh, Repo(args.repo), args.rev, args.text, args.verbose, args.strict, args.use_tags)
+ make_release_notes(
+ gh,
+ Repo(args.repo),
+ args.rev,
+ args.text,
+ args.verbose,
+ args.strict,
+ args.use_tags
+ )