feat(release): add release script for version bumping and tagging.

This commit is contained in:
Miroslav Šedivý
2026-10-15 23:57:27 +02:00
parent 55df495389
commit 2d2e11502d
2 changed files with 119 additions and 0 deletions
+9
View File
@@ -6,11 +6,20 @@ on:
- master - master
paths: paths:
- client/** - client/**
- utils/release.sh
- .github/workflows/client_build.yml - .github/workflows/client_build.yml
- .github/workflows/client_test.yml - .github/workflows/client_test.yml
- .github/workflows/npm_publish.yml - .github/workflows/npm_publish.yml
jobs: jobs:
check-release-helper:
name: Check Release Helper
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check Bash syntax
run: bash -n utils/release.sh
test-client: test-client:
name: Test Client name: Test Client
uses: ./.github/workflows/client_build.yml uses: ./.github/workflows/client_build.yml
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: utils/release.sh <major|minor|patch> [--push]
Bumps the client and core package versions together, commits the release, and
creates an annotated vX.Y.Z tag. The tag is shared by the backend and client
release workflows. By default, prints the command to push the branch and tag.
Pass --push to push both atomically to origin.
EOF
}
if [[ ${1:-} == --help || ${1:-} == -h ]]; then
usage
exit 0
fi
if [[ $# -lt 1 || $# -gt 2 ]]; then
usage >&2
exit 2
fi
bump=$1
case "$bump" in
major|minor|patch) ;;
*)
usage >&2
exit 2
;;
esac
push=0
if [[ $# -eq 2 ]]; then
if [[ $2 != --push ]]; then
usage >&2
exit 2
fi
push=1
fi
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
root=$(git -C "$script_dir" rev-parse --show-toplevel 2>/dev/null) || {
printf 'Error: run this script from inside a git repository.\n' >&2
exit 1
}
cd "$root"
if [[ -n $(git status --porcelain) ]]; then
printf 'Error: working tree must be clean before releasing.\n' >&2
exit 1
fi
branch=$(git branch --show-current)
if [[ -z $branch ]]; then
printf 'Error: release from a branch, not a detached HEAD.\n' >&2
exit 1
fi
if [[ $push -eq 1 ]] && ! git remote get-url origin >/dev/null 2>&1; then
printf 'Error: remote "origin" is not configured.\n' >&2
exit 1
fi
client_version=$(node -p "require('./client/package.json').version")
core_version=$(node -p "require('./client/core/package.json').version")
if [[ $client_version != "$core_version" ]]; then
printf 'Error: client version (%s) and core version (%s) differ.\n' "$client_version" "$core_version" >&2
exit 1
fi
next_version=$(node - "$client_version" "$bump" <<'NODE'
const [, , version, bump] = process.argv
const match = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/.exec(version)
if (!match) throw new Error(`Expected a stable semver version, got ${version}`)
let [major, minor, patch] = match.slice(1).map(BigInt)
if (bump === 'major') [major, minor, patch] = [major + 1n, 0n, 0n]
if (bump === 'minor') [minor, patch] = [minor + 1n, 0n]
if (bump === 'patch') patch += 1n
console.log(`${major}.${minor}.${patch}`)
NODE
)
tag="v$next_version"
if git rev-parse --verify --quiet "refs/tags/$tag" >/dev/null; then
printf 'Error: tag %s already exists locally.\n' "$tag" >&2
exit 1
fi
(cd "$root/client" && npm version "$next_version" --no-git-tag-version)
(cd "$root/client/core" && npm version "$next_version" --no-git-tag-version)
git add -- \
client/package.json \
client/package-lock.json \
client/core/package.json \
client/core/package-lock.json
git commit -m "release: $tag"
git tag --annotate "$tag" --message "Release $tag"
push_command=(git push --atomic origin "HEAD:refs/heads/$branch" "refs/tags/$tag")
if [[ $push -eq 1 ]]; then
"${push_command[@]}"
else
printf '\nRelease %s created locally. To push the release commit and tag, run:\n ' "$tag"
printf '%q ' "${push_command[@]}"
printf '\n'
fi