Move release_notes.py into releasetools package
Start a new releasetools package and move the release notes generation script into it, turning it into a more reusable library at the same time. Change-Id: I9f1f38cba34fc63d3de4e3bb7eae97e26a72db35
This commit is contained in:
parent
5670efbbeb
commit
649f7adfc4
57
README.rst
57
README.rst
@ -12,6 +12,35 @@ similar_tarballs.sh also requires that you have tardiff installed. If it's not
|
||||
packaged for your distribution, you can find it at
|
||||
http://tardiff.coolprojects.org/.
|
||||
|
||||
Tox Targets
|
||||
===========
|
||||
|
||||
Because some of the scripts have special dependencies, they can be run
|
||||
through tox as a convenience for managing those requirements.
|
||||
|
||||
release_postversion
|
||||
-------------------
|
||||
|
||||
This script is used to publish alphas and final releases for Oslo
|
||||
libraries, other libraries, and projects that use post-versioning
|
||||
(Ironic) instead of pre-versioning (Nova, etc.). FixCommitted bugs
|
||||
are turned to FixReleased, the next-$SERIES milestone is renamed or a
|
||||
version-based milestone is created, and the milestone is marked
|
||||
released.
|
||||
|
||||
Examples:
|
||||
|
||||
tox -e release_postversion -- juno 1.3.0.0a3 HEAD oslo.rootwrap
|
||||
|
||||
Push a 1.3.0.0a3 tag to oslo.rootwrap current HEAD. Mark all FixCommitted
|
||||
bugs in oslo.rootwrap to FixReleased in next-juno.
|
||||
|
||||
tox -e release_postversion -- juno 1.3.0 HEAD oslo.rootwrap
|
||||
|
||||
Rename the next-juno milestone to '1.3.0'. Push a 1.3.0 tag to oslo.rootwrap
|
||||
current HEAD. Mark all FixCommitted bugs in oslo.rootwrap (if any) to
|
||||
FixReleased in 1.3.0, and mark 1.3.0 released.
|
||||
|
||||
|
||||
Top-level scripts
|
||||
=================
|
||||
@ -122,25 +151,7 @@ Examples:
|
||||
release_postversion.sh
|
||||
----------------------
|
||||
|
||||
This script is used to publish alphas and final releases for Oslo
|
||||
libraries, other libraries, and projects that use post-versioning
|
||||
(Ironic) instead of pre-versioning (Nova, etc.). FixCommitted bugs
|
||||
are turned to FixReleased, the next-$SERIES milestone is renamed or a
|
||||
version-based milestone is created, and the milestone is marked
|
||||
released.
|
||||
|
||||
Examples:
|
||||
|
||||
./release_postversion.sh juno 1.3.0.0a3 HEAD oslo.rootwrap"
|
||||
|
||||
Push a 1.3.0.0a3 tag to oslo.rootwrap current HEAD. Mark all FixCommitted
|
||||
bugs in oslo.rootwrap to FixReleased in next-juno.
|
||||
|
||||
./release_postversion.sh juno 1.3.0 HEAD oslo.rootwrap"
|
||||
|
||||
Rename the next-juno milestone to '1.3.0'. Push a 1.3.0 tag to oslo.rootwrap
|
||||
current HEAD. Mark all FixCommitted bugs in oslo.rootwrap (if any) to
|
||||
FixReleased in 1.3.0, and mark 1.3.0 released.
|
||||
See the 'release_postversion' tox environment, above.
|
||||
|
||||
release_many.sh
|
||||
---------------
|
||||
@ -155,8 +166,8 @@ Optionally, the line can also include a series name, for example::
|
||||
1.13.0 85c069e oslo.messaging
|
||||
1.8.3 0f24108 oslo.messaging kilo
|
||||
|
||||
release_notes.py
|
||||
----------------
|
||||
release-notes
|
||||
-------------
|
||||
|
||||
This produces a set of release notes intended to be sent as an
|
||||
announcement email when a new library or package is produced. It is
|
||||
@ -176,12 +187,12 @@ the one-line description to include in the output text.
|
||||
|
||||
Examples:
|
||||
|
||||
./release_notes.py ~/repos/openstack/oslo.config 1.7.0 1.8.0
|
||||
release-notes ~/repos/openstack/oslo.config 1.7.0 1.8.0
|
||||
|
||||
Print the release notes between versions 1.7.0 and 1.8.0 for the
|
||||
project in the ``~/repos/openstack/oslo.config`` directory.
|
||||
|
||||
./release_notes.py --show-dates --changes-only ~/repos/openstack/oslo.config 1.8.0 HEAD
|
||||
release-notes --show-dates --changes-only ~/repos/openstack/oslo.config 1.8.0 HEAD
|
||||
|
||||
Print the list of changes after 1.8.0 for the project in the
|
||||
``~/repos/openstack/oslo.config`` directory, including the date of
|
||||
|
@ -27,6 +27,11 @@ repos="$@"
|
||||
TOOLSDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
source $TOOLSDIR/functions
|
||||
|
||||
if [[ -z "$VIRTUAL_ENV" ]]; then
|
||||
tox -e venv --notest
|
||||
source ./.tox/venv/bin/activate
|
||||
fi
|
||||
|
||||
# Make sure no pager is configured so the output is not blocked
|
||||
export PAGER=
|
||||
|
||||
@ -44,7 +49,7 @@ function list_changes {
|
||||
else
|
||||
echo
|
||||
local end_sha=$(git log -n 1 --pretty=tformat:%h)
|
||||
$TOOLSDIR/release_notes.py \
|
||||
release-notes \
|
||||
--show-dates \
|
||||
--changes-only \
|
||||
. $prev_tag $end_sha
|
||||
|
@ -127,7 +127,7 @@ else
|
||||
if [[ "$EMAIL_TAGS" != "" ]]; then
|
||||
email_tags="--email-tags $EMAIL_TAGS"
|
||||
fi
|
||||
${TOOLSDIR}/release_notes.py \
|
||||
release-notes \
|
||||
--email \
|
||||
$email_tags \
|
||||
--series $SERIES \
|
||||
|
0
releasetools/__init__.py
Normal file
0
releasetools/__init__.py
Normal file
131
release_notes.py → releasetools/release_notes.py
Executable file → Normal file
131
release_notes.py → releasetools/release_notes.py
Executable file → Normal file
@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
@ -180,8 +178,8 @@ def run_cmd(cmd, cwd=None):
|
||||
return stdout, stderr
|
||||
|
||||
|
||||
def is_skippable_commit(args, line):
|
||||
return (args.skip_requirement_merges and
|
||||
def is_skippable_commit(skip_requirement_merges, line):
|
||||
return (skip_requirement_merges and
|
||||
line.lower().endswith('updated from global requirements'))
|
||||
|
||||
|
||||
@ -263,10 +261,74 @@ def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
library_path = os.path.abspath(args.library)
|
||||
|
||||
notable_changes = ''
|
||||
if args.notable_changes:
|
||||
with open(args.notable_changes, 'r') as fh:
|
||||
notable_changes = fh.read().rstrip()
|
||||
|
||||
notes = generate_release_notes(
|
||||
library=args.library,
|
||||
library_path=library_path,
|
||||
start_revision=args.start_revision,
|
||||
end_revision=args.end_revision,
|
||||
show_dates=args.show_dates,
|
||||
skip_requirement_merges=args.skip_requirement_merges,
|
||||
notable_changes=notable_changes,
|
||||
is_stable=args.stable,
|
||||
series=args.series,
|
||||
email=args.email,
|
||||
email_from=args.email_from,
|
||||
email_to=args.email_to,
|
||||
email_reply_to=args.email_reply_to,
|
||||
email_tags=args.email_tags,
|
||||
include_pypi_link=args.include_pypi_link,
|
||||
changes_only=args.changes_only,
|
||||
)
|
||||
print(notes)
|
||||
return 0
|
||||
|
||||
|
||||
def generate_release_notes(library, library_path,
|
||||
start_revision, end_revision,
|
||||
show_dates, skip_requirement_merges,
|
||||
notable_changes,
|
||||
is_stable, series,
|
||||
email, email_from,
|
||||
email_to, email_reply_to, email_tags,
|
||||
include_pypi_link,
|
||||
changes_only,
|
||||
):
|
||||
"""Return the text of the release notes.
|
||||
|
||||
:param library: The name of the library.
|
||||
:param library_path: Path to the library repository on disk.
|
||||
:param start_revision: First reference for finding change log.
|
||||
:param end_revision: Final reference for finding change log.
|
||||
:param show_dates: Boolean indicating whether or not to show dates
|
||||
in the output.
|
||||
:param skip_requirement_merges: Boolean indicating whether to
|
||||
skip merge commits for requirements changes.
|
||||
:param notable_changes: Highlights from the release.
|
||||
:param is_stable: Boolean indicating whether this is a stable
|
||||
series or not.
|
||||
:param series: String holding the name of the series.
|
||||
:param email: Boolean indicating whether the output format should
|
||||
be an email message.
|
||||
:param email_from: String containing the sender email address.
|
||||
:param email_to: String containing the email recipient.
|
||||
:param email_reply_to: String containing the email reply-to address.
|
||||
:param email_tags: String containing the email header topic tags to add.
|
||||
:param include_pypi_link: Boolean indicating whether or not to
|
||||
include an automatically generated link to the PyPI package
|
||||
page.
|
||||
:param changes_only: Boolean indicating whether to limit output to
|
||||
the list of changes, without any extra data.
|
||||
|
||||
"""
|
||||
|
||||
if not os.path.isfile(os.path.join(library_path, "setup.py")):
|
||||
sys.stderr.write("No 'setup.py' file found in %s\n" % library_path)
|
||||
sys.stderr.write("This will not end well...\n")
|
||||
return 1
|
||||
raise RuntimeError("No 'setup.py' file found in %s\n" % library_path)
|
||||
|
||||
# Get the python library/program description...
|
||||
cmd = [sys.executable, 'setup.py', '--description']
|
||||
@ -279,8 +341,8 @@ def main():
|
||||
library_name = stdout.strip()
|
||||
|
||||
# Get the commits that are in the desired range...
|
||||
git_range = "%s..%s" % (args.start_revision, args.end_revision)
|
||||
if args.show_dates:
|
||||
git_range = "%s..%s" % (start_revision, end_revision)
|
||||
if show_dates:
|
||||
format = "--format=%h %ci %s"
|
||||
else:
|
||||
format = "--oneline"
|
||||
@ -289,7 +351,8 @@ def main():
|
||||
changes = []
|
||||
for commit_line in stdout.splitlines():
|
||||
commit_line = commit_line.strip()
|
||||
if not commit_line or is_skippable_commit(args, commit_line):
|
||||
if not commit_line or is_skippable_commit(skip_requirement_merges,
|
||||
commit_line):
|
||||
continue
|
||||
else:
|
||||
changes.append(commit_line)
|
||||
@ -315,60 +378,60 @@ def main():
|
||||
continue
|
||||
diff_stats.append(line)
|
||||
|
||||
notables = ''
|
||||
if args.notable_changes:
|
||||
with open(args.notable_changes, 'r') as fh:
|
||||
notables = fh.read().rstrip()
|
||||
|
||||
# Extract + valdiate needed sections from readme...
|
||||
readme_sections = parse_readme(library_path)
|
||||
bug_url = readme_sections['bug_url']
|
||||
if bug_url:
|
||||
lp_url = bug_url.replace("bugs.", "").rstrip("/")
|
||||
milestone_url = lp_url + "/+milestone/%s" % args.end_revision
|
||||
milestone_url = lp_url + "/+milestone/%s" % end_revision
|
||||
else:
|
||||
lp_url = ''
|
||||
milestone_url = ''
|
||||
change_header = ["Changes in %s %s" % (library_name, git_range)]
|
||||
change_header.append("-" * len(change_header[0]))
|
||||
|
||||
if not email_from:
|
||||
raise RuntimeError('No email-from specified')
|
||||
|
||||
params = dict(readme_sections)
|
||||
params.update({
|
||||
'project': os.path.basename(library_path),
|
||||
'description': description,
|
||||
'end_rev': args.end_revision,
|
||||
'end_rev': end_revision,
|
||||
'range': git_range,
|
||||
'lib': library_path,
|
||||
'milestone_url': milestone_url,
|
||||
'skip_requirement_merges': args.skip_requirement_merges,
|
||||
'skip_requirement_merges': skip_requirement_merges,
|
||||
'changes': changes,
|
||||
'requirement_changes': requirement_changes,
|
||||
'diff_stats': diff_stats,
|
||||
'notables': notables,
|
||||
'notables': notable_changes,
|
||||
'change_header': "\n".join(change_header),
|
||||
'emotion': random.choice(EMOTIONS),
|
||||
'stable_series': args.stable,
|
||||
'series': args.series,
|
||||
'email': args.email,
|
||||
'email_from': args.email_from,
|
||||
'email_to': args.email_to,
|
||||
'email_reply_to': args.email_reply_to,
|
||||
'email_tags': args.email_tags,
|
||||
'stable_series': is_stable,
|
||||
'series': series,
|
||||
'email': email,
|
||||
'email_from': email_from,
|
||||
'email_to': email_to,
|
||||
'email_reply_to': email_reply_to,
|
||||
'email_tags': email_tags,
|
||||
})
|
||||
if args.include_pypi_link:
|
||||
if include_pypi_link:
|
||||
params['pypi_url'] = PYPI_URL_TPL % library_name
|
||||
else:
|
||||
params['pypi_url'] = None
|
||||
if args.changes_only:
|
||||
print(expand_template(CHANGES_ONLY_TPL, params))
|
||||
|
||||
response = []
|
||||
if changes_only:
|
||||
response.append(expand_template(CHANGES_ONLY_TPL, params))
|
||||
else:
|
||||
if args.email:
|
||||
if email:
|
||||
email_header = expand_template(EMAIL_HEADER_TPL.strip(), params)
|
||||
print(email_header.lstrip())
|
||||
response.append(email_header.lstrip())
|
||||
header = expand_template(HEADER_RELEASE_TPL.strip(), params)
|
||||
print(parawrap.fill(header))
|
||||
print(expand_template(CHANGE_RELEASE_TPL, params))
|
||||
return 0
|
||||
response.append(parawrap.fill(header))
|
||||
response.append(expand_template(CHANGE_RELEASE_TPL, params))
|
||||
return '\n'.join(response)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
12
setup.cfg
12
setup.cfg
@ -1,11 +1,12 @@
|
||||
[metadata]
|
||||
name = release-tools
|
||||
name = releasetools
|
||||
summary = OpenStack Release Tools
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
@ -15,4 +16,11 @@ classifier =
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 2.6
|
||||
|
||||
[files]
|
||||
packages =
|
||||
releasetools
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
release-notes = releasetools.release_notes:main
|
||||
|
6
tox.ini
6
tox.ini
@ -4,6 +4,7 @@ envlist = py27,pep8,bashate
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
passenv=EMAIL
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
--allow-external lazr.authentication
|
||||
@ -30,7 +31,10 @@ commands = bash -c "find {toxinidir} \
|
||||
-print0 | xargs -0 bashate -v"
|
||||
|
||||
[testenv:venv]
|
||||
#commands = {posargs}
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:release_postversion]
|
||||
commands = {toxinidir}/release_postversion.sh {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
#commands = python setup.py testr --coverage --testr-args='{posargs}'
|
||||
|
Loading…
Reference in New Issue
Block a user