Files
DeepSeek-Reasonix/scripts/resolve-cli-release.sh
T
SivanCola ab24371d98 fix(release): bind stable tags to reviewed candidate
Problem:
The stable helper could consume immutable version tags if main-v2 advanced while exact-SHA CI or the protected relay was starting. Manual CLI and Desktop recovery also still accepted RC tags despite the official-only release contract.

Root cause:
The helper sampled main-v2 once, pushed only tag refs, and the orchestrator required the tags to equal the later branch tip. The candidate was not proven to be the commit that changed the target reviewed Notes record.

Fix:
Bind normal candidates to their reviewed Notes change, revalidate exact push CI from the protected control plane, include main-v2 as a guarded ref in the atomic tag transaction, and accept the immutable candidate after later protected branch advances. Restrict manual CLI and Desktop recovery to official versions while retaining historical reusable-workflow parsing.

Verification:
- bash scripts/release-stable.test.sh
- bash scripts/release-workflows.test.sh
- node --test scripts/release-notes.test.mjs
- actionlint on all active release workflows
- site contract tests, SignPath contract tests, Desktop control-plane test, cache guard, and git diff --check
2026-08-03 01:05:09 +08:00

89 lines
2.6 KiB
Bash

#!/usr/bin/env bash
# Resolve and validate one native CLI release tag. Manual recovery exposes only
# official versions; prerelease parsing remains for historical workflow calls.
set -euo pipefail
stable_semver_re='(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)'
release_semver_re="^v${stable_semver_re}(-([0-9A-Za-z-]+)(\.[0-9A-Za-z-]+)*)?)?$"
preview_semver_re="^v${stable_semver_re}-preview\.(0|[1-9][0-9]*)$"
if [ "${IN_ORCHESTRATED:-false}" = "true" ]; then
tag="${IN_TAG:?orchestrated CLI release requires tag}"
else
if [ "${EVENT_NAME:-}" = "workflow_dispatch" ]; then
if [ "${CALLER_REF:-}" != "refs/heads/main-v2" ] || [ "${CALLER_REF_PROTECTED:-}" != "true" ]; then
echo "::error::manual CLI releases must run from protected main-v2" >&2
exit 1
fi
tag="${IN_TAG:?manual CLI release requires an existing protected tag}"
else
tag="${REF_NAME:?CLI tag push requires ref name}"
fi
fi
if [[ ! "$tag" =~ $release_semver_re ]]; then
echo "::error::CLI release tag must be vMAJOR.MINOR.PATCH[-PRERELEASE], got: $tag" >&2
exit 1
fi
base_version="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}"
version="${tag#v}"
case "$version" in
*-*) prerelease="true" ;;
*) prerelease="false" ;;
esac
if [ "${EVENT_NAME:-}" = "workflow_dispatch" ] && \
[ "${IN_ORCHESTRATED:-false}" != "true" ] && [ "$prerelease" = "true" ]; then
echo "::error::manual CLI recovery accepts only vMAJOR.MINOR.PATCH" >&2
exit 1
fi
if [[ "$tag" =~ $preview_semver_re ]]; then
channel="preview"
notes_version="$tag"
else
case "$tag" in
v*-preview*)
echo "::error::CLI Preview tag must be vMAJOR.MINOR.PATCH-preview.N, got: $tag" >&2
exit 1
;;
esac
channel="stable"
case "$version" in
*-*) notes_version="v${base_version}" ;;
*) notes_version="$tag" ;;
esac
fi
requested_channel="${IN_CHANNEL:-}"
if [ -n "$requested_channel" ]; then
case "$requested_channel" in
stable | preview) ;;
*)
echo "::error::CLI release channel must be stable or preview, got: $requested_channel" >&2
exit 1
;;
esac
if [ "$requested_channel" != "$channel" ]; then
echo "::error::CLI tag $tag belongs to $channel, not requested channel $requested_channel" >&2
exit 1
fi
fi
if [ "$channel" = "preview" ] && [ "${IN_ORCHESTRATED:-false}" != "true" ]; then
echo "::error::public CLI Preview releases must be dispatched by release-preview.yml" >&2
exit 1
fi
{
echo "tag=$tag"
echo "version=$version"
echo "base_version=$base_version"
echo "notes_version=$notes_version"
echo "channel=$channel"
echo "prerelease=$prerelease"
} >>"${GITHUB_OUTPUT:-/dev/stdout}"
echo "CLI release resolved: tag=$tag channel=$channel prerelease=$prerelease"