#!/bin/sh # Validate one committed candidate and publish only its selected annotated tag. set -eu cd "$(dirname "$0")/.." release_version=${1:?usage: scripts/release.sh vMAJOR.MINOR.PATCH} printf '%s\n' "$release_version" | grep -Eq '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' release_commit=$(git rev-parse --verify 'HEAD^{commit}') check_candidate() { test "$(git branch --show-current)" = main test -z "$(git status --porcelain)" test "$(git rev-parse HEAD)" = "$release_commit" git fetch origin main test "$(git rev-parse refs/remotes/origin/main)" = "$release_commit" if git show-ref --verify --quiet "refs/tags/$release_version"; then printf '%s\n' "local tag already exists: $release_version" >&2 exit 1 fi remote_refs=$(git ls-remote --tags origin "refs/tags/$release_version" "refs/tags/$release_version^{}") test -z "$remote_refs" } check_candidate python3 - "$release_version" <<'PY' from pathlib import Path import sys version = sys.argv[1] note = Path("docs/releases") / (version + ".md") lines = note.read_text(encoding="utf-8").splitlines() assert lines and lines[0] == "# Promptkit " + version, f"wrong release title: {note}" headings = set() fence = None for line in lines: marker = line.lstrip()[:3] if marker in {"```", "~~~"}: if fence is None: fence = marker elif fence == marker: fence = None elif fence is None: headings.add(line) for section in ("Summary", "Compatibility", "Upgrade", "Changes"): assert "## " + section in headings, f"missing {section}: {note}" PY ./scripts/release-check.sh check_candidate release_note=$(git show "$release_commit:docs/releases/$release_version.md") printf '%s\n\nValidated commit: %s\n' "$release_note" "$release_commit" | git tag --annotate --cleanup=verbatim "$release_version" --file - "$release_commit" test "$(git cat-file -t "refs/tags/$release_version")" = tag test "$(git rev-parse "refs/tags/$release_version^{commit}")" = "$release_commit" git push origin "refs/tags/$release_version:refs/tags/$release_version" printf '%s\n' "Released $release_version. No CI or module-availability wait is required."