Files
dsh-slopo/.gitea/scripts/release.test.sh
T
James Mackay e0813bcbea Release on every push to master, and make updating possible
A dsh plugin installed as a bare git URL is pinned to a commit in the profile
lockfile. With no tags in the repository there was nothing for an update to move
to, which is why updating appeared to do nothing and a reinstall was the only way
through.

Gitea action on push to master: work out the range since the last tag, decide
whether the push releases at all, bump package.json, prepend a CHANGELOG section,
commit, tag vX.Y.Z, and publish the release with those notes as the body.

The bump is a patch unless a commit says [minor] or [major]; [skip release]
publishes nothing. With no tags yet the declared version is what gets tagged, so
the first release matches the code. Notes come from commit subjects, grouped under
headings only when the range actually uses conventional prefixes.

The logic is a script rather than inline YAML so it runs without a runner:
npm run test:release covers 31 cases, including an empty range, a repository with
no tags, and a patch number already at 9. The empty-range guard is load-bearing:
the release commit is itself a push to master.

Two bugs the tests caught: an empty note section made '[ -n x ] && printf' return
non-zero, which under set -e aborted the whole release, and a bullet beginning
with '-' was read by grep as an option.

README documents the #semver: spec, which tracks releases rather than every
commit, and the two things that look like a failed update: an unfetchable remote,
and the profile restart client bundle bytes require.
2026-09-25 19:03:50 +01:00

165 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Tests for the release script, in throwaway git repositories.
#
# These paths only occur at moments you cannot stage by hand — a repository with
# no tags, a range that asks for no release, a version whose patch number is
# already two digits — and getting them wrong publishes a wrong version or no
# release at all. Run with: bash .gitea/scripts/release.test.sh
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RELEASE="$HERE/release.sh"
PASS=0
FAIL=0
# A fresh repository with one commit, optionally tagged.
new_repo() {
local dir
dir="$(mktemp -d)"
cd "$dir"
git init -q
git config user.email test@example.com
git config user.name Test
printf '{\n "name": "thing",\n "version": "%s"\n}\n' "${1:-0.1.0}" > package.json
git add -A
git commit -qm "Scaffold the thing"
echo "$dir"
}
commit() {
git commit -q --allow-empty -m "$1"
}
tag() {
git tag "$1"
}
check() {
local label="$1" expected="$2" actual="$3"
if [ "$expected" = "$actual" ]; then
PASS=$((PASS + 1))
printf ' ok %s\n' "$label"
else
FAIL=$((FAIL + 1))
printf ' FAIL %s\n expected: %s\n actual: %s\n' "$label" "$expected" "$actual"
fi
}
check_contains() {
local label="$1" needle="$2" haystack="$3"
if printf '%s' "$haystack" | grep -qF -- "$needle"; then
PASS=$((PASS + 1))
printf ' ok %s\n' "$label"
else
FAIL=$((FAIL + 1))
printf ' FAIL %s\n missing: %s\n in: %s\n' "$label" "$needle" "$haystack"
fi
}
check_absent() {
local label="$1" needle="$2" haystack="$3"
if printf '%s' "$haystack" | grep -qF -- "$needle"; then
FAIL=$((FAIL + 1))
printf ' FAIL %s\n should not contain: %s\n' "$label" "$needle"
else
PASS=$((PASS + 1))
printf ' ok %s\n' "$label"
fi
}
echo "semver arithmetic"
check "patch" "0.1.1" "$(bash "$RELEASE" apply-bump 0.1.0 patch)"
check "minor" "0.2.0" "$(bash "$RELEASE" apply-bump 0.1.5 minor)"
check "major" "1.0.0" "$(bash "$RELEASE" apply-bump 0.9.9 major)"
check "patch to 10" "0.1.10" "$(bash "$RELEASE" apply-bump 0.1.9 patch)"
check "minor to 10" "0.10.0" "$(bash "$RELEASE" apply-bump 0.9.3 minor)"
check "prerelease" "0.1.1" "$(bash "$RELEASE" apply-bump 0.1.0-rc.1 patch)"
echo
echo "first release with no tags"
new_repo 0.2.0 >/dev/null
check "version is the declared one" "0.2.0" "$(bash "$RELEASE" next-version patch)"
check "no tag yet" "" "$(bash "$RELEASE" last-tag)"
cd /;
echo
echo "subsequent releases move forward from the tag"
new_repo 0.1.0 >/dev/null
tag v0.1.3
check "patch" "0.1.4" "$(bash "$RELEASE" next-version patch)"
check "minor" "0.2.0" "$(bash "$RELEASE" next-version minor)"
check "major" "1.0.0" "$(bash "$RELEASE" next-version major)"
commit "Fix the thing"
check "bump defaults to patch" "patch" "$(bash "$RELEASE" bump "v0.1.3..HEAD")"
commit "Add a capability [minor]"
check "marker raises the bump" "minor" "$(bash "$RELEASE" bump "v0.1.3..HEAD")"
check "and the version follows" "0.2.0" "$(bash "$RELEASE" next-version "$(bash "$RELEASE" bump "v0.1.3..HEAD")")"
commit "Rewrite everything [major]"
check "major outranks minor" "major" "$(bash "$RELEASE" bump "v0.1.3..HEAD")"
cd /;
echo
echo "skip release"
new_repo 0.1.0 >/dev/null
tag v0.1.0
commit "Update the README [skip release]"
check "skip is detected" "yes" "$(bash "$RELEASE" wants-skip "v0.1.0..HEAD")"
check_absent "skipped work is not in the notes" "Update the README" "$(bash "$RELEASE" notes "v0.1.0..HEAD")"
commit "Fix a real bug"
check "a later real change does not un-skip" "yes" "$(bash "$RELEASE" wants-skip "v0.1.0..HEAD")"
check "so nothing is released" "no" "$(bash "$RELEASE" should-release "v0.1.0..HEAD")"
cd /;
echo
echo "notes only cover the range"
new_repo 0.1.0 >/dev/null
tag v0.1.0
commit "Fix the leaked handle"
commit "Add the status card"
NOTES="$(bash "$RELEASE" notes "v0.1.0..HEAD")"
check_contains "includes the new work" "Add the status card" "$NOTES"
check_contains "includes the fix" "Fix the leaked handle" "$NOTES"
check_absent "excludes released work" "Scaffold the thing" "$NOTES"
check "a range with real work releases" "yes" "$(bash "$RELEASE" should-release "v0.1.0..HEAD")"
cd /;
echo
echo "conventional commits are grouped, plain ones are not"
new_repo 0.1.0 >/dev/null
tag v0.1.0
commit "feat: add the release workflow"
commit "fix: stop the runner tagging twice"
commit "docs: explain the markers"
NOTES="$(bash "$RELEASE" notes "v0.1.0..HEAD")"
check_contains "grouped heading" "### Added" "$NOTES"
check_contains "grouped fix" "### Fixed" "$NOTES"
check_contains "prefix stripped" "- add the release workflow" "$NOTES"
check_absent "no raw prefix" "feat: add" "$NOTES"
cd /;
echo
new_repo 0.1.0 >/dev/null
tag v0.1.0
commit "Background only the warm-up, and use the framework's own parameter name"
commit "Make the settings card behave like the other cards"
NOTES="$(bash "$RELEASE" notes "v0.1.0..HEAD")"
check_absent "prose commits get no invented headings" "### Added" "$NOTES"
check_contains "and stay as bullets" "- Make the settings card behave" "$NOTES"
cd /;
echo
echo "an empty range says so rather than printing nothing"
new_repo 0.1.0 >/dev/null
tag v0.1.0
check "empty range" "_No changes._" "$(bash "$RELEASE" notes "v0.1.0..HEAD")"
# The release commit is itself a push to master; without this the next run would
# publish a release describing nothing.
check "an empty range releases nothing" "no" "$(bash "$RELEASE" should-release "v0.1.0..HEAD")"
cd /;
echo
printf 'passed %d, failed %d\n' "$PASS" "$FAIL"
[ "$FAIL" -eq 0 ]