Goals:
- Remove dependency on any proprietary build tools.
- Require at least 2 people to approve a release.
- Reduce possibility for error during the build process.
- Continue CalVer version numbering scheme.
- Improve artifact publication security.
Status Quo
Besu currently does branch based release strategy, using a proprietary tool developed by ConsenSys. Regardless of the branch being released from, a new release branch is created and pushed to origin. This triggers an additional (arguably unnecessary) build PR for the merge back to the “releasing branch” (master, release-<calver>, etc). It also forces a “quiet period” where we do not / cannot merge to the branch-to-release. The branch based flow also submits an additional post-release PR to merge the next project version property (so that build artifacts are <next-calver>-SNAPSHOT).
Lastly, this process requires at least 2 contributors to complete the release process because 2 rounds of PR approvals are necessary to proceed. The requirement for more than one person to participate is an important feature of this process which we want to preserve.
Overall, the branch flow requires 3 CI builds and 2 PR approvals to complete a release. It seems that this flow was built to support quarterly releases, but was generalized for regular releases also.
Perfect Scenario
Any 2 authorized individuals (TBD) can inspect a proposed release candidate, and upon approval the binary goes unchanged into the GitHub release page, and any other supported artifact distribution systems.
- Releaser has easy to find changelog to review.
- Releaser has easy to find PR that shows changes from previous release for closer inspection.
- Immutability of release metadata?
- Documentation changes?
- One-click-execution?
- Audit trail?
Proposal 1: Tag Based Releases
The idea for this was stolen from the Teku project at ConsenSys. Tag releases are commit based rather than branch HEAD based. When releasing from master, there is no branch management, PRs or approval required to release. There will be a single build of the release tag by circle-ci. The release process is hands-off and automated all the way up to the release on github. This is a substantial time savings and much lower friction compared to the branch based release strategy.
Pros:
- Super low friction, a release is triggered by the application of a tag, and CI does the rest.
- Can target any branch.
- Allows possibility of using an existing binary built for the commit the tag is at.
Cons:
- A single contributor can do a release since there are no PR approvals required. This is a concerning issue that must be solved, since a single contributor could ‘go rogue’.
- Metadata of the release (the tag itself) is mutable and could be moved around the underlying repository history.
- The semantic meaning of the release must be encodable in the tag itself.
- Does not allow for differences between a snapshot and a release artifact. The artifact released would ALSO exist as a development snapshot.
Deficiencies compared to status quo.
The status-quo logic “recognizes” a release branch by naming convention and increments the artifact version correctly for release candidates and final versions. Also the branch flow deletes the long-lived release branch upon release of a final quarterly (x.y.0) release The tag flow does not have any provision for quarterly or release candidate flows. IMO, this can be worked around manually by the deployer, or quarterly release behavior could be added as a config to the tag flow without too much effort.
The primary post-deployment difference between the existing tag and branch based release flows is snapshot versioning for interim builds. The existing tag flow does not push a subsequent PR to change the release version to <calver>-SNAPSHOT at the end of the process. While this means that an additional contributor is not necessary for post-deployment, it also means that interim builds will have a common or static name like `besu-develop`. This is an artifact of the no-PR approach of this release flow. Again I think this is not terribly difficult to add as a configurable behavior to the existing tag flow. Alternatively, we could invest effort into a dynamic version calculation build function, though the mechanism for determining the current snapshot version is not straightforward when we are not on a tagged commit.
Proposal 2: Gradle Release Process
This process would be more like a Maven release process. Release approvers approve a specific commit, and that is run through a build task that adjusts the version number, and then re-runs CI to produce the release artifact. The diff between the release artifact and the commit it is sourced from should only show the new embedded release metadata; usually GAV coordinates and any build timestamps.
Pros:
- Immutable build metadata, embedded in the release artifact.
- Can target any branch.
Cons:
- Requires logic in build tools.
- Requires release branches, which are the only place that release version numbers exist. These may have value for near-future hotfixes to a published version, but will definitely need to be pruned over time.
- Security becomes a CI concern, instead of something we trust GitHub with by using branch permissions.