Add --interactive to new-releases

Shamelessly steal^Wcopy some of the code from interactive-release so
that after validating the release we list the changes that will be
released.  This gives us the ability to decide that the release
contains no functional changes and elect to not create that release.

NOTE: I chose not to use interactive-release as it seems not to
correctly handle first releases in a series (because it doesn't load all
release history.  It also doesn't use some of the new features (like
series_status).  Adding --interactive to new-release gets us a long way
to deprecating interactive-release but we aren't quire there yet.

Change-Id: I25bbb4d7df9ae618500dd37f4b0cbc32c0bbd153
This commit is contained in:
Tony Breeds 2019-06-06 21:39:32 +10:00
parent 2c0feab157
commit 4cf2670c1f
2 changed files with 70 additions and 11 deletions

View File

@ -12,8 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import print_function
import argparse
import atexit
import logging
@ -22,6 +20,8 @@ import shutil
import sys
import tempfile
from openstack_releases.cmds.interactive_release import clean_changes
from openstack_releases.cmds.interactive_release import yes_no_prompt
from openstack_releases import gitutils
from openstack_releases import series_status
from openstack_releases import yamlutils
@ -178,6 +178,12 @@ def main():
action='store_true',
help='be more chatty',
)
parser.add_argument(
'-i', '--interactive',
default=False,
action='store_true',
help='Be interactive and only make releases when instructed'
)
parser.add_argument(
'release_type',
choices=('bugfix', 'feature', 'major', 'milestone', 'rc',
@ -239,7 +245,7 @@ def main():
if args.cleanup:
shutil.rmtree(workdir, True)
else:
print('not cleaning up %s' % workdir)
LOG.warning('not cleaning up %s', workdir)
atexit.register(cleanup_workdir)
# Allow for independent projects.
@ -441,6 +447,9 @@ def main():
sha = gitutils.sha_for_tag(workdir, repo, version)
# Check out the working repo to the sha
gitutils.checkout_ref(workdir, repo, sha)
if is_retagging:
changes += 1
LOG.info('re-tagging %s at %s (%s)', repo, sha, previous_tag)
@ -468,6 +477,29 @@ def main():
projects.append(new_project)
elif previous_sha != sha or force_tag:
# TODO(tonyb): Do this early and also prompt for release type.
# Once we do that we can probably deprecate interactive-release
if args.interactive:
# NOTE(tonyb): This is pretty much just copied from
# interactive-release
last_tag = '.'.join(last_version)
change_lines = list(clean_changes(gitutils.changes_since(
workdir, repo, last_tag).splitlines()))
max_changes_show = 100
LOG.info('')
if last_tag:
LOG.info("%s changes to %s since %s are:",
len(change_lines), repo, last_tag)
else:
LOG.info("%s changes to %s are:", len(change_lines), repo)
for sha, descr in change_lines[0:max_changes_show]:
LOG.info("* %s %s", sha[:7], descr)
leftover_change_lines = change_lines[max_changes_show:]
if leftover_change_lines:
LOG.info(" and %s more changes...",
len(leftover_change_lines))
LOG.info('')
changes += 1
LOG.info('advancing %s from %s (%s) to %s',
repo, previous_sha, previous_tag, sha)
@ -507,7 +539,12 @@ def main():
'location': new_version,
})
if changes > 0:
create_release = changes > 0
if create_release and args.interactive:
create_release = yes_no_prompt(
'Create a release in %s containing those changes? ' % series)
if create_release:
deliverable_filename = 'deliverables/%s/%s.yaml' % (
series, args.deliverable)
with open(deliverable_filename, 'w', encoding='utf-8') as f:

View File

@ -42,6 +42,26 @@ def find_modified_deliverable_files():
return filenames
def changes_since(workdir, repo, ref):
"""Get all changes between the last ref and the current point.
:param workdir: The git repo working directory.
:param repo: The name of the repo.
:param ref: The starting ref.
:returns: Merged commits between the two points.
"""
try:
changes = processutils.check_output(
['git', 'log', '--decorate', '--no-merges', '--pretty=oneline',
"%s..HEAD" % ref],
cwd=os.path.join(workdir, repo),
).decode('utf-8').strip()
except processutils.CalledProcessError as err:
LOG.error('Could not find {}: {}'.format(ref, err))
changes = ''
return changes
def commit_exists(workdir, repo, ref):
"""Return boolean specifying whether the reference exists in the repository.
@ -126,7 +146,7 @@ def safe_clone_repo(workdir, repo, ref, messages):
return True
def checkout_ref(workdir, repo, ref, messages):
def checkout_ref(workdir, repo, ref, messages=None):
"""Checkout a specific ref in the repo."""
LOG.debug('Resetting the repository %s to HEAD', repo)
@ -138,9 +158,10 @@ def checkout_ref(workdir, repo, ref, messages):
['git', 'reset', '--hard'],
cwd=os.path.join(workdir, repo))
except processutils.CalledProcessError as err:
messages.warning(
'Could not reset repository {} to HEAD: {}'.format(
repo, err))
if messages:
messages.warning(
'Could not reset repository {} to HEAD: {}'.format(
repo, err))
LOG.debug('Checking out repository %s to %s', repo, ref)
try:
@ -148,9 +169,10 @@ def checkout_ref(workdir, repo, ref, messages):
['git', 'checkout', ref],
cwd=os.path.join(workdir, repo))
except processutils.CalledProcessError as err:
messages.error(
'Could not checkout repository {} at {}: {}'.format(
repo, ref, err))
if messages:
messages.error(
'Could not checkout repository {} at {}: {}'.format(
repo, ref, err))
return False
return True