From 869c9f91c462bafc0678f0c745d54d4a4cce0cd9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 30 Aug 2017 13:02:50 +0100 Subject: [PATCH 1/4] Add hotfix support to release script --- zcutil/make-release.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/zcutil/make-release.py b/zcutil/make-release.py index 514d5883f..470fd09ec 100755 --- a/zcutil/make-release.py +++ b/zcutil/make-release.py @@ -27,6 +27,7 @@ def main(args=sys.argv[1:]): opts.RELEASE_VERSION, opts.RELEASE_PREV, opts.RELEASE_HEIGHT, + opts.HOTFIX, ) except SystemExit as e: logging.error(str(e)) @@ -44,6 +45,12 @@ def parse_args(args): type=str, help='Path to repository root.', ) + p.add_argument( + '--hotfix', + action='store_true', + dest='HOTFIX', + help='Use if this is a hotfix release from a non-master branch.', + ) p.add_argument( 'RELEASE_VERSION', type=Version.parse_arg, @@ -63,9 +70,9 @@ def parse_args(args): # Top-level flow: -def main_logged(release, releaseprev, releaseheight): +def main_logged(release, releaseprev, releaseheight, hotfix): verify_releaseprev_tag(releaseprev) - initialize_git(release) + initialize_git(release, hotfix) patch_version_in_files(release, releaseprev) patch_release_height(releaseheight) commit('Versioning changes for {}.'.format(release.novtext)) @@ -124,16 +131,20 @@ def verify_releaseprev_tag(releaseprev): @phase('Initializing git.') -def initialize_git(release): +def initialize_git(release, hotfix): junk = sh_out('git', 'status', '--porcelain') if junk.strip(): raise SystemExit('There are uncommitted changes:\n' + junk) branch = sh_out('git', 'rev-parse', '--abbrev-ref', 'HEAD').strip() - if branch != 'master': + if hotfix: + expected = 'hotfix-' + release.vtext + else: + expected = 'master' + if branch != expected: raise SystemExit( - "Expected branch 'master', found branch {!r}".format( - branch, + "Expected branch {!r}, found branch {!r}".format( + expected, branch, ), ) From df3005f9555bc808dc85102a6c612473fd601057 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 30 Aug 2017 14:19:09 +0100 Subject: [PATCH 2/4] Document the hotfix release process --- doc/hotfix-process.md | 57 ++++++++++++++++++++++++++++++++++++++++++ doc/release-process.md | 7 ++++++ 2 files changed, 64 insertions(+) create mode 100644 doc/hotfix-process.md diff --git a/doc/hotfix-process.md b/doc/hotfix-process.md new file mode 100644 index 000000000..9328b1f9d --- /dev/null +++ b/doc/hotfix-process.md @@ -0,0 +1,57 @@ +Hotfix Release Process +====================== + +In the commands below, and are prefixed with a v, ie. +v1.0.11 (not 1.0.11). + +## Create a hotfix branch + +Create a hotfix branch from the previous release tag, and push it to the main +repository: + + $ git branch hotfix- + $ git push 'git@github.com:zcash/zcash' hotfix- + +## Merge hotfix PRs + +Developer should create hotfix PRs based on the hotfix branch. Each PR should be +reviewed as normal, and then the following process should be used to merge: + +- A CI merge build is manually run, by force-triggering the pr-merge builder + with the following fields set: + + - Repository: https://github.com//zcash + - must be in the set of "safe" users as-specified in the CI + config. + - Branch: name of the hotfix PR branch (not the hotfix release branch). + +- A link to the build and its result is manually added to the PR as a comment. + +- If the build was successful, the PR is merged via the GitHub button. + +## Release process + +The majority of this process is identical to the standard release process. +However, there are a few notable differences: + +- When running the release script, use the `--hotfix` flag: + + $ ./zcutil/make-release.py --hotfix + +- To review the automated changes in git: + + $ git log hotfix-..HEAD + +- After the standard review process, use the hotfix merge process outlined above + instead of the regular merge process. + +- When making the tag, check out the hotfix branch instead of master. + +## Post-release + +Once the hotfix release has been created, a new PR should be opened for merging +the hotfix release branch into master. This may require fixing merge conflicts +(e.g. changing the version number in the hotfix branch to match master, if +master is ahead). Such conflicts **MUST** be addressed with additional commits +to the hotfix branch; specifically, the branch **MUST NOT** be rebased on +master. diff --git a/doc/release-process.md b/doc/release-process.md index d6090dfcd..bc19c5eb1 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -2,6 +2,8 @@ Release Process ==================== Meta: There should always be a single release engineer to disambiguate responsibility. +If this is a hotfix release, please see `./hotfix-process.md` before proceeding. + ## Pre-release ### Github Milestone @@ -40,6 +42,11 @@ whole engineering team. ## Release process +In the commands below, and are prefixed with a v, ie. +v1.0.9 (not 1.0.9). + +### Create the release branch + Run the release script, which will verify you are on the latest clean checkout of master, create a branch, then commit standard automated changes to that branch locally: From 24bfc7c6c6ba05153536b5345e7b0f35e19deb6c Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 31 Aug 2017 02:05:07 +0100 Subject: [PATCH 3/4] Enforce sequential hotfix versioning --- doc/hotfix-process.md | 6 ++++++ zcutil/make-release.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/doc/hotfix-process.md b/doc/hotfix-process.md index 9328b1f9d..15c88bbba 100644 --- a/doc/hotfix-process.md +++ b/doc/hotfix-process.md @@ -1,6 +1,12 @@ Hotfix Release Process ====================== +Hotfix releases are versioned by incrementing the build number of the latest +release. For example: + + First hotfix: v1.0.11 -> v1.0.11-1 + Second hotfix: v1.0.11-1 -> v1.0.11-2 + In the commands below, and are prefixed with a v, ie. v1.0.11 (not 1.0.11). diff --git a/zcutil/make-release.py b/zcutil/make-release.py index 470fd09ec..991fde051 100755 --- a/zcutil/make-release.py +++ b/zcutil/make-release.py @@ -72,6 +72,7 @@ def parse_args(args): # Top-level flow: def main_logged(release, releaseprev, releaseheight, hotfix): verify_releaseprev_tag(releaseprev) + verify_version(release, releaseprev, hotfix) initialize_git(release, hotfix) patch_version_in_files(release, releaseprev) patch_release_height(releaseheight) @@ -130,6 +131,26 @@ def verify_releaseprev_tag(releaseprev): ) +@phase('Checking version.') +def verify_version(release, releaseprev, hotfix): + if not hotfix: + return + + expected = Version( + releaseprev.major, + releaseprev.minor, + releaseprev.patch, + releaseprev.betarc, + releaseprev.hotfix + 1 if releaseprev.hotfix else 1, + ) + if release != expected: + raise SystemExit( + "Expected {!r}, given {!r}".format( + expected, release, + ), + ) + + @phase('Initializing git.') def initialize_git(release, hotfix): junk = sh_out('git', 'status', '--porcelain') From de3e1434618d7a22c38ab0b8a6158744c96c7579 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 15 Sep 2017 10:51:21 +0100 Subject: [PATCH 4/4] Clarify branching and force-building operations in hotfix process --- doc/hotfix-process.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/doc/hotfix-process.md b/doc/hotfix-process.md index 15c88bbba..e6ce8a7a2 100644 --- a/doc/hotfix-process.md +++ b/doc/hotfix-process.md @@ -18,13 +18,24 @@ repository: $ git branch hotfix- $ git push 'git@github.com:zcash/zcash' hotfix- +## Implement hotfix changes + +Hotfix changes are implemented the same way as regular changes (developers work +in separate branches per change, and push the branches to their own repositories), +except that the branches are based on the hotfix branch instead of master: + + $ git checkout hotfix- + $ git checkout -b + ## Merge hotfix PRs -Developer should create hotfix PRs based on the hotfix branch. Each PR should be -reviewed as normal, and then the following process should be used to merge: +Hotfix PRs are created like regular PRs, except using the hotfix branch as the +base instead of master. Each PR should be reviewed as normal, and then the +following process should be used to merge: -- A CI merge build is manually run, by force-triggering the pr-merge builder - with the following fields set: +- A CI merge build is manually run by logging into the CI server, going to the + pr-merge builder, clicking the "force" button, and entering the following + values: - Repository: https://github.com//zcash - must be in the set of "safe" users as-specified in the CI