Files
DeepSeek-Reasonix/scripts/validate-release-candidate-source.sh
T
SivanCola c45b168a87 Build reusable release candidates for fast recovery
Problem:
Stable releases rebuild, resign, and rerun broad validation after publication failures or unrelated main-v2 changes, making recovery slow and fragile.

Root cause:
Build, signing, acceptance, and publication were coupled to one run. Candidate identity and evidence could not be verified across runs, platform scheduling introduced unrelated waits, and control-plane faults were found after expensive jobs started.

Fix:
Introduce immutable, provenance-bound release candidates; split preparation, promotion, recovery, and public verification; share CLI/npm binaries; decouple Desktop platform scheduling; reuse one Certum session; add a publication ledger; move release-control checks ahead of builds; route CI by affected surface; and document the new workflow.

Verification:
- node --test scripts/*.test.mjs npm/*.test.mjs
- bash scripts/validate-release-control-plane.test.sh
- bash scripts/validate-release-candidate-source.test.sh
- bash scripts/verify-release-push-ci.test.sh
- bash scripts/release-stable.test.sh
- bash scripts/release-workflows.test.sh
- actionlint .github/workflows/*.yml
- go run ./tools/repolint
- go vet ./...
- (cd desktop && go vet ./...)
- go test ./internal/agent ./internal/control ./tools/desktopinventory
- (cd desktop && go test ./...)
2026-09-18 17:00:33 +08:00

44 lines
1.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Validate an untagged Stable candidate without requiring it to remain the tip
# of main-v2 or to be the commit that introduced its reviewed release notes.
set -euo pipefail
if [ "$#" -ne 2 ] || [[ ! "$1" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] || [[ ! "$2" =~ ^[0-9a-f]{40}$ ]]; then
echo "usage: validate-release-candidate-source.sh MAJOR.MINOR.PATCH FULL_COMMIT_SHA" >&2
exit 2
fi
version="$1"
candidate="$2"
remote="${RELEASE_REMOTE:-origin}"
git fetch --quiet "$remote" main-v2 --tags
main_sha="$(git rev-parse "$remote/main-v2^{commit}")"
git cat-file -e "$candidate^{commit}" 2>/dev/null || {
echo "candidate commit is unavailable: $candidate" >&2
exit 1
}
git merge-base --is-ancestor "$candidate" "$main_sha" || {
echo "candidate $candidate is not on main-v2 history at $main_sha" >&2
exit 1
}
for tag in "v$version" "npm-v$version" "desktop-v$version"; do
if git show-ref --verify --quiet "refs/tags/$tag"; then
echo "candidate preparation refuses existing release tag: $tag" >&2
exit 1
fi
done
catalog="$(mktemp "${TMPDIR:-/tmp}/reasonix-candidate-catalog.XXXXXX")"
trap 'rm -f -- "$catalog"' EXIT
git show "$candidate:release-notes/releases.json" >"$catalog"
jq -e --arg version "$version" '
[.releases[] | select(.version == $version and .channel == "stable" and .status == "reviewed")] | length == 1
' "$catalog" >/dev/null || {
echo "candidate must contain exactly one reviewed Stable release record for $version" >&2
exit 1
}
echo "candidate source verified: version=$version sha=$candidate main=$main_sha"