Refresh documentation and policies and simplify releases
This commit is contained in:
48
scripts/release.sh
Executable file
48
scripts/release.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
fail() { echo "Release stopped: $*" >&2; exit 1; }
|
||||
if [[ $# != 2 || ( $1 != --check && $1 != --publish ) || ! $2 =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then
|
||||
echo 'Usage: scripts/release.sh --check|--publish vMAJOR.MINOR.PATCH' >&2
|
||||
exit 2
|
||||
fi
|
||||
mode=$1
|
||||
version=$2
|
||||
cd "$(dirname "$0")/.."
|
||||
[[ $(git symbolic-ref --quiet --short HEAD) == main ]] || fail 'switch to main'
|
||||
[[ -z $(git status --porcelain) ]] || fail 'commit or remove outstanding changes first'
|
||||
candidate=$(git rev-parse HEAD)
|
||||
note="docs/releases/$version.md"
|
||||
[[ -s $note ]] || fail "missing release note: $note"
|
||||
IFS= read -r title < "$note" || true
|
||||
[[ $title == "# Audita $version" ]] || fail "note must begin with # Audita $version"
|
||||
git show-ref --verify --quiet "refs/tags/$version" && fail 'version already exists locally'
|
||||
|
||||
# Check mode is local and never contacts a remote or creates a tag.
|
||||
if [[ $mode == --publish ]]; then
|
||||
existing=$(git ls-remote --tags origin "refs/tags/$version")
|
||||
[[ -z $existing ]] || fail 'version already exists on origin'
|
||||
fi
|
||||
build_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$build_dir"' EXIT
|
||||
GOWORK=off go test -mod=readonly ./...
|
||||
bash scripts/build-release.sh "$version" "$build_dir"
|
||||
[[ $(git rev-parse HEAD) == "$candidate" ]] || fail 'HEAD changed during validation'
|
||||
[[ -z $(git status --porcelain) ]] || fail 'worktree changed during validation'
|
||||
[[ $(git symbolic-ref --quiet --short HEAD) == main ]] || fail 'branch changed during validation'
|
||||
if [[ $mode == --check ]]; then
|
||||
echo "Validated $version at $candidate; nothing published."
|
||||
exit 0
|
||||
fi
|
||||
# Fetch the authoritative branch before checking that this is a forward update.
|
||||
git fetch --no-tags origin refs/heads/main
|
||||
git merge-base --is-ancestor FETCH_HEAD "$candidate" || fail 'candidate does not include origin/main'
|
||||
existing=$(git ls-remote --tags origin "refs/tags/$version")
|
||||
[[ -z $existing ]] || fail 'version already exists on origin'
|
||||
git tag "$version" "$candidate"
|
||||
[[ $(git rev-parse "refs/tags/$version") == "$candidate" ]] || fail 'tag target changed'
|
||||
# Atomic publication prevents a rejected tag/branch update from publishing only half.
|
||||
if ! git push --atomic origin "$candidate:refs/heads/main" "refs/tags/$version:refs/tags/$version"; then
|
||||
fail "push failed or its outcome is uncertain; inspect origin/main and refs/tags/$version before retrying (local tag retained)"
|
||||
fi
|
||||
echo "Released $version at $candidate. Binary publication runs asynchronously; CI is not awaited."
|
||||
Reference in New Issue
Block a user