diff options
Diffstat (limited to 'tools/dist/create-minor-release-branch.py')
-rwxr-xr-x | tools/dist/create-minor-release-branch.py | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/tools/dist/create-minor-release-branch.py b/tools/dist/create-minor-release-branch.py new file mode 100755 index 0000000..7126edd --- /dev/null +++ b/tools/dist/create-minor-release-branch.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python +# python: coding=utf-8 +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +# About this script: +# This script is intended to automate steps in creating a new Subversion +# minor release. + +import os +import re +import sys +import logging +import subprocess +import argparse # standard in Python 2.7 + +from release import Version + + +# Some constants +repos = 'https://svn.apache.org/repos/asf/subversion' +secure_repos = 'https://svn.apache.org/repos/asf/subversion' +buildbot_repos = 'https://svn.apache.org/repos/infra/infrastructure/buildbot/aegis/buildmaster' + +# Local working copies +base_dir = None # set by main() + +def get_trunk_wc_path(path=None): + trunk_wc_path = os.path.join(base_dir, 'svn-trunk') + if path is None: return trunk_wc_path + return os.path.join(trunk_wc_path, path) +def get_branch_wc_path(ver, path=None): + branch_wc_path = os.path.join(base_dir, ver.branch + '.x') + if path is None: return branch_wc_path + return os.path.join(branch_wc_path, path) +def get_buildbot_wc_path(path=None): + buildbot_wc_path = os.path.join(base_dir, 'svn-buildmaster') + if path is None: return buildbot_wc_path + return os.path.join(buildbot_wc_path, path) + +def get_trunk_url(): + return secure_repos + '/trunk' +def get_branch_url(ver): + return secure_repos + '/branches/' + ver.branch + '.x' +def get_tag_url(ver): + return secure_repos + '/tags/' + ver.base +def get_buildbot_url(): + return buildbot_repos + +#---------------------------------------------------------------------- +# Utility functions + +def run(cmd, dry_run=False): + print('+ ' + ' '.join(cmd)) + if not dry_run: + stdout = subprocess.check_output(cmd) + print(stdout) + +def run_svn(cmd, dry_run=False): + run(['svn'] + cmd, dry_run) + +def svn_commit(cmd): + run_svn(['commit'] + cmd, dry_run=True) + +def svn_checkout(*args): + args = ['checkout'] + list(args) + ['--revision={2017-12-01}'] + run_svn(args) + +#---------------------------------------------------------------------- +def edit_file(path, pattern, replacement): + print("Editing '%s'" % (path,)) + print(" pattern='%s'" % (pattern,)) + print(" replace='%s'" % (replacement,)) + old_text = open(path, 'r').read() + new_text = re.sub(pattern, replacement, old_text) + assert new_text != old_text + open(path, 'w').write(new_text) + +def prepend_file(path, text): + print("Prepending to '%s'" % (path,)) + print(" text='%s'" % (text,)) + original = open(path, 'r').read() + open(path, 'w').write(text + original) + +#---------------------------------------------------------------------- +def make_release_branch(ver): + run_svn(['copy', get_trunk_url(), get_branch_url(ver), + '-m', 'Create the ' + ver.branch + '.x release branch.'], + dry_run=True) + +#---------------------------------------------------------------------- +def update_minor_ver_in_trunk(ver): + """Change the minor version in trunk to the next (future) minor version. + """ + trunk_wc = get_trunk_wc_path() + trunk_url = get_trunk_url() + svn_checkout(trunk_url, trunk_wc) + + prev_ver = Version('1.%d.0' % (ver.minor - 1,)) + next_ver = Version('1.%d.0' % (ver.minor + 1,)) + relpaths = [] + + relpath = 'subversion/include/svn_version.h' + relpaths.append(relpath) + edit_file(get_trunk_wc_path(relpath), + r'(#define SVN_VER_MINOR *)%s' % (ver.minor,), + r'\g<1>%s' % (next_ver.minor,)) + + relpath = 'subversion/tests/cmdline/svntest/main.py' + relpaths.append(relpath) + edit_file(get_trunk_wc_path(relpath), + r'(SVN_VER_MINOR = )%s' % (ver.minor,), + r'\g<1>%s' % (next_ver.minor,)) + + relpath = 'subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java' + relpaths.append(relpath) + try: + # since r1817921 (just after branching 1.10) + edit_file(get_trunk_wc_path(relpath), + r'SVN_VER_MINOR = %s;' % (ver.minor,), + r'SVN_VER_MINOR = %s;' % (next_ver.minor,)) + except: + # before r1817921: two separate places + edit_file(get_trunk_wc_path(relpath), + r'version.isAtLeast\(1, %s, 0\)' % (ver.minor,), + r'version.isAtLeast\(1, %s, 0\)' % (next_ver.minor,)) + edit_file(get_trunk_wc_path(relpath), + r'1.%s.0, but' % (ver.minor,), + r'1.%s.0, but' % (next_ver.minor,)) + + relpath = 'CHANGES' + relpaths.append(relpath) + # insert at beginning of CHANGES file + prepend_file(get_trunk_wc_path(relpath), + 'Version ' + next_ver.base + '\n' + + '(?? ??? 20XX, from /branches/' + next_ver.branch + '.x)\n' + + get_tag_url(next_ver) + '\n' + + '\n') + + log_msg = '''\ +Increment the trunk version number to %s, and introduce a new CHANGES +section, following the creation of the %s.x release branch. + +* subversion/include/svn_version.h, + subversion/bindings/javahl/src/org/apache/subversion/javahl/NativeResources.java, + subversion/tests/cmdline/svntest/main.py + (SVN_VER_MINOR): Increment to %s. + +* CHANGES: New section for %s.0. +''' % (next_ver.branch, ver.branch, next_ver.minor, next_ver.branch) + commit_paths = [get_trunk_wc_path(p) for p in relpaths] + svn_commit(commit_paths + ['-m', log_msg]) + +#---------------------------------------------------------------------- +def create_status_file_on_branch(ver): + branch_wc = get_branch_wc_path(ver) + branch_url = get_branch_url(ver) + svn_checkout(branch_url, branch_wc, '--depth=immediates') + + status_local_path = os.path.join(branch_wc, 'STATUS') + text='''\ + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * THIS RELEASE STREAM IS OPEN FOR STABILIZATION. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +This file tracks the status of releases in the %s.x line. + +See http://subversion.apache.org/docs/community-guide/releasing.html#release-stabilization +for details on how release lines and voting work, what kinds of bugs can +delay a release, etc. + +Status of %s: + +Candidate changes: +================== + + +Veto-blocked changes: +===================== + + +Approved changes: +================= +''' % (ver.branch, ver.base) + open(status_local_path, 'wx').write(text) + run_svn(['add', status_local_path]) + svn_commit([status_local_path, + '-m', '* branches/' + ver.branch + '.x/STATUS: New file.']) + +#---------------------------------------------------------------------- +def update_backport_bot(ver): + print("""MANUAL STEP: Fork & edit & pull-request on GitHub: +https://github.com/apache/infrastructure-puppet/blob/deployment/modules/svnqavm_pvm_asf/manifests/init.pp +"Add new %s.x branch to list of backport branches" +""" % (ver.branch,)) + print("""Someone needs to run the 'svn checkout' manually. +The exact checkout command is documented in machines/svn-qavm2/notes.txt +in the private repository (need to use a trunk client and the svn-master.a.o +hostname). +""") + +#---------------------------------------------------------------------- +def update_buildbot_config(ver): + """Add the new branch to the list of branches monitored by the buildbot + master. + """ + buildbot_wc = get_buildbot_wc_path() + buildbot_url = get_buildbot_url() + svn_checkout(buildbot_url, buildbot_wc) + + prev_ver = Version('1.%d.0' % (ver.minor - 1,)) + next_ver = Version('1.%d.0' % (ver.minor + 1,)) + + relpath = 'master1/projects/subversion.conf' + edit_file(get_buildbot_wc_path(relpath), + r'(MINOR_LINES=\[.*%s)(\])' % (prev_ver.minor,), + r'\1, %s\2' % (ver.minor,)) + + log_msg = '''\ +Subversion: start monitoring the %s branch. +''' % (ver.branch) + commit_paths = [get_buildbot_wc_path(relpath)] + svn_commit(commit_paths + ['-m', log_msg]) + +#---------------------------------------------------------------------- +def steps(args): + ver = Version('1.10.0') + + make_release_branch(ver) + update_minor_ver_in_trunk(ver) + create_status_file_on_branch(ver) + update_backport_bot(ver) + update_buildbot_config(ver) + + +#---------------------------------------------------------------------- +# Main entry point for argument parsing and handling + +def main(): + 'Parse arguments, and drive the appropriate subcommand.' + + # Setup our main parser + parser = argparse.ArgumentParser( + description='Create an Apache Subversion release branch.') + parser.add_argument('--verbose', action='store_true', default=False, + help='Increase output verbosity') + parser.add_argument('--base-dir', default=os.getcwd(), + help='''The directory in which to create needed files and + folders. The default is the current working + directory.''') + subparsers = parser.add_subparsers(title='subcommands') + + # Setup the parser for the build-env subcommand + subparser = subparsers.add_parser('steps', + help='''Run the release-branch-creation steps.''') + subparser.set_defaults(func=steps) + + # Parse the arguments + args = parser.parse_args() + + global base_dir + base_dir = args.base_dir + + # Set up logging + logger = logging.getLogger() + if args.verbose: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + # Make timestamps in tarballs independent of local timezone + os.environ['TZ'] = 'UTC' + + # finally, run the subcommand, and give it the parsed arguments + args.func(args) + + +if __name__ == '__main__': + main() |