Merge pull request #5883 from nuttycom/util/release_from_commit_hash
Build releases from a commit hash, rather than a named branch.
This commit is contained in:
commit
9227cfb5cb
|
@ -30,45 +30,20 @@ except that the branches are based on the hotfix branch instead of master:
|
|||
## Merge hotfix PRs
|
||||
|
||||
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 logging into the CI server, going to the
|
||||
pr-merge builder, clicking the "force" button, and entering the following
|
||||
values:
|
||||
|
||||
- Repository: https://github.com/<DevUser>/zcash
|
||||
- <DevUser> 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.
|
||||
base instead of `master`. Each PR should be reviewed and merged as normal.
|
||||
|
||||
## Release process
|
||||
|
||||
The majority of this process is identical to the standard release process.
|
||||
However, there are a few notable differences:
|
||||
Release candidates for hotfixes should be created and tested as normal, using
|
||||
the `hotfix-<RELEASE>` branch in place of the release stabilization branch,
|
||||
with a couple of minor differences:
|
||||
|
||||
- When running the release script, use the `--hotfix` flag:
|
||||
- When running the release script, use the `--hotfix` flag. Provide the hash of
|
||||
the commit to be released as the first argument:
|
||||
|
||||
$ ./zcutil/make-release.py --hotfix <RELEASE> <RELEASE_PREV> <APPROX_RELEASE_HEIGHT>
|
||||
$ ./zcutil/make-release.py --hotfix <COMMIT_ID> <RELEASE> <RELEASE_PREV> <APPROX_RELEASE_HEIGHT>
|
||||
|
||||
- To review the automated changes in git:
|
||||
|
||||
$ git log hotfix-<RELEASE>..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.
|
||||
|
|
|
@ -2,7 +2,9 @@ Release Process
|
|||
====================
|
||||
Meta: There should always be a single release engineer to disambiguate responsibility.
|
||||
|
||||
If this is a hotfix release, please see the [Hotfix Release Process](https://github.com/zcash/zcash/blob/master/doc/hotfix-process.md) documentation before proceeding.
|
||||
If this is a hotfix release, please see the [Hotfix Release
|
||||
Process](https://github.com/zcash/zcash/blob/master/doc/hotfix-process.md) documentation
|
||||
before proceeding.
|
||||
|
||||
## Pre-release
|
||||
|
||||
|
@ -61,62 +63,105 @@ The release script has the following dependencies:
|
|||
You can optionally install the `progressbar2` Python module with pip to have a
|
||||
progress bar displayed during the build process.
|
||||
|
||||
## Release process
|
||||
## Versioning
|
||||
|
||||
In the commands below, <RELEASE> and <RELEASE_PREV> are prefixed with a v, ie.
|
||||
v1.1.0 (not 1.1.0).
|
||||
Zcash version identifiers have the format `vX.Y.Z` with the following conventions:
|
||||
|
||||
### Create the release branch
|
||||
* Increments to the `X` component (the "major version") correspond to network
|
||||
upgrades. A network upgrade occurs only when there is a change to the
|
||||
consensus rules.
|
||||
* Increments to the `Y` component (the "minor version") correspond to regular
|
||||
Zcash releases. These occur approximately every 6 weeks and may include breaking
|
||||
changes to public APIs.
|
||||
* Increments to the `Z` component occur only in the case of hotfix releases.
|
||||
|
||||
Run the release script, which will verify you are on the latest clean
|
||||
checkout of master, create a branch, then commit standard automated
|
||||
## Release candidate & release process
|
||||
|
||||
Identify the commit from which the release stabilization branch will be made.
|
||||
Release stabilization branches are used so that development can proceed
|
||||
unblocked on the `master` branch during the release candidate testing and
|
||||
bug-fixing process. By convention, release stabilization branches are named
|
||||
`version-X.Y.0` where `X` and `Y` are the major and minor versions for the
|
||||
release.
|
||||
|
||||
In the commands below, <RELEASE> and <RELEASE_PREV> must identify `git` tags
|
||||
prefixed with the character `v`, i.e. `v1.0.9` (not `1.0.9`). <COMMIT_ID> is a
|
||||
`git` hash identifying the commit on which a release stabilization or release
|
||||
branch will be based. It is recommended to use the entire hash value to
|
||||
identify the commit, although a prefix of at least 10 characters is also
|
||||
permitted.
|
||||
|
||||
### Create the release stabilization branch
|
||||
|
||||
Having identified the commit from which the release will be made, the release
|
||||
manager constructs the release stabilization branch as follows:
|
||||
|
||||
$ git checkout -b version-X.Y.0 <COMMIT_ID>
|
||||
$ git push 'git@github.com:zcash/zcash' $(git rev-parse --abrev-ref HEAD)
|
||||
|
||||
### Create the release candidate branch
|
||||
|
||||
Run the release script to create the first release candidate. This will create
|
||||
a branch based upon the specified commit ID, then commit standard automated
|
||||
changes to that branch locally:
|
||||
|
||||
$ ./zcutil/make-release.py <RELEASE> <RELEASE_PREV> <RELEASE_FROM> <APPROX_RELEASE_HEIGHT>
|
||||
$ ./zcutil/make-release.py <COMMIT_ID> <RELEASE> <RELEASE_PREV> <RELEASE_FROM> <APPROX_RELEASE_HEIGHT>
|
||||
|
||||
Examples:
|
||||
|
||||
$ ./zcutil/make-release.py v1.1.0 v1.0.0 v1.0.0 120000
|
||||
$ ./zcutil/make-release.py v1.1.0 v1.1.0-rc1 v1.0.0 222900
|
||||
$ ./zcutil/make-release.py 600c4acee1 v1.1.0-rc1 v1.0.0 v1.0.0 280300
|
||||
$ ./zcutil/make-release.py b89b48cda1 v1.1.0 v1.1.0-rc1 v1.0.0 300600
|
||||
|
||||
### Create, Review, and Merge the release branch pull request
|
||||
|
||||
Review the automated changes in git:
|
||||
|
||||
$ git log master..HEAD
|
||||
$ git log version-X.Y.0..HEAD
|
||||
|
||||
Push the resulting branch to github:
|
||||
|
||||
$ git push 'git@github.com:$YOUR_GITHUB_NAME/zcash' $(git rev-parse --abbrev-ref HEAD)
|
||||
|
||||
Then create the PR on github. Complete the standard review process,
|
||||
then merge, then wait for CI to complete.
|
||||
Then create the PR on github targeting the `version-X.Y.0` branch. Complete the
|
||||
standard review process and wait for CI to complete.
|
||||
|
||||
## Make tag for the newly merged result
|
||||
## Make a tag for the tip of the release candidate branch
|
||||
|
||||
Checkout master and pull the latest version to ensure master is up to date with the release PR which was merged in before.
|
||||
NOTE: This has changed from the previously recommended process. The tag should
|
||||
be created at the tip of the automatically-generated release branch created by
|
||||
the release script; this ensures that any changes made to the release
|
||||
stabilization branch since the initiation of the release process are not
|
||||
accidentally tagged as being part of the release as a consequence of having
|
||||
been included in a merge commit.
|
||||
|
||||
$ git checkout master
|
||||
$ git pull --ff-only
|
||||
|
||||
Check the last commit on the local and remote versions of master to make sure they are the same:
|
||||
Check the last commit on the local and remote versions of the release branch to
|
||||
make sure they are the same:
|
||||
|
||||
$ git log -1
|
||||
|
||||
The output should include something like, which is created by Homu:
|
||||
|
||||
Auto merge of #4242 - nathan-at-least:release-v1.0.9, r=nathan-at-least
|
||||
|
||||
If you haven't previously done so, set the gpg key id you intend to use for signing:
|
||||
If you haven't previously done so, set the gpg key id you intend to use for
|
||||
signing:
|
||||
|
||||
git config --global user.signingkey <keyid>
|
||||
|
||||
Then create the git tag. The `-s` means the release tag will be signed.
|
||||
Enter "Release <version>." and save when prompted for a commit message.
|
||||
**CAUTION:** Remember the `v` at the beginning here:
|
||||
Then create the git tag. The `-s` means the release tag will be signed. Enter
|
||||
"Release <version>." and save when prompted for a commit message. **CAUTION:**
|
||||
Remember the `v` at the beginning here:
|
||||
|
||||
$ git tag -s v1.1.0
|
||||
$ git push origin v1.1.0
|
||||
$ git tag -s vX.Y.Z-rcN
|
||||
$ git push origin vX.Y.Z-rcN
|
||||
|
||||
## Merge the release candidate branch to the release stabilization branch
|
||||
|
||||
Once CI has completed and the release candidate branch has sufficient approving
|
||||
reviews, merge the release candidate branch back to the release stabilization
|
||||
branch. Testing proceeds as normal. Any changes that need to be made during the
|
||||
release candidate period are made by submitting PRs targeting the release
|
||||
stabilization branch.
|
||||
|
||||
Subsequent release candidates, and the creation of the final release, follow
|
||||
the same process as for release candidates, omitting the `-rcN` suffix for the
|
||||
final release.
|
||||
|
||||
## Make and deploy deterministic builds
|
||||
|
||||
|
@ -143,22 +188,35 @@ the marking to see what GitHub wants to be done.
|
|||
|
||||
## Post Release Task List
|
||||
|
||||
### Merge the release stabilization branch
|
||||
|
||||
Once the final release branch has merged to the release stabilization branch, a
|
||||
new PR should be opened for merging the release stabilization branch into
|
||||
master. This may require fixing merge conflicts (e.g. changing the version
|
||||
number in the release stabilization branch to match master, if master is
|
||||
ahead). Such conflicts **MUST** be addressed with additional commits to the
|
||||
release stabilization branch; specifically, the branch **MUST NOT** be rebased
|
||||
on master.
|
||||
|
||||
Once any conflicts have been resolved, the release stabilization branch should
|
||||
be merged back to the `master` branch, and then deleted.
|
||||
|
||||
### Deploy testnet
|
||||
|
||||
Notify the Zcash DevOps engineer/sysadmin that the release has been tagged. They update some variables in the company's automation code and then run an Ansible playbook, which:
|
||||
Notify the Zcash DevOps engineer/sysadmin that the release has been tagged. They update
|
||||
some variables in the company's automation code and then run an Ansible playbook, which:
|
||||
|
||||
* builds Zcash based on the specified branch
|
||||
* deploys it as a public service (e.g. betatestnet.z.cash, mainnet.z.cash)
|
||||
* often the same server can be re-used, and the role idempotently handles upgrades, but if not then they also need to update DNS records
|
||||
* possible manual steps: blowing away the `testnet3` dir, deleting old parameters, restarting DNS seeder
|
||||
* deploys it as a public service (e.g. testnet.z.cash, mainnet.z.cash)
|
||||
* often the same server can be re-used, and the role idempotently handles upgrades, but if
|
||||
not then they also need to update DNS records
|
||||
* possible manual steps: blowing away the `testnet3` dir, deleting old parameters,
|
||||
restarting DNS seeder.
|
||||
|
||||
Then, verify that nodes can connect to the testnet server, and update the guide on the wiki to ensure the correct hostname is listed in the recommended zcash.conf.
|
||||
Verify that nodes can connect to the mainnet and testnet servers.
|
||||
|
||||
### Update the 1.0 User Guide
|
||||
|
||||
This also means updating [the translations](https://github.com/zcash/zcash-docs).
|
||||
Coordinate with the translation team for now. Suggestions for improving this
|
||||
part of the process should be added to #2596.
|
||||
Update the [Zcashd Full Node and CLI](https://zcash.readthedocs.io/en/latest/rtd_pages/zcashd.html)
|
||||
documentation on ReadTheDocs to give the new version number.
|
||||
|
||||
### Publish the release announcement (blog, github, zcash-dev, slack)
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ def main(args=sys.argv[1:]):
|
|||
|
||||
try:
|
||||
main_logged(
|
||||
opts.REVISION,
|
||||
opts.RELEASE_VERSION,
|
||||
opts.RELEASE_PREV,
|
||||
opts.RELEASE_FROM,
|
||||
|
@ -52,6 +53,11 @@ def parse_args(args):
|
|||
dest='HOTFIX',
|
||||
help='Use if this is a hotfix release from a non-master branch.',
|
||||
)
|
||||
p.add_argument(
|
||||
'REVISION',
|
||||
type=GitHash.parse_arg,
|
||||
help='The git commit hash from which to construct the release.',
|
||||
)
|
||||
p.add_argument(
|
||||
'RELEASE_VERSION',
|
||||
type=Version.parse_arg,
|
||||
|
@ -76,16 +82,16 @@ def parse_args(args):
|
|||
|
||||
|
||||
# Top-level flow:
|
||||
def main_logged(release, releaseprev, releasefrom, releaseheight, hotfix):
|
||||
def main_logged(revision, release, releaseprev, releasefrom, releaseheight, hotfix):
|
||||
verify_dependencies([
|
||||
('help2man', None),
|
||||
('debchange', 'devscripts'),
|
||||
])
|
||||
|
||||
verify_tags(releaseprev, releasefrom)
|
||||
verify_tags(revision, releaseprev, releasefrom)
|
||||
verify_version(release, releaseprev, hotfix)
|
||||
verify_dependency_updates()
|
||||
initialize_git(release, hotfix)
|
||||
initialize_git(revision, release, hotfix)
|
||||
patch_version_in_files(release, releaseprev)
|
||||
patch_release_height(releaseheight)
|
||||
commit('Versioning changes for {}.'.format(release.novtext))
|
||||
|
@ -131,10 +137,10 @@ def verify_dependency_updates():
|
|||
try:
|
||||
sh_log('./qa/zcash/updatecheck.py')
|
||||
except SystemExit:
|
||||
raise SystemExit("Dependency update check found updates that have not been correctly postponed.")
|
||||
raise SystemExit("Dependency update check failed. Either some updates have not been correctly postponed, or the .updatecheck-token file is missing.")
|
||||
|
||||
@phase('Checking tags.')
|
||||
def verify_tags(releaseprev, releasefrom):
|
||||
def verify_tags(revision, releaseprev, releasefrom):
|
||||
candidates = []
|
||||
|
||||
# Any tag beginning with a 'v' followed by [1-9] must be a version
|
||||
|
@ -143,7 +149,7 @@ def verify_tags(releaseprev, releasefrom):
|
|||
# ignored. Any other tag is silently ignored.
|
||||
candidatergx = re.compile('^v[1-9].*$')
|
||||
|
||||
for tag in sh_out('git', 'tag', '--list').splitlines():
|
||||
for tag in sh_out('git', 'tag', '--list', '--merged', revision.value).splitlines():
|
||||
if candidatergx.match(tag):
|
||||
v = Version.parse(tag)
|
||||
if v is not None:
|
||||
|
@ -153,12 +159,18 @@ def verify_tags(releaseprev, releasefrom):
|
|||
try:
|
||||
latest = candidates[-1]
|
||||
except IndexError:
|
||||
raise SystemExit('No previous releases found by `git tag --list`.')
|
||||
raise SystemExit(
|
||||
'No previous releases found by `git tag --list --merged {}`.'
|
||||
.format(
|
||||
revision.value
|
||||
),
|
||||
)
|
||||
|
||||
if releaseprev != latest:
|
||||
raise SystemExit(
|
||||
'The latest candidate in `git tag --list` is {} not {}'
|
||||
'The latest candidate in `git tag --list --merged {} is {} not {}'
|
||||
.format(
|
||||
revision.value,
|
||||
latest.vtext,
|
||||
releaseprev.vtext,
|
||||
),
|
||||
|
@ -173,9 +185,10 @@ def verify_tags(releaseprev, releasefrom):
|
|||
prev_tags.append(candidate)
|
||||
else:
|
||||
raise SystemExit(
|
||||
'{} does not appear in `git tag --list`'
|
||||
'{} does not appear in `git tag --list --merged {}`'
|
||||
.format(
|
||||
releasefrom.vtext,
|
||||
revision.value,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -211,29 +224,20 @@ def verify_version(release, releaseprev, hotfix):
|
|||
|
||||
|
||||
@phase('Initializing git.')
|
||||
def initialize_git(release, hotfix):
|
||||
def initialize_git(revision, 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 hotfix:
|
||||
expected = 'hotfix-' + release.vtext
|
||||
else:
|
||||
expected = 'master'
|
||||
if branch != expected:
|
||||
raise SystemExit(
|
||||
"Expected branch {!r}, found branch {!r}".format(
|
||||
expected, branch,
|
||||
),
|
||||
)
|
||||
|
||||
logging.info('Ensuring we are up to date with the branch to be released...')
|
||||
sh_log('git', 'pull', '--ff-only')
|
||||
|
||||
branch = 'release-' + release.vtext
|
||||
logging.info('Creating release branch: %r', branch)
|
||||
sh_log('git', 'checkout', '-b', branch)
|
||||
logging.info(
|
||||
'Creating release branch {} from revision {}.'
|
||||
.format(
|
||||
branch,
|
||||
revision.value
|
||||
)
|
||||
)
|
||||
sh_log('git', 'checkout', '-b', branch, revision.value)
|
||||
return branch
|
||||
|
||||
|
||||
|
@ -481,6 +485,30 @@ def sh_progress(markers, *args):
|
|||
if status != 0:
|
||||
raise SystemExit('Nonzero exit status: {!r}'.format(status))
|
||||
|
||||
class GitHash (object):
|
||||
'''A git commit hash.'''
|
||||
RGX = re.compile(
|
||||
r'^([0-9a-f]{10,40})$',
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def parse_arg(text):
|
||||
m = GitHash.RGX.match(text)
|
||||
if m is None:
|
||||
raise argparse.ArgumentTypeError(
|
||||
'Could not parse revision {!r} against regex {}'.format(
|
||||
text,
|
||||
GitHash.RGX.pattern,
|
||||
),
|
||||
)
|
||||
else:
|
||||
assert len(m.groups()) == 1
|
||||
[value] = m.groups()
|
||||
return GitHash(value)
|
||||
|
||||
def __init__(self, value):
|
||||
assert GitHash.RGX.match(value) is not None
|
||||
self.value = value
|
||||
|
||||
class Version (object):
|
||||
'''A release version.'''
|
||||
|
@ -652,6 +680,11 @@ class TestVersion (unittest.TestCase):
|
|||
v = Version.parse_arg(case)
|
||||
self.assertEqual(v.vtext, case)
|
||||
|
||||
def test_rev_parse(self):
|
||||
sample = '958bcf2dac6d81d17797c0f58f176262a496cfd4'
|
||||
rev = GitHash.parse_arg(sample)
|
||||
self.assertEqual(rev.value, sample)
|
||||
|
||||
def test_arg_parse_negatives(self):
|
||||
cases = [
|
||||
'v07.0.0',
|
||||
|
@ -697,7 +730,7 @@ if __name__ == '__main__':
|
|||
|
||||
print('=== Self Test ===')
|
||||
try:
|
||||
unittest.main()
|
||||
unittest.main(verbosity=2)
|
||||
except SystemExit as e:
|
||||
if e.args[0] != 0:
|
||||
raise
|
||||
|
|
Loading…
Reference in New Issue