Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 66 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# Publishes a release when a version tag is pushed.
# Cuts a release from the Actions tab.
#
# The notes are generated from the commits and pull requests since the last
# tag, so a release describes what actually changed rather than what someone
# remembered to write down.
# Actions -> Release -> Run workflow, choose major, minor or patch. The
# workflow works out the next version from the last tag, runs both test
# suites, tags the commit and publishes. Nothing is tagged by hand.
name: Release

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
bump:
description: Which part of the version to increase
type: choice
options:
- patch
- minor
- major
default: patch

permissions:
contents: write
Expand All @@ -19,8 +26,16 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
# Tags and full history: the next version comes from the last tag,
# and the notes come from the commits since it.
fetch-depth: 0

- name: Refuse to release from anywhere but main
if: github.ref_name != 'main'
run: |
echo "::error::Releases are cut from main, not ${{ github.ref_name }}."
exit 1

- uses: actions/setup-java@v4
with:
distribution: temurin
Expand All @@ -41,13 +56,53 @@ jobs:
- run: npm test
working-directory: web

# --generate-notes works out the previous tag on its own, and copes
# with there not being one.
- name: Work out the next version
id: version
run: |
set -euo pipefail
previous=$(git tag -l 'v*' --sort=-v:refname | head -1)

if [ -z "$previous" ]; then
next="v0.1.0"
else
IFS=. read -r major minor patch <<< "${previous#v}"
case "${{ inputs.bump }}" in
major) major=$((major + 1)); minor=0; patch=0 ;;
minor) minor=$((minor + 1)); patch=0 ;;
patch) patch=$((patch + 1)) ;;
esac
next="v${major}.${minor}.${patch}"
fi

if git rev-parse -q --verify "refs/tags/$next" >/dev/null; then
echo "::error::$next already exists."
exit 1
fi

echo "next=$next" >> "$GITHUB_OUTPUT"
echo "previous=${previous:-none}" >> "$GITHUB_OUTPUT"
echo "### $next" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "A ${{ inputs.bump }} release, up from ${previous:-no previous tag}." \
>> "$GITHUB_STEP_SUMMARY"

- name: Tag it
env:
NEXT: ${{ steps.version.outputs.next }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$NEXT" -m "$NEXT"
git push origin "$NEXT"

- name: Publish
env:
GH_TOKEN: ${{ github.token }}
NEXT: ${{ steps.version.outputs.next }}
run: |
gh release create "$GITHUB_REF_NAME" \
--title "$GITHUB_REF_NAME" \
set -euo pipefail
gh release create "$NEXT" \
--title "$NEXT" \
--generate-notes \
--verify-tag
19 changes: 12 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,19 @@ easier to argue for than changes that add controls.

## Releasing

Tag a commit on `main` and push the tag. Everything else is automatic:
Go to **Actions → Release → Run workflow** and pick which part of the version to
increase. `patch` is the default.

```bash
git tag -a v2.1.0 -m "v2.1.0"
git push origin v2.1.0
```
| Choice | v2.4.1 becomes |
|---|---|
| `patch` | v2.4.2 |
| `minor` | v2.5.0 |
| `major` | v3.0.0 |

The workflow reads the last tag, works out the next version, runs both test
suites, tags the commit and publishes. It refuses to run from anywhere but
`main`, and refuses to reuse a version that already exists.

The release workflow runs both test suites, and publishes only if they pass.
The notes are generated from the commits and pull requests since the previous
tag, so they describe what actually changed rather than what someone remembered
to write down.
to write down. There is no need to tag anything by hand.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,12 @@ their children without overlapping anything.

## Releasing

Push a tag on `main` — `git tag -a v2.1.0 -m "v2.1.0" && git push origin v2.1.0`.
The workflow runs both suites and publishes only if they pass, with notes
generated from the commits since the previous tag.
**Actions → Release → Run workflow**, and choose `patch`, `minor` or `major`.

The workflow works out the next version from the last tag, runs both test
suites, tags the commit and publishes — with notes generated from the commits
since the previous tag. Nothing is tagged by hand, and nothing is released that
does not pass.

## License

Expand Down
Loading