add flag to collapse pre-releases into final releases
Add a new flag to combine pre-release notes into their final release version after that version is tagged. The flag defaults to False to preserve the current behavior. Change-Id: I6b9c058289f0baa3e39048b3fa78f6af81bdd83b Signed-off-by: Doug Hellmann <doug@doughellmann.com>
This commit is contained in:
parent
207776787f
commit
53891ddb1a
@ -46,6 +46,12 @@ Enable the extension by adding ``'reno.sphinxext'`` to the
|
||||
A comma separated list of versions to include in the notes. The
|
||||
default is to include all versions found on ``branch``.
|
||||
|
||||
*collapse-pre-releases*
|
||||
|
||||
A flag indicating that notes attached to pre-release versions
|
||||
should be incorporated into the notes for the final release,
|
||||
after the final release is tagged.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add a flag to collapse pre-release notes into their final release,
|
||||
if the final release tag is present.
|
@ -25,7 +25,11 @@ def list_cmd(args):
|
||||
LOG.debug('starting list')
|
||||
reporoot = args.reporoot.rstrip('/') + '/'
|
||||
notesdir = utils.get_notes_dir(args)
|
||||
notes = scanner.get_notes_by_version(reporoot, notesdir, args.branch)
|
||||
collapse = args.collapse_pre_releases
|
||||
notes = scanner.get_notes_by_version(
|
||||
reporoot, notesdir, args.branch,
|
||||
collapse_pre_releases=collapse,
|
||||
)
|
||||
if args.version:
|
||||
versions = args.version
|
||||
else:
|
||||
|
12
reno/main.py
12
reno/main.py
@ -76,6 +76,12 @@ def main(argv=sys.argv[1:]):
|
||||
default=None,
|
||||
help='the branch to scan, defaults to the current',
|
||||
)
|
||||
do_list.add_argument(
|
||||
'--collapse-pre-releases',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='combine pre-releases with their final release',
|
||||
)
|
||||
do_list.set_defaults(func=lister.list_cmd)
|
||||
|
||||
do_report = subparsers.add_parser(
|
||||
@ -102,6 +108,12 @@ def main(argv=sys.argv[1:]):
|
||||
action='append',
|
||||
help='the version(s) to include, defaults to all',
|
||||
)
|
||||
do_report.add_argument(
|
||||
'--collapse-pre-releases',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='combine pre-releases with their final release',
|
||||
)
|
||||
do_report.set_defaults(func=report.report_cmd)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
@ -21,7 +21,11 @@ def report_cmd(args):
|
||||
"Generates a release notes report"
|
||||
reporoot = args.reporoot.rstrip('/') + '/'
|
||||
notesdir = utils.get_notes_dir(args)
|
||||
notes = scanner.get_notes_by_version(reporoot, notesdir, args.branch)
|
||||
collapse = args.collapse_pre_releases
|
||||
notes = scanner.get_notes_by_version(
|
||||
reporoot, notesdir, args.branch,
|
||||
collapse_pre_releases=collapse,
|
||||
)
|
||||
if args.version:
|
||||
versions = args.version
|
||||
else:
|
||||
|
@ -115,6 +115,9 @@ TAG_RE = re.compile('''
|
||||
((?:[\d.ab]|rc)+) # digits, a, b, and rc cover regular and pre-releases
|
||||
[,)] # possible trailing comma or closing paren
|
||||
''', flags=re.VERBOSE | re.UNICODE)
|
||||
PRE_RELEASE_RE = re.compile('''
|
||||
\.(\d+(?:[ab]|rc)+\d*)$
|
||||
''', flags=re.VERBOSE | re.UNICODE)
|
||||
|
||||
|
||||
def _get_version_tags_on_branch(reporoot, branch):
|
||||
@ -146,13 +149,24 @@ def _get_version_tags_on_branch(reporoot, branch):
|
||||
return tags
|
||||
|
||||
|
||||
def get_notes_by_version(reporoot, notesdir, branch=None):
|
||||
def get_notes_by_version(reporoot, notesdir, branch=None,
|
||||
collapse_pre_releases=False):
|
||||
"""Return an OrderedDict mapping versions to lists of notes files.
|
||||
|
||||
The versions are presented in reverse chronological order.
|
||||
|
||||
Notes files are associated with the earliest version for which
|
||||
they were available, regardless of whether they changed later.
|
||||
|
||||
:param reporoot: Path to the root of the git repository.
|
||||
:type reporoot: str
|
||||
:param notesdir: The directory under *reporoot* with the release notes.
|
||||
:type notesdir: str
|
||||
:param branch: The name of the branch to scan. Defaults to current.
|
||||
:type branch: str
|
||||
:param collapse_pre_releases: When true, merge pre-release versions
|
||||
into the final release, if it is present.
|
||||
:type collapse_pre_releases: bool
|
||||
"""
|
||||
|
||||
LOG.debug('scanning %s/%s (branch=%s)' % (reporoot, notesdir, branch))
|
||||
@ -277,6 +291,36 @@ def get_notes_by_version(reporoot, notesdir, branch=None):
|
||||
LOG.debug(msg)
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
# Combine pre-releases into the final release, if we are told to
|
||||
# and the final release exists.
|
||||
if collapse_pre_releases:
|
||||
collapsing = files_and_tags
|
||||
files_and_tags = collections.OrderedDict()
|
||||
for ov in versions_by_date:
|
||||
if ov not in collapsing:
|
||||
# We don't need to collapse this one because there are
|
||||
# no notes attached to it.
|
||||
continue
|
||||
pre_release_match = PRE_RELEASE_RE.search(ov)
|
||||
LOG.debug('checking %r', ov)
|
||||
if pre_release_match:
|
||||
# Remove the trailing pre-release part of the version
|
||||
# from the string.
|
||||
pre_rel_str = pre_release_match.groups()[0]
|
||||
canonical_ver = ov[:-len(pre_rel_str)].rstrip('.')
|
||||
if canonical_ver not in versions_by_date:
|
||||
# This canonical version was never tagged, so we
|
||||
# do not want to collapse the pre-releases. Reset
|
||||
# to the original version.
|
||||
canonical_ver = ov
|
||||
else:
|
||||
LOG.debug('combining into %r', canonical_ver)
|
||||
else:
|
||||
canonical_ver = ov
|
||||
if canonical_ver not in files_and_tags:
|
||||
files_and_tags[canonical_ver] = []
|
||||
files_and_tags[canonical_ver].extend(collapsing[ov])
|
||||
|
||||
# Only return the parts of files_and_tags that actually have
|
||||
# filenames associated with the versions.
|
||||
trimmed = collections.OrderedDict()
|
||||
|
@ -33,6 +33,7 @@ class ReleaseNotesDirective(rst.Directive):
|
||||
'relnotessubdir': directives.unchanged,
|
||||
'notesdir': directives.unchanged,
|
||||
'version': directives.unchanged,
|
||||
'collapse-pre-releases': directives.flag,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
@ -50,12 +51,16 @@ class ReleaseNotesDirective(rst.Directive):
|
||||
defaults.RELEASE_NOTES_SUBDIR)
|
||||
notessubdir = self.options.get('notesdir', defaults.NOTES_SUBDIR)
|
||||
version_opt = self.options.get('version')
|
||||
collapse = self.options.get('collapse-pre-releases')
|
||||
|
||||
notesdir = os.path.join(relnotessubdir, notessubdir)
|
||||
info('scanning %s for %s release notes' %
|
||||
(os.path.join(reporoot, notesdir), branch or 'current branch'))
|
||||
|
||||
notes = scanner.get_notes_by_version(reporoot, notesdir, branch)
|
||||
notes = scanner.get_notes_by_version(
|
||||
reporoot, notesdir, branch,
|
||||
collapse_pre_releases=collapse,
|
||||
)
|
||||
if version_opt is not None:
|
||||
versions = [
|
||||
v.strip()
|
||||
|
@ -519,6 +519,83 @@ class PreReleaseTest(Base):
|
||||
results,
|
||||
)
|
||||
|
||||
def test_collapse(self):
|
||||
files = []
|
||||
self._make_python_package()
|
||||
files.append(self._add_notes_file('slug1'))
|
||||
self._run_git('tag', '-s', '-m', 'alpha tag', '1.0.0.0a1')
|
||||
files.append(self._add_notes_file('slug2'))
|
||||
self._run_git('tag', '-s', '-m', 'beta tag', '1.0.0.0b1')
|
||||
files.append(self._add_notes_file('slug3'))
|
||||
self._run_git('tag', '-s', '-m', 'release candidate tag', '1.0.0.0rc1')
|
||||
files.append(self._add_notes_file('slug4'))
|
||||
self._run_git('tag', '-s', '-m', 'full release tag', '1.0.0')
|
||||
raw_results = scanner.get_notes_by_version(
|
||||
self.reporoot,
|
||||
'releasenotes/notes',
|
||||
collapse_pre_releases=True,
|
||||
)
|
||||
results = {
|
||||
k: [f for (f, n) in v]
|
||||
for (k, v) in raw_results.items()
|
||||
}
|
||||
self.assertEqual(
|
||||
{'1.0.0': files,
|
||||
},
|
||||
results,
|
||||
)
|
||||
|
||||
def test_collapse_without_full_release(self):
|
||||
self._make_python_package()
|
||||
f1 = self._add_notes_file('slug1')
|
||||
self._run_git('tag', '-s', '-m', 'alpha tag', '1.0.0.0a1')
|
||||
f2 = self._add_notes_file('slug2')
|
||||
self._run_git('tag', '-s', '-m', 'beta tag', '1.0.0.0b1')
|
||||
f3 = self._add_notes_file('slug3')
|
||||
self._run_git('tag', '-s', '-m', 'release candidate tag', '1.0.0.0rc1')
|
||||
raw_results = scanner.get_notes_by_version(
|
||||
self.reporoot,
|
||||
'releasenotes/notes',
|
||||
collapse_pre_releases=True,
|
||||
)
|
||||
results = {
|
||||
k: [f for (f, n) in v]
|
||||
for (k, v) in raw_results.items()
|
||||
}
|
||||
self.assertEqual(
|
||||
{'1.0.0.0a1': [f1],
|
||||
'1.0.0.0b1': [f2],
|
||||
'1.0.0.0rc1': [f3],
|
||||
},
|
||||
results,
|
||||
)
|
||||
|
||||
def test_collapse_without_notes(self):
|
||||
self._make_python_package()
|
||||
self._run_git('tag', '-s', '-m', 'earlier tag', '0.1.0')
|
||||
f1 = self._add_notes_file('slug1')
|
||||
self._run_git('tag', '-s', '-m', 'alpha tag', '1.0.0.0a1')
|
||||
f2 = self._add_notes_file('slug2')
|
||||
self._run_git('tag', '-s', '-m', 'beta tag', '1.0.0.0b1')
|
||||
f3 = self._add_notes_file('slug3')
|
||||
self._run_git('tag', '-s', '-m', 'release candidate tag', '1.0.0.0rc1')
|
||||
raw_results = scanner.get_notes_by_version(
|
||||
self.reporoot,
|
||||
'releasenotes/notes',
|
||||
collapse_pre_releases=True,
|
||||
)
|
||||
results = {
|
||||
k: [f for (f, n) in v]
|
||||
for (k, v) in raw_results.items()
|
||||
}
|
||||
self.assertEqual(
|
||||
{'1.0.0.0a1': [f1],
|
||||
'1.0.0.0b1': [f2],
|
||||
'1.0.0.0rc1': [f3],
|
||||
},
|
||||
results,
|
||||
)
|
||||
|
||||
|
||||
class MergeCommitTest(Base):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user