Release / release (push) Successful in 5s
Tags are what make `dsh plugin update` work at all: a plugin installed as a bare `git+https://…/repo.git` is pinned to a commit in the profile's lockfile, so with no tags there is nothing for an update to move to. This adds the pipeline that creates them. `.gitea/workflows/release.yml` runs on a push to master and on manual dispatch: it reads the commit subjects since the last tag, derives the bump, prepends a `CHANGELOG.md` section, commits and tags `vX.Y.Z`, and publishes the Gitea release with those subjects as the body. The range with no tags yet is the whole history, so the first release describes everything it contains. The logic lives in `.gitea/scripts/release.sh` rather than in the YAML so it can run — and be checked — without a runner: `npm run test:release` exercises it (43 checks), `npm run release:plan` says whether HEAD would release, and `npm run release:notes` prints the notes it would publish. `AGENTS.md` states the subject conventions a contributor has to follow, because the subject is both the version decision and the sentence a user reads in the release notes. One property worth knowing: with no tags yet the version released is the one `package.json` already declares — no bump is applied to a version that has never been published — so the first tag is `v0.1.0`, and the bump rules take effect from the second release onward.
200 lines
7.0 KiB
Bash
Executable File
200 lines
7.0 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 "the subject decides the bump"
|
|
new_repo 0.1.0 >/dev/null
|
|
tag v0.1.0
|
|
check "prose is a patch" "patch" "$(bash "$RELEASE" bump "v0.1.0..HEAD")"
|
|
commit "feat: add the recall index"
|
|
check "feat is a minor" "minor" "$(bash "$RELEASE" bump "v0.1.0..HEAD")"
|
|
check "so 0.1 -> 0.2" "0.2.0" "$(bash "$RELEASE" next-version "$(bash "$RELEASE" bump "v0.1.0..HEAD")")"
|
|
cd /;
|
|
|
|
new_repo 0.1.0 >/dev/null
|
|
tag v0.1.0
|
|
commit "feat(scope): add the scope filter"
|
|
check "a scoped feat counts" "minor" "$(bash "$RELEASE" bump "v0.1.0..HEAD")"
|
|
cd /;
|
|
|
|
new_repo 0.1.0 >/dev/null
|
|
tag v0.1.0
|
|
commit "Fix the feature flag leak"
|
|
check "feat inside a word is not a feature" "no" "$(bash "$RELEASE" is-feature "Fix the feature flag leak")"
|
|
check "so it stays a patch" "patch" "$(bash "$RELEASE" bump "v0.1.0..HEAD")"
|
|
cd /;
|
|
|
|
new_repo 0.1.0 >/dev/null
|
|
tag v0.1.0
|
|
commit "feat!: move the index to sqlite"
|
|
check "a bang is breaking" "yes" "$(bash "$RELEASE" is-breaking "feat!: move the index to sqlite")"
|
|
check "so it is a major" "major" "$(bash "$RELEASE" bump "v0.1.0..HEAD")"
|
|
check "and 0.1 -> 1.0" "1.0.0" "$(bash "$RELEASE" next-version "$(bash "$RELEASE" bump "v0.1.0..HEAD")")"
|
|
NOTES="$(bash "$RELEASE" notes "v0.1.0..HEAD")"
|
|
check_contains "breaking gets a section" "### Breaking" "$NOTES"
|
|
check_contains "and is listed there" "- move the index to sqlite" "$NOTES"
|
|
check_absent "not under Added" "### Added" "$NOTES"
|
|
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 ]
|