Files
sanderling/.github/scripts/next-version.sh
T
pj a08d8b262c feat(ci): report the release a version follows
The manual pipeline promotes the commit that release was cut from, so it
needs the tag as well as the next version.

Claude-Session: https://claude.ai/code/session_01ShuAy8q8ZfPi8KHxwc8JpQ
2026-08-16 14:52:56 +05:30

70 lines
2.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Resolves the version a release is cutting. The tags this repo carries are the
# record of what has been released, so the version is counted off them and
# nothing in the tree holds it: no commit has to land on master to advance a
# version, and a release cannot disagree with a package.json someone edited.
#
# BUMP is major, minor or patch. VERSION overrides it with a version named
# outright. Writes `version`, `tag` and `released_tag` to $GITHUB_OUTPUT when it
# is set; `released_tag` is the release this one follows, and is empty in a
# repository that has never cut one.
set -euo pipefail
bump="${BUMP:-patch}"
named="${VERSION:-}"
semver='^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$'
# Only a stable tag counts as a release. v0.0.1-rc4 is a candidate for 0.0.1, so
# counting a patch off it would skip the very version it was a candidate for.
# `sort -V` puts 0.10.0 above 0.9.0, which a lexical sort does not, and
# `sed -n p` reports no matches as an empty line rather than as the failure
# `grep` would return under pipefail.
released="$(git tag -l 'v*' \
| sed -n 's/^v\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)$/\1/p' \
| sort -V \
| tail -1)"
released_tag=""
[ -n "$released" ] && released_tag="v$released"
if [ -n "$named" ]; then
if [[ ! "$named" =~ $semver ]]; then
echo "next-version: '$named' is not a version this releases" >&2
echo "next-version: a version is MAJOR.MINOR.PATCH with an optional -prerelease, e.g. 0.1.0 or 1.0.0-rc1" >&2
exit 1
fi
version="$named"
from="named outright"
else
base="${released:-0.0.0}"
from="a $bump off ${base}"
IFS=. read -r major minor patch <<<"$base"
case "$bump" in
major) version="$((major + 1)).0.0" ;;
minor) version="$major.$((minor + 1)).0" ;;
patch) version="$major.$minor.$((patch + 1))" ;;
*)
echo "next-version: '$bump' is not a bump; use major, minor or patch" >&2
exit 1
;;
esac
fi
tag="v$version"
# A tag that is already there means this version was already cut. Moving it
# would relabel a release that people have installed.
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
echo "next-version: $tag is already tagged, so there is nothing to release at $version" >&2
exit 1
fi
echo "next-version: releasing $version, $from"
if [ -n "${GITHUB_OUTPUT:-}" ]; then
{
echo "version=$version"
echo "tag=$tag"
echo "released_tag=$released_tag"
} >> "$GITHUB_OUTPUT"
fi