From 276ba8d195e09ae65323bc2cc04734a842f5ca10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadej=20Jane=C5=BE?= Date: Fri, 25 Sep 2020 17:56:20 +0200 Subject: [PATCH] internal: Refactor project's version handling and release process Simplify version computation logic in Make files and start using Punch's version file to track version. --- .changelog/3327.doc.md | 3 + .changelog/3327.internal.2.md | 4 + .changelog/3327.internal.3.md | 6 + .github/workflows/release.yml | 20 +++- .goreleaser.yml | 6 +- .punch_config.py | 11 +- .punch_version.py | 3 + Makefile | 70 ++++++++---- common.mk | 210 +++++++++++++++++++++++----------- docs/release-process.md | 163 +++++++++++++------------- towncrier.toml | 5 + 11 files changed, 321 insertions(+), 180 deletions(-) create mode 100644 .changelog/3327.doc.md create mode 100644 .changelog/3327.internal.2.md create mode 100644 .changelog/3327.internal.3.md create mode 100644 .punch_version.py diff --git a/.changelog/3327.doc.md b/.changelog/3327.doc.md new file mode 100644 index 00000000000..df62532ee07 --- /dev/null +++ b/.changelog/3327.doc.md @@ -0,0 +1,3 @@ +Update [Release Process] documentation + +[Release Process]: docs/release-process.md diff --git a/.changelog/3327.internal.2.md b/.changelog/3327.internal.2.md new file mode 100644 index 00000000000..1841d86929a --- /dev/null +++ b/.changelog/3327.internal.2.md @@ -0,0 +1,4 @@ +internal: Refactor project's version handling + +Simplify version computation logic in Make files and start using Punch's +version file to track version. diff --git a/.changelog/3327.internal.3.md b/.changelog/3327.internal.3.md new file mode 100644 index 00000000000..d86ec5bc9cc --- /dev/null +++ b/.changelog/3327.internal.3.md @@ -0,0 +1,6 @@ +Make: Refactor release-related targets + +- Rename `tag-next-release` target to `release-tag`. +- Rename `release` target to `release-build`. +- Add `release-stable-branch` target that creates and pushes a stable branch + for the current release. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e18eb6d599c..d3a3bdfb370 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,13 +18,21 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 + with: + # Fetch all history as the recommended way to fetch all tags and + # branches of the project. + # This allows the release helpers in common.mk to determine the + # project's version from git correctly. + # For more info, see: + # https://github.com/actions/checkout#fetch-all-history-for-all-tags-and-branches + fetch-depth: 0 - name: Set up Go 1.15 uses: actions/setup-go@v2.1.2 with: go-version: "1.15.x" - name: Set up Rust uses: actions-rs/toolchain@v1 - - name: Install oasis-node prerequisites + - name: Install Oasis Node prerequisites run: | sudo apt-get update sudo apt-get install make libseccomp-dev protobuf-compiler @@ -43,9 +51,15 @@ jobs: GORELEASER_VERSION: 0.127.0 GORELEASER_TARBALL: goreleaser_Linux_x86_64.tar.gz CURL_CMD: curl --proto =https --tlsv1.2 -sSL - - name: Create release + - name: Set RELEASE_BRANCH name for stable/bugfix releases + run: | + GIT_VERSION=${GITHUB_REF#refs/tags/v} + if [[ ! ${GIT_VERSION} =~ ^[0-9]+\.[0-9]+$ ]]; then + echo ::set-env name=RELEASE_BRANCH::stable/${GIT_VERSION%.*}.x + fi + - name: Build and publish the next release run: | - make release + make release-build env: # Instruct Make to create a real release. OASIS_CORE_REAL_RELEASE: "true" diff --git a/.goreleaser.yml b/.goreleaser.yml index a675b111c15..ec578eca9df 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -24,7 +24,7 @@ builds: # to ldflags. # For more details, see: https://github.com/oasislabs/goreleaser/issues/1. - -buildid= - - -X github.com/oasisprotocol/oasis-core/go/common/version.SoftwareVersion={{.Env.VERSION}} + - "{{.Env.GOLDFLAGS_VERSION}}" goos: - linux goarch: @@ -42,7 +42,7 @@ builds: # to ldflags. # For more details, see: https://github.com/oasislabs/goreleaser/issues/1. - -buildid= - - -X github.com/oasisprotocol/oasis-core/go/common/version.SoftwareVersion={{.Env.VERSION}} + - "{{.Env.GOLDFLAGS_VERSION}}" goos: - linux goarch: @@ -60,7 +60,7 @@ builds: # to ldflags. # For more details, see: https://github.com/oasislabs/goreleaser/issues/1. - -buildid= - - -X github.com/oasisprotocol/oasis-core/go/common/version.SoftwareVersion={{.Env.VERSION}} + - "{{.Env.GOLDFLAGS_VERSION}}" goos: - linux goarch: diff --git a/.punch_config.py b/.punch_config.py index bcd93188738..d555d06a70b 100644 --- a/.punch_config.py +++ b/.punch_config.py @@ -1,7 +1,11 @@ +# Punch configuration file. + +# For more information, see: https://punch.readthedocs.io/. + __config_version__ = 1 GLOBALS = { - 'serializer': '{{year}}.{{minor}}', + 'serializer': '{{year}}.{{minor}}.{{micro}}', } # NOTE: The FILES list is not allowed to be empty, so we need to pass it at @@ -18,7 +22,12 @@ 'name': 'minor', 'type': 'integer', }, + { + 'name': 'micro', + 'type': 'integer', + }, ] + ACTIONS = { 'custom_bump': { 'type': 'conditional_reset', diff --git a/.punch_version.py b/.punch_version.py new file mode 100644 index 00000000000..b3a1ba4eab4 --- /dev/null +++ b/.punch_version.py @@ -0,0 +1,3 @@ +year = '20' +minor = 11 +micro = 0 diff --git a/Makefile b/Makefile index ed9f8862ca6..79bea8317d6 100644 --- a/Makefile +++ b/Makefile @@ -118,7 +118,7 @@ test-e2e: test: $(test-targets) # Clean. -clean-targets := clean-runtimes clean-rust clean-go clean-version-files +clean-targets := clean-runtimes clean-rust clean-go clean-runtimes: @$(ECHO) "$(CYAN)*** Cleaning up runtimes...$(OFF)" @@ -135,10 +135,6 @@ clean-rust: clean-go: @$(MAKE) -C go clean -clean-version-files: - @$(ECHO) "$(CYAN)*** Cleaning Punch version files...$(OFF)" - @rm --force $(_PUNCH_VERSION_FILE_PATH_PREFIX)*.py - clean: $(clean-targets) # Fetch all the latest changes (including tags) from the canonical upstream git @@ -147,36 +143,65 @@ fetch-git: @$(ECHO) "Fetching the latest changes (including tags) from $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote..." @git fetch $(OASIS_CORE_GIT_ORIGIN_REMOTE) --tags -# Assemble Change log. -changelog: fetch-git - @$(ENSURE_NEXT_VERSION) - @$(ECHO) "Generating Change Log for version $(NEXT_VERSION)..." - towncrier build --version $(NEXT_VERSION) +# Private target for bumping project's version using the Punch tool. +# NOTE: It should not be invoked directly. +_version-bump: fetch-git + @$(ENSURE_VALID_RELEASE_BRANCH_NAME) + @$(ENSURE_GIT_VERSION_FROM_TAG_EQUALS_PUNCH_VERSION) + @$(PUNCH_BUMP_VERSION) + @git add $(PUNCH_VERSION_FILE) + +# Private target for assembling the Change Log. +# NOTE: It should not be invoked directly. +_changelog: + @$(ECHO) "$(CYAN)*** Generating Change Log for version $(PUNCH_VERSION)...$(OFF)" + @$(BUILD_CHANGELOG) @$(ECHO) "Next, review the staged changes, commit them and make a pull request." @$(WARN_BREAKING_CHANGES) +# Assemble Change Log. +# NOTE: We need to call Make recursively since _version-bump target updates +# Punch's version and hence we need Make to re-evaluate the PUNCH_VERSION +# variable. +changelog: _version-bump + @$(MAKE) --no-print-directory _changelog + # Tag the next release. -tag-next-release: fetch-git - @$(ENSURE_NEXT_VERSION) - @$(ECHO) "Checking if we can tag version $(NEXT_VERSION) as the next release..." +release-tag: fetch-git + @$(ECHO) "Checking if we can tag version $(PUNCH_VERSION) as the next release..." @$(ENSURE_VALID_RELEASE_BRANCH_NAME) + @$(ENSURE_RELEASE_TAG_DOES_NOT_EXIST) @$(ENSURE_NO_CHANGELOG_FRAGMENTS) - @$(ENSURE_NEXT_VERSION_IN_CHANGELOG) + @$(ENSURE_NEXT_RELEASE_IN_CHANGELOG) @$(ECHO) "All checks have passed. Proceeding with tagging the $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH)'s HEAD with tags:\n- $(RELEASE_TAG)\n- $(RELEASE_TAG_GO)" @$(CONFIRM_ACTION) @$(ECHO) "If this appears to be stuck, you might need to touch your security key for GPG sign operation." - @git tag --sign --message="Version $(NEXT_VERSION)" $(RELEASE_TAG) $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) + @git tag --sign --message="Version $(PUNCH_VERSION)" $(RELEASE_TAG) $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) @$(ECHO) "If this appears to be stuck, you might need to touch your security key for GPG sign operation." - @git tag --sign --message="Version $(NEXT_VERSION)" $(RELEASE_TAG_GO) $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) + @git tag --sign --message="Version $(PUNCH_VERSION)" $(RELEASE_TAG_GO) $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) @git push $(OASIS_CORE_GIT_ORIGIN_REMOTE) $(RELEASE_TAG) $(RELEASE_TAG_GO) - @$(ECHO) "$(CYAN)The following tags have been successfully pushed to $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote:\n- $(RELEASE_TAG)\n- $(RELEASE_TAG_GO)$(OFF)" + @$(ECHO) "$(CYAN)*** The following tags have been successfully pushed to $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote:\n- $(RELEASE_TAG)\n- $(RELEASE_TAG_GO)$(OFF)" + +# Create and push a stable branch for the current release. +release-stable-branch: fetch-git + @$(ECHO) "Checking if we can create a stable release branch for version $(PUNCH_VERSION)...$(OFF)" + @$(ENSURE_VALID_STABLE_BRANCH) + @$(ENSURE_RELEASE_TAG_EXISTS) + @$(ENSURE_STABLE_BRANCH_DOES_NOT_EXIST) + @$(ECHO) "All checks have passed. Proceeding with creating the '$(STABLE_BRANCH)' branch on $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote." + @$(CONFIRM_ACTION) + @git branch $(STABLE_BRANCH) $(RELEASE_TAG) + @git push $(OASIS_CORE_GIT_ORIGIN_REMOTE) $(STABLE_BRANCH) + @$(ECHO) "$(CYAN)*** Branch '$(STABLE_BRANCH)' has been sucessfully pushed to $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote.$(OFF)" -# Prepare release. -release: +# Build and publish the next release. +release-build: + @$(ENSURE_VALID_RELEASE_BRANCH_NAME) + @$(ENSURE_GIT_VERSION_EQUALS_PUNCH_VERSION) @$(ECHO) "$(CYAN)*** Building release version of oasis-core-runtime-loader...$(OFF)" @CARGO_TARGET_DIR=target/default cargo build -p oasis-core-runtime-loader --release @cp target/default/release/oasis-core-runtime-loader . - @$(ECHO) "$(CYAN)*** Preparing release archive...$(OFF)" + @$(ECHO) "$(CYAN)*** Creating release for version $(PUNCH_VERSION)...$(OFF)" @goreleaser $(GORELEASER_ARGS) @rm oasis-core-runtime-loader @@ -200,5 +225,8 @@ docker-shell: $(lint-targets) lint \ $(test-unit-targets) $(test-targets) test \ $(clean-targets) clean \ - fetch-git changelog tag-next-release release docker-shell \ + fetch-git \ + _version_bump _changelog changelog \ + release-tag release-stable-branch release-build \ + docker-shell \ all diff --git a/common.mk b/common.mk index 2bc02a8c830..eb3d0c5c505 100644 --- a/common.mk +++ b/common.mk @@ -34,47 +34,72 @@ OASIS_CORE_GIT_ORIGIN_REMOTE ?= origin # Name of the branch where to tag the next release. RELEASE_BRANCH ?= master -# Try to determine Oasis Core's version from git. -LATEST_TAG := $(shell git describe --tags --match 'v*' --abbrev=0 2>/dev/null) -VERSION := $(subst v,,$(LATEST_TAG)) -IS_TAG := $(shell git describe --tags --match 'v*' --exact-match 2>/dev/null && echo YES || echo NO) -ifeq ($(and $(LATEST_TAG),$(IS_TAG)),NO) - # The current commit is not exactly a tag, append commit and dirty info to - # the version. - VERSION := $(VERSION)-git$(shell git describe --always --match '' --dirty=+dirty 2>/dev/null) +# Determine project's version from git. +GIT_VERSION_LATEST_TAG := $(shell git describe --tags --match 'v*' --abbrev=0 2>/dev/null $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) || echo undefined) +GIT_VERSION_FROM_TAG := $(subst v,,$(GIT_VERSION_LATEST_TAG)) +GIT_VERSION_IS_TAG := $(shell git describe --tags --match 'v*' --exact-match &>/dev/null $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) && echo YES || echo NO) +ifeq ($(GIT_VERSION_IS_TAG),YES) + GIT_VERSION := $(GIT_VERSION_FROM_TAG) +else + # The current commit is not exactly a tag, append commit and dirty info to + # the version. + GIT_VERSION := $(GIT_VERSION_FROM_TAG)-git$(shell git describe --always --match '' --dirty=+dirty 2>/dev/null) endif -export VERSION +# Determine project's git branch. GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) -# Try to compute the next version based on the latest tag of the origin remote -# using the Punch tool. -# First, all tags from the origin remote are fetched. Next, the latest tag on -# origin's release branch is determined. It represents Oasis Core's current -# version. Lastly, the Punch tool is used to bump the version according to the -# configurated versioning scheme in .punch_config.py. -# -# NOTE: This is a little messy because Punch doesn't support the following at -# the moment: -# - Passing current version as an CLI parameter. -# - Outputting the new version to stdout without making modifications to any -# files. -_PUNCH_VERSION_FILE_PATH_PREFIX := /tmp/oasis-core -# NOTE: The "OUTPUT = $(eval OUTPUT := $$(shell some-comand))$(OUTPUT)" syntax -# defers simple variable expansion so that it is only computed the first time it -# is used. For more details, see: -# http://make.mad-scientist.net/deferred-simple-variable-expansion/. -_PUNCH_VERSION_FILE = $(eval _PUNCH_VERSION_FILE := $$(shell \ - mktemp $(_PUNCH_VERSION_FILE_PATH_PREFIX).XXXXX.py \ - ))$(_PUNCH_VERSION_FILE) -NEXT_VERSION ?= $(eval NEXT_VERSION := $$(shell \ - set -e; \ - LATEST_TAG_ORIGIN=`git describe --tags --match 'v*' --abbrev=0 $(OASIS_CORE_GIT_ORIGIN_REMOTE)/master` \ - python3 -c "import os; year, minor = os.environ['LATEST_TAG_ORIGIN'].lstrip('v').split('.'); \ - print(f'year=\"{year}\"\nminor={minor}')" > $(_PUNCH_VERSION_FILE); \ - punch --config-file .punch_config.py --version-file $(_PUNCH_VERSION_FILE) --action custom_bump --quiet; \ - python3 -c "exec(open('$(_PUNCH_VERSION_FILE)').read()); print('{}.{}'.format(year, minor))" \ - ))$(NEXT_VERSION) +PUNCH_CONFIG_FILE := .punch_config.py +PUNCH_VERSION_FILE := .punch_version.py +# Obtain project's version as tracked by the Punch tool. +# NOTE: The Punch tool doesn't have the ability fo print project's version to +# stdout yet. +# For more details, see: https://github.com/lgiordani/punch/issues/42. +PUNCH_VERSION := $(shell python3 -c "exec(open('$(PUNCH_VERSION_FILE)').read()); version = f'{year}.{minor}.{micro}' if micro > 0 else f'{year}.{minor}'; print(version)") + +# Helper that bumps project's version with the Punch tool. +define PUNCH_BUMP_VERSION = + if [[ "$(RELEASE_BRANCH)" == master ]]; then \ + FLAG="--action custom_bump"; \ + elif [[ "$(RELEASE_BRANCH)" == stable/* ]]; then \ + if [[ -n "$(CHANGELOG_FRAGMENTS_BREAKING)" ]]; then \ + $(ECHO) "$(RED)Error: There shouldn't be breaking changes in a release on a stable branch.$(OFF)"; \ + $(ECHO) "List of detected breaking changes:"; \ + for fragment in "$(CHANGELOG_FRAGMENTS_BREAKING)"; do \ + $(ECHO) "- $$fragment"; \ + done; \ + exit 1; \ + else \ + FLAG="--part micro"; \ + fi; \ + else \ + $(ECHO) "$(RED)Error: Unsupported release branch: '$(RELEASE_BRANCH)'.$(OFF)"; \ + exit 1; \ + fi; \ + punch --config-file $(PUNCH_CONFIG_FILE) --version-file $(PUNCH_VERSION_FILE) $$FLAG --quiet +endef + +# Helper that ensures project's version according to the latest Git tag equals +# project's version as tracked by the Punch tool. +define ENSURE_GIT_VERSION_FROM_TAG_EQUALS_PUNCH_VERSION = + if [[ "$(GIT_VERSION_FROM_TAG)" != "$(PUNCH_VERSION)" ]]; then \ + $(ECHO) "$(RED)Error: Project version according to the latest Git tag from \ + $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) ($(GIT_VERSION)) \ + doesn't equal project's version in $(PUNCH_VERSION_FILE) ($(PUNCH_VERSION)).$(OFF)"; \ + exit 1; \ + fi +endef + +# Helper that ensures project's version determined from git equals project's +# version as tracked by the Punch tool. +define ENSURE_GIT_VERSION_EQUALS_PUNCH_VERSION = + if [[ "$(GIT_VERSION)" != "$(PUNCH_VERSION)" ]]; then \ + $(ECHO) "$(RED)Error: Project's version for $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) \ + determined from git ($(GIT_VERSION)) doesn't equal project's version in \ + $(PUNCH_VERSION_FILE) ($(PUNCH_VERSION)).$(OFF)"; \ + exit 1; \ + fi +endef # Go binary to use for all Go commands. OASIS_GO ?= go @@ -82,15 +107,6 @@ OASIS_GO ?= go # Go command prefix to use in all Go commands. GO := env -u GOPATH $(OASIS_GO) -# NOTE: The -trimpath flag strips all host dependent filesystem paths from -# binaries which is required for deterministic builds. -GOFLAGS ?= -trimpath -v - -# Add Oasis Core's version as a linker string value definition. -ifneq ($(VERSION),) - export GOLDFLAGS ?= "-X github.com/oasisprotocol/oasis-core/go/common/version.SoftwareVersion=$(VERSION) -X github.com/oasisprotocol/oasis-core/go/common/version.GitBranch=$(GIT_BRANCH)" -endif - # Go build command to use by default. GO_BUILD_CMD := env -u GOPATH $(OASIS_GO) build $(GOFLAGS) @@ -100,27 +116,88 @@ GO_TEST_HELPER_MKVS_PATH := storage/mkvs/interop/mkvs-test-helpers # Path to the example signer plugin binary in go/. GO_EXAMPLE_PLUGIN_PATH := oasis-test-runner/scenario/pluginsigner/example_signer_plugin -# Helper that ensures $(NEXT_VERSION) variable is not empty. -define ENSURE_NEXT_VERSION = - if [[ -z "$(NEXT_VERSION)" ]]; then \ - $(ECHO) "$(RED)Error: Could not compute project's next version.$(OFF)"; \ +# NOTE: The -trimpath flag strips all host dependent filesystem paths from +# binaries which is required for deterministic builds. +GOFLAGS ?= -trimpath -v + +# Project's version as the linker's string value definition. +export GOLDFLAGS_VERSION := -X github.com/oasisprotocol/oasis-core/go/common/version.SoftwareVersion=$(GIT_VERSION) +# Project's git branch as the linker's string value definition. +GOLDFLAGS_BRANCH := -X github.com/oasisprotocol/oasis-core/go/common/version.GitBranch=$(GIT_BRANCH) + +# Go's linker flags. +export GOLDFLAGS ?= "$(GOLDFLAGS_VERSION) $(GOLDFLAGS_BRANCH)" + +# Helper that ensures the origin's release branch's HEAD contains a Change Log +# section for the next release. +define ENSURE_NEXT_RELEASE_IN_CHANGELOG = + if ! ( git show $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH):CHANGELOG.md | \ + grep --quiet '^## $(PUNCH_VERSION) (.*)' ); then \ + $(ECHO) "$(RED)Error: Could not locate Change Log section for release $(PUNCH_VERSION) on $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) branch.$(OFF)"; \ exit 1; \ fi endef # Git tag of the next release. -RELEASE_TAG := v$(NEXT_VERSION) +RELEASE_TAG := v$(PUNCH_VERSION) # Go Modules compatible Git tag of the next release. -RELEASE_TAG_GO = $(eval RELEASE_TAG_GO := $$(shell \ - python3 -c "ver_parts = '$(NEXT_VERSION)'.split('.'); \ +RELEASE_TAG_GO := $(shell \ + python3 -c "ver_parts = '$(PUNCH_VERSION)'.split('.'); \ ver_parts.append(0) if len(ver_parts) == 2 else ''; \ print('go/v0.{}{:0>2}.{}'.format(*ver_parts))" \ - ))$(RELEASE_TAG_GO) + ) + +# Helper that ensures the new release's tag doesn't already exist on the origin +# remote. +define ENSURE_RELEASE_TAG_EXISTS = + if ! git ls-remote --exit-code --tags $(OASIS_CORE_GIT_ORIGIN_REMOTE) $(RELEASE_TAG) 1>/dev/null; then \ + $(ECHO) "$(RED)Error: Tag '$(RELEASE_TAG)' doesn't exist on $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote.$(OFF)"; \ + exit 1; \ + fi +endef + +# Helper that ensures the new release's tag doesn't already exist on the origin +# remote. +define ENSURE_RELEASE_TAG_DOES_NOT_EXIST = + if git ls-remote --exit-code --tags $(OASIS_CORE_GIT_ORIGIN_REMOTE) $(RELEASE_TAG) 1>/dev/null; then \ + $(ECHO) "$(RED)Error: Tag '$(RELEASE_TAG)' already exists on $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote.$(OFF)"; \ + exit 1; \ + fi; \ + if git show-ref --quiet --tags $(RELEASE_TAG); then \ + $(ECHO) "$(RED)Error: Tag '$(RELEASE_TAG)' already exists locally.$(OFF)"; \ + exit 1; \ + fi +endef + +# Name of the stable release branch (if the current version is appropriate). +STABLE_BRANCH := $(shell python3 -c "exec(open('$(PUNCH_VERSION_FILE)').read()); print(f'stable/{year}.{minor}.x') if micro == 0 else print('undefined')") + +# Helper that ensures the stable branch name is valid. +define ENSURE_VALID_STABLE_BRANCH = + if [[ "$(STABLE_BRANCH)" == "undefined" ]]; then \ + $(ECHO) "$(RED)Error: Cannot create a stable release branch for version $(PUNCH_VERSION).$(OFF)"; \ + exit 1; \ + fi +endef + +# Helper that ensures the new stable branch doesn't already exist on the origin +# remote. +define ENSURE_STABLE_BRANCH_DOES_NOT_EXIST = + if git ls-remote --exit-code --heads $(OASIS_CORE_GIT_ORIGIN_REMOTE) $(STABLE_BRANCH) 1>/dev/null; then \ + $(ECHO) "$(RED)Error: Branch '$(STABLE_BRANCH)' already exists on $(OASIS_CORE_GIT_ORIGIN_REMOTE) remote.$(OFF)"; \ + exit 1; \ + fi; \ + if git show-ref --quiet --heads $(STABLE_BRANCH); then \ + $(ECHO) "$(RED)Error: Branch '$(STABLE_BRANCH)' already exists locally.$(OFF)"; \ + exit 1; \ + fi +endef -# Helper that ensures $(RELEASE_BRANCH) variable contains a valid release branch name. +# Helper that ensures $(RELEASE_BRANCH) variable contains a valid release branch +# name. define ENSURE_VALID_RELEASE_BRANCH_NAME = if [[ ! $(RELEASE_BRANCH) =~ ^(master|(stable/[0-9]+\.[0-9]+\.x$$)) ]]; then \ - $(ECHO) "$(RED)Error: Invalid release branch name: $(RELEASE_BRANCH)."; \ + $(ECHO) "$(RED)Error: Invalid release branch name: '$(RELEASE_BRANCH)'."; \ exit 1; \ fi endef @@ -138,15 +215,6 @@ define ENSURE_NO_CHANGELOG_FRAGMENTS = fi endef -# Helper that ensures the origin's release branch's HEAD contains a Change Log section for the next release. -define ENSURE_NEXT_VERSION_IN_CHANGELOG = - if ! ( git show $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH):CHANGELOG.md | \ - grep --quiet '^## $(NEXT_VERSION) (.*)' ); then \ - $(ECHO) "$(RED)Error: Could not locate Change Log section for release $(NEXT_VERSION) on $(OASIS_CORE_GIT_ORIGIN_REMOTE)/$(RELEASE_BRANCH) branch.$(OFF)"; \ - exit 1; \ - fi -endef - # Auxiliary variable that defines a new line for later substitution. define newline @@ -160,7 +228,7 @@ For a list of changes in this release, see the [Change Log]. *NOTE: If you are upgrading from an earlier release, please **carefully review** the [Change Log] for **Removals and Breaking changes**.* -[Change Log]: https://github.com/oasisprotocol/oasis-core/blob/v$(VERSION)/CHANGELOG.md +[Change Log]: https://github.com/oasisprotocol/oasis-core/blob/v$(GIT_VERSION)/CHANGELOG.md endef @@ -174,6 +242,16 @@ _ := $(shell printf "$(subst ",\",$(subst $(newline),\n,$(RELEASE_TEXT)))" > $(_ GORELEASER_ARGS = release --release-notes $(_RELEASE_NOTES_FILE) endif +# Manually set GoReleaser's release tag since its automatic detection fails when +# two tags point to the same commit. +# In our case, each release has two tags: +# - an ordinary Git tag +# - a Go Modules compatible Git tag +# and hence we need to set it manually. +# For more details, see: +# https://goreleaser.com/customization/build/#define-build-tag +export GORELEASER_CURRENT_TAG := $(RELEASE_TAG) + # List of non-trivial Change Log fragments. CHANGELOG_FRAGMENTS_NON_TRIVIAL := $(filter-out $(wildcard .changelog/*trivial*.md),$(wildcard .changelog/[0-9]*.md)) diff --git a/docs/release-process.md b/docs/release-process.md index 91e32c68c21..5e853d60e0c 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -1,4 +1,4 @@ -# Release process +# Release Process The following steps should be followed when preparing a release. @@ -7,17 +7,17 @@ The following steps should be followed when preparing a release. Our release process relies on some tooling that needs to be available on a maintainer's system: -- [Python] 3.5+. -- [Oasis Labs' towncrier fork]. +- [Python] 3.6+. +- [Oasis' towncrier fork]. - [Punch] 2.0.x. Most systems should already have [Python] pre-installed. -To install [Oasis Labs' towncrier fork] and [Punch], use [pip]: +To install [Oasis' towncrier fork] and [Punch], use [pip]: ```bash pip3 install --upgrade \ - https://github.com/oasislabs/towncrier/archive/oasis-master.tar.gz \ + https://github.com/oasisprotocol/towncrier/archive/oasis-master.tar.gz \ punch.py~=2.0.0 ``` @@ -26,10 +26,11 @@ via so-called [User install] (i.e. isolated to the current user). [Python]: https://www.python.org/ -[Oasis Labs' towncrier fork]: https://github.com/oasislabs/towncrier +[Oasis' towncrier fork]: https://github.com/oasisprotocol/towncrier [Punch]: https://github.com/lgiordani/punch [pip]: https://pip.pypa.io/en/stable/ -[Python virtual environment]: https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments +[Python virtual environment]: + https://packaging.python.org/tutorials/installing-packages/#creating-virtual-environments [User install]: https://pip.pypa.io/en/stable/user_guide/#user-installs @@ -38,36 +39,37 @@ via so-called [User install] (i.e. isolated to the current user). Our [Make] tooling has some targets that automate parts of the release process and try to make it less error-prone: -- `changelog`: Assembles the [Change Log] from the [Change Log fragments] using - the [towncrier] utility. -- `tag-next-release`: After performing a bunch of sanity checks, it tags the - git origin remote's release branch's `HEAD` with the `v` tag - and pushes it to the remote. +- `changelog`: Bumps project's version with the [Punch] utility and assembles + the [Change Log] from the [Change Log Fragments] using the + [towncrier][Oasis' towncrier fork] utility. +- `release-tag`: After performing a bunch of sanity checks, it tags the git + origin remote's release branch's `HEAD` with the `v` tag and + pushes it to the remote. +- `release-stable-branch`: Creates and pushes a stable branch for the current + release. -Note that both targets depend on the `fetch-git` target which fetches the latest -changes (including tags) from the git origin remote to ensure the computed next -version and other things are always up-to-date. +Note that all above targets depend on the `fetch-git` target which fetches the +latest changes (including tags) from the git origin remote to ensure the +computed next version and other things are always up-to-date. -The next version of Oasis Core's regularly scheduled release is computed -automatically using the [Punch] utility and the configuration in the -`.punch_config.py` file based on the latest version tag present in git origin -remote's `master` branch. +The version of the Oasis Core's next release is computed automatically using +the [Punch] utility according to the project's [Versioning] scheme. -To override the automatically computed next version, one can pass the -`NEXT_VERSION` environment variable when calling [Make]. +The `changelog` Make target checks the name of the branch on which the release +is being made to know which part of the project's version to bump. -It is also possible to set the following environment variables to customize the -release process: +To customize the release process, one can set the following environment +variables: - `OASIS_CORE_GIT_ORIGIN_REMOTE` (default: `origin`): Name of the git remote pointing to the canonical upstream git repository. - `RELEASE_BRANCH` (default: `master`): Name of the branch where to tag the next release. -[Make]: https://en.wikipedia.org/wiki/Make_(software) +[Make]: https://en.wikipedia.org/wiki/Make_\(software\) [Change Log]: ../CHANGELOG.md -[Change Log fragments]: ../.changelog/README.md -[towncrier]: https://github.com/hawkowl/towncrier +[Change Log Fragments]: ../.changelog/README.md +[Versioning]: versioning.md ## Preparing a Regular Release @@ -84,11 +86,11 @@ that bumps the respective version(s) before proceeding with the release process. Before a release, all [Change Log fragments] should be assembled into a new section of the [Change Log] using the `changelog` [Make] target. -Create a new branch, e.g. `/changelog-`, and then +Create a new branch, e.g. `/changelog`, and then run [Make]: ```bash -git checkout -b /changelog- +git checkout -b /changelog make changelog ``` @@ -116,20 +118,20 @@ For example: | Runtime Host | 1.0.0 | | Runtime Committee | 1.0.0 | -After you've made the changes, commit them, push them to the origin and make a -pull request. +After you are content with the changes, commit them, push them to the origin +and make a pull request. Once the pull request had been reviewed and merged, proceed to the next step. [version-file]: ../go/common/version/version.go -### Tag the next release +### Tag Next Release To create a signed git tag from the latest commit in origin remote's `master` branch, use: ```bash -make tag-next-release +make release-tag ``` This command will perform a bunch of sanity checks to prevent common errors @@ -137,16 +139,7 @@ while tagging the next release. After those checks have passed, it will ask for confirmation before proceeding. -### Create a `stable/YY.MINOR.x` branch - -Prepare a new stable branch from the tag and push it to the origin: - -```bash -git checkout -b stable/${TAG_VERSION}.x v${TAG_VERSION} -git push -u origin stable/${TAG_VERSION}.x -``` - -### Ensure a GitHub release was published +### Ensure GitHub Release Was Published After the tag with the next release is pushed to the [canonical git repository], the GitHub Actions [Release manager workflow] is triggered which uses the @@ -156,94 +149,92 @@ checksums, and publish a GitHub Release that accompanies the versioned git tag. Browse to [Oasis Core's releases page] and make sure the new release is properly published. - +### Create `stable/MAJOR.MINOR.x` Branch + +To prepare a new stable branch from the new release tag and push it to the +origin remote, use: + +```bash +make release-stable-branch +``` + +This command will perform sanity checks to prevent common errors. + +After those checks have passed, it will ask for confirmation before proceeding. + [canonical git repository]: https://github.com/oasisprotocol/oasis-core [Release manager workflow]: ../.github/workflows/release.yml [GoReleaser]: https://goreleaser.com/ -[Oasis Core's releases page]: https://github.com/oasisprotocol/oasis-core/releases - +[Oasis Core's releases page]: + https://github.com/oasisprotocol/oasis-core/releases -## Preparing a bugfix/stable release +## Preparing a Bugfix/Stable Release -As mentioned in the [Versioning scheme] document, sometimes we will encounter a -situation when there is a major (security) fix that we want to back-port from an -upcoming release and release it, without also releasing all the other +As mentioned in the [Versioning] documentation, sometimes we will want to +back-port some fixes (e.g. a security fix) and (backwards compatible) changes +from an upcoming release and release them without also releasing all the other (potentially breaking) changes. -To make the following steps easier, set the `BACKPORT_VERSION` environment -variable to the `YY.MINOR` release you want to back-port the changes to, e.g. -`20.1`: +To make the following steps easier, set the `RELEASE_BRANCH` environment +variable to the name of the stable branch of the `YY.MINOR` release you want +to back-port the changes to, e.g. `stable/20.11.x`: ```bash -BACKPORT_VERSION="20.1" +RELEASE_BRANCH="stable/20.11.x" ``` -[Versioning scheme]: versioning.md +### Back-port Changes -### Back-port the changes - -Create a new branch, e.g. -`/stable/${BACKPORT_VERSION}.x/backport-foo`, from the -`stable/${BACKPORT_VERSION}.x` branch: +Create a new branch, e.g. `/${RELEASE_BRANCH}/backport-foo`, from +the `${RELEASE_BRANCH}` branch: ```bash -git checkout -b stable/${BACKPORT_VERSION}.x/backport-foo - stable/${BACKPORT_VERSION}.x +git checkout -b /${RELEASE_BRANCH}/backport-foo ${RELEASE_BRANCH} ``` After back-porting all the desired changes, push it to the origin and make a -pull request against the `stable/${BACKPORT_VERSION}.x` branch. +pull request against the `${RELEASE_BRANCH}` branch. -### Prepare the Change Log for the bugfix/stable release +### Prepare Change Log for Bugfix/Stable Release As with a regular release, the back-ported changes should include the -corresponding [Change Log fragments] that need to be assembled into a new +corresponding [Change Log Fragments] that need to be assembled into a new section of the [Change Log] using the `changelog` [Make] target. -Create a new branch, e.g. -`/stable/${BACKPORT_VERSION}.x/changelog-`, from the -`stable/${BACKPORT_VERSION}.x` branch: +Create a new branch, e.g. `/${RELEASE_BRANCH}/changelog`, from the +`${RELEASE_BRANCH}` branch: ```bash -git checkout -b /stable/${BACKPORT_VERSION}.x/changelog- \ - stable/${BACKPORT_VERSION}.x +git checkout -b /${RELEASE_BRANCH}/changelog ${RELEASE_BRANCH} ``` -Then run [Make]'s `changelog` target and manually set the `NEXT_VERSION` -environment variable to the appropriate version, e.g. `${BACKPORT_VERSION}.1`, -and over-ride the release branch by setting the `RELEASE_BRANCH` environment -variable to `stable/${BACKPORT_VERSION}.x`: +Then run [Make]'s `changelog` target: ```bash -NEXT_VERSION=${BACKPORT_VERSION}.1 \ -RELEASE_BRANCH=stable/${BACKPORT_VERSION}.x \ make changelog ``` +*NOTE: The `changelog` Make target will bump the `MICRO` part of the version +automatically.* + After reviewing the staged changes, commit them, push the changes to the origin -and make a pull request against the `stable/${BACKPORT_VERSION}.x` branch. +and make a pull request against the `${RELEASE_BRANCH}` branch. Once the pull request had been reviewed and merged, proceed to the next step. -### Tag the bugfix/stable release +### Tag Bugfix/Stable Release As with a regular release, create a signed git tag from the latest commit in -origin remote's release branch. -Again, you need to manually set the `NEXT_VERSION` environment variable to the -appropriate version, e.g. `${BACKPORT_VERSION}.1`, and over-ride the release -branch by setting the `RELEASE_BRANCH` environment variable to -`stable/${BACKPORT_VERSION}.x`: +origin remote's release branch by running the `release-tag` Make target: ```bash -NEXT_VERSION=${BACKPORT_VERSION}.1 \ -RELEASE_BRANCH=stable/${BACKPORT_VERSION}.x \ -make tag-next-release +make release-tag ``` After the sanity checks have passed, it will ask for confirmation before proceeding. -### Ensure a GitHub release for the bugfix/stable release was published +### Ensure GitHub Release for Bugfix/Stable Release Was Published Similar to a regular release, after the tag with the next release is pushed to the [canonical git repository], the GitHub Actions [Release manager workflow] is diff --git a/towncrier.toml b/towncrier.toml index d58f7331a6b..6d9e7bc4d9c 100644 --- a/towncrier.toml +++ b/towncrier.toml @@ -1,6 +1,11 @@ [tool.towncrier] filename = "CHANGELOG.md" directory = ".changelog" +# Ignore certain files when running towncrier check. +check_ignore_files = [ + # Punch's version file. + ".punch_version.py", +] issue_format = "[#{issue}](https://github.com/oasisprotocol/oasis-core/issues/{issue})" start_string = "\n" # Custom Jinja2 template for preparing a new section of the Change Log.