Repository navigation
feat(release): add release management workflows #454
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| name: Create RC | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - 'releases/v*' | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| jobs: | ||
| create-tag: | ||
| runs-on: ubuntu-latest | ||
| # The default pending slot cancels older queued RCs on rapid pushes. | ||
| concurrency: | ||
| group: release-${{ github.ref_name }} | ||
| cancel-in-progress: false | ||
| queue: max | ||
|
|
||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: Checkout | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| # Tag the triggering commit even if the branch advances while queued. | ||
| ref: ${{ github.sha }} | ||
| fetch-depth: 0 | ||
| fetch-tags: true | ||
|
|
||
| - name: Create or reuse RC tag | ||
| env: | ||
| RELEASE_BRANCH: ${{ github.ref_name }} | ||
| run: ./scripts/release/create-tag.sh "$RELEASE_BRANCH" rc | ||
|
|
||
| - name: Summary | ||
| run: | | ||
| TAG=$(cat "$RUNNER_TEMP/release_tag") | ||
| { | ||
| echo "## RC tag ready" | ||
| echo "" | ||
| echo "**Tag**: \`${TAG}\`" | ||
| } >> "$GITHUB_STEP_SUMMARY" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| name: Publish Release | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| version: | ||
| description: Version to release (X.Y.Z, without a v prefix) | ||
| required: true | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| concurrency: | ||
| group: publish-release-${{ inputs.version }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| publish: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: Checkout release tools | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| ref: main | ||
| fetch-depth: 0 | ||
| fetch-tags: true | ||
|
|
||
| - name: Preserve release tools | ||
| # Existing release branches may not contain the release scripts. | ||
| run: cp -R scripts/release "$RUNNER_TEMP/release-scripts" | ||
|
|
||
| - name: Validate and checkout release branch | ||
| env: | ||
| VERSION: ${{ inputs.version }} | ||
| run: | | ||
| if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | ||
| echo "::error::Invalid version format: $VERSION (expected X.Y.Z)" | ||
| exit 1 | ||
| fi | ||
| RELEASE_BRANCH="releases/v${VERSION}" | ||
| git fetch origin "refs/heads/$RELEASE_BRANCH" || { | ||
| echo "::error::Branch $RELEASE_BRANCH not found. Run Start Release first." | ||
| exit 1 | ||
| } | ||
| git checkout --detach FETCH_HEAD | ||
|
|
||
| - name: Create or reuse final tag | ||
| env: | ||
| VERSION: ${{ inputs.version }} | ||
| run: bash "$RUNNER_TEMP/release-scripts/create-tag.sh" "releases/v${VERSION}" final | ||
|
|
||
| - name: Create or reuse draft release | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| GH_REPO: ${{ github.repository }} | ||
| run: | | ||
| TAG=$(cat "$RUNNER_TEMP/release_tag") | ||
| if gh release view "$TAG" > /dev/null 2>&1; then | ||
| echo "Release $TAG already exists; reusing it." | ||
| else | ||
| gh release create "$TAG" --draft --generate-notes --title "$TAG" --verify-tag | ||
| fi | ||
|
|
||
| - name: Summary | ||
| run: | | ||
| TAG=$(cat "$RUNNER_TEMP/release_tag") | ||
| { | ||
| echo "## Release ready" | ||
| echo "" | ||
| echo "**Tag**: \`${TAG}\`" | ||
| echo "" | ||
| echo "Review the generated notes and publish the draft on GitHub. An existing release is left unchanged." | ||
| } >> "$GITHUB_STEP_SUMMARY" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| name: Release Branch CI | ||
|
|
||
| on: | ||
| pull_request: | ||
| # `edited` covers retargeting the PR to another base branch. | ||
| types: [opened, synchronize, reopened, edited] | ||
| branches: | ||
| - 'releases/v*' | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.base_ref }}-${{ github.head_ref || github.run_id }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| finalized-release: | ||
| name: Finalized Release | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: Checkout release tools | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| ref: main | ||
| fetch-depth: 0 | ||
| fetch-tags: true | ||
| persist-credentials: false | ||
|
|
||
| - name: Check release is not finalized | ||
| env: | ||
| RELEASE_BRANCH: ${{ github.base_ref }} | ||
| run: ./scripts/release/check-finalized.sh "$RELEASE_BRANCH" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| name: Release Tests | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - 'scripts/release/**' | ||
| - '.github/workflows/*release*.yml' | ||
| - '.github/workflows/create-rc.yml' | ||
| - 'justfile' | ||
| push: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - 'scripts/release/**' | ||
| - '.github/workflows/*release*.yml' | ||
| - '.github/workflows/create-rc.yml' | ||
| - 'justfile' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: Checkout | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Test release scripts | ||
| run: ./scripts/release/test-release.sh | ||
|
|
||
| - name: Check release shell scripts | ||
| run: shellcheck scripts/release/*.sh |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| name: Start Release | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| bump_type: | ||
| description: Version bump type | ||
| required: true | ||
| type: choice | ||
| options: | ||
| - minor | ||
| - patch | ||
| - major | ||
| default: minor | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| concurrency: | ||
| group: start-release | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| start: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Harden the runner (Audit all outbound calls) | ||
| uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | ||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: Checkout release tools | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| ref: main | ||
| fetch-depth: 0 | ||
| fetch-tags: true | ||
|
|
||
| - name: Preserve release tools | ||
| # Patch bases can predate the release scripts. | ||
| run: cp -R scripts/release "$RUNNER_TEMP/release-scripts" | ||
|
|
||
| - name: Create release branch and first RC | ||
| env: | ||
| BUMP_TYPE: ${{ inputs.bump_type }} | ||
| run: bash "$RUNNER_TEMP/release-scripts/start-release.sh" "$BUMP_TYPE" | ||
|
|
||
| - name: Summary | ||
| run: | | ||
| CURRENT=$(cat "$RUNNER_TEMP/current_version") | ||
| NEW=$(cat "$RUNNER_TEMP/new_version") | ||
| BRANCH=$(cat "$RUNNER_TEMP/release_branch") | ||
| TAG=$(cat "$RUNNER_TEMP/release_tag") | ||
| { | ||
| echo "## Release branch created" | ||
| echo "" | ||
| echo "**Version**: v${CURRENT} to v${NEW}" | ||
| echo "**Branch**: \`${BRANCH}\`" | ||
| echo "**First RC**: \`${TAG}\`" | ||
| echo "" | ||
| echo "Next: test the RC, then run **Publish Release**." | ||
| } >> "$GITHUB_STEP_SUMMARY" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| # Release process | ||
|
|
||
| Release versions come from branch names, not a repository version file. Release | ||
| management creates branches, tags, and draft GitHub releases. It does not build | ||
| artifacts or change individual contracts' versions or semver snapshots. | ||
|
|
||
| ## Start a release | ||
|
|
||
| Run **Start Release** in GitHub Actions from `main` and select a bump type: | ||
|
|
||
| - `minor` is the default. It increments the minor version and resets the patch. | ||
| - `patch` increments the patch version. | ||
| - `major` increments the major version and resets the minor and patch. | ||
|
|
||
| The workflow uses the highest final `vX.Y.Z` tag by version order. RC tags of | ||
| both formats are ignored. For example, `v8.10.0` comes after `v8.9.0`, even if a | ||
| `v9.0.0-rc.1` tag also exists. | ||
|
|
||
| Major and minor releases branch from `main`. Patch releases branch from the | ||
| highest existing `releases/vX.Y.*` branch for the latest final tag's major and | ||
| minor version. Starting a release fails if the new branch already exists or if | ||
| a patch release has no matching base branch. | ||
|
|
||
| Start Release pushes `releases/vX.Y.Z` and an annotated first RC tag at its head | ||
| atomically. Both refs become available together, before subsequent Create RC jobs | ||
| can allocate a tag. Branch pushes made with `GITHUB_TOKEN` do not trigger other | ||
| workflows, so Start Release cannot rely on Create RC to make that first tag. | ||
| There is no version-sync step. | ||
|
|
||
| ## Create release candidates | ||
|
|
||
| Every push to a `releases/v*` branch containing the automation runs **Create RC**. | ||
| It tags the triggering commit, even if newer commits arrive while it waits. | ||
| New tags use `vX.Y.Z-rc.N`. | ||
|
|
||
| Legacy tags use `vX.Y.Z.rcN`. Both formats reserve RC numbers for their exact | ||
| version. For example, with `v8.4.0.rc1` and `v8.4.0.rc2` present, the next RC is | ||
| `v8.4.0-rc.3`. Numbering is numeric, so `rc.10` follows `rc.9`. Tags for other | ||
| versions do not affect the count. | ||
|
|
||
| Subsequent RC tag allocation is serialized per release branch with | ||
| `cancel-in-progress: false` | ||
| and `queue: max`. GitHub allows up to 100 pending jobs in that queue. A full | ||
| queue cancels additional jobs. Jobs enter the queue when they start waiting, | ||
| which need not match commit order. Each job still tags its own triggering commit. | ||
|
|
||
| ### Retries | ||
|
|
||
| Rerun a failed Create RC workflow. If a new-format RC tag for that version already | ||
| points at the triggering commit, the script reuses the highest such tag instead | ||
| of allocating another number. Legacy tags at that commit count toward numbering | ||
| but are not reused. | ||
|
|
||
| Start Release refuses to reuse an existing release branch. Its atomic push | ||
| publishes neither the branch nor the first RC if either ref is rejected. If the | ||
| workflow fails after the push succeeds, verify the branch and tag rather than | ||
| rerunning Start Release. Do not move an existing tag to repair a failed run. | ||
|
|
||
| ## Publish the final release | ||
|
|
||
| Run **Publish Release** from `main` and enter `X.Y.Z`, without a `v` or `releases/` | ||
| prefix. The workflow: | ||
|
|
||
| - Validates the version and checks that `releases/vX.Y.Z` exists. | ||
| - Checks out the release branch head and creates `vX.Y.Z` there. | ||
| - Creates a draft GitHub release with GitHub-generated "What's Changed" notes. | ||
|
|
||
| Review the notes and publish the draft on GitHub. There are no Docker images, | ||
| binaries, or other release assets to wait for. | ||
|
|
||
| Publishing is serialized per version. If the final tag already points at the | ||
| release branch head, a retry reuses it. A final tag at a different commit causes | ||
| a failure; the workflow never moves it. If a GitHub release already exists, | ||
| the workflow leaves its notes and draft or published state unchanged. | ||
|
|
||
| When a pull request is opened, reopened, updated, or edited (including | ||
| retargeting its base branch), **Release Branch CI** checks the repository's tags using | ||
| tools from `main`. Its Finalized Release check fails for a `releases/vX.Y.Z` | ||
| target once `vX.Y.Z` exists, even if the GitHub release is still a draft. Keep | ||
| this check required in branch protection. Creating the final tag does not rerun | ||
| checks on open pull requests, so an earlier passing check can stay green. After | ||
| finalizing, rerun checks on open pull requests into that branch or lock the | ||
| branch via branch protection to prevent merges. The check does not prevent | ||
| direct pushes. | ||
|
|
||
| ## Existing release branches | ||
|
|
||
| Push-triggered workflows only run on release branches that contain their | ||
| workflow files. Updating `main` does not install automation on existing branches. | ||
| Branches such as `releases/v8.4.0` keep the manual release process unless the | ||
| release workflows and scripts are backported. Patch branches created from an | ||
| older release branch inherit that limitation, although Start Release still | ||
| creates their initial RC using the tools from `main`. | ||
|
|
||
| To adopt automatic RCs and the finalized-release check on an active branch, | ||
| backport the release workflows and `scripts/release/`. Existing queued or running | ||
| workflows keep the configuration they started with. Release Branch CI loads its | ||
| tools from `main`, so the backport can pass even when the target branch has no | ||
| release scripts. Publish Release also uses tools from `main` and can tag an | ||
| existing branch without that backport. | ||
|
|
||
| ## Quick reference | ||
|
|
||
| | Action | Workflow | Trigger | Output | | ||
| |--------|----------|---------|--------| | ||
| | Start a release | Start Release | Manual bump type | Release branch and first RC tag | | ||
| | Create an RC | Create RC | Push to `releases/v*` | RC tag at the triggering commit | | ||
| | Finalize a release | Publish Release | Manual version | Final tag and draft GitHub release | | ||
| | Guard a finalized branch | Release Branch CI | Pull request to `releases/v*` | Fails if the final tag exists | | ||
|
|
||
| Run `just test-release` to test version selection, legacy RC numbering, retry | ||
| behavior, atomic branch and first-RC publication, and final-tag protection using | ||
| temporary local repositories without network access. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Please add
types: [opened, synchronize, reopened, edited]: retargeting a PR from an unfinalised release branch to a finalised one otherwise leaves the previous passing check on the unchanged head SHA without rerunning this guard.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in fbaa6a5. Release Branch CI now runs on
types: [opened, synchronize, reopened, edited], so retargeting a PR to a finalized release branch reruns the Finalized Release check.docs/RELEASE.mdlists the events.🤖 Reply generated with Toshi