fix(staging): let migrate accept docs-only-ahead like deploy
Docs-only staging HEAD blocked Migrate Staging Database even when an ancestor already had a successful exact-SHA gate, so pending SQL could not be applied. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
deploy_sha:
|
||||
description: Full current staging SHA to migrate
|
||||
description: Exact tested 40-character staging commit SHA
|
||||
required: true
|
||||
type: string
|
||||
|
||||
@@ -52,9 +52,7 @@ jobs:
|
||||
select(test("^[0-9a-f]{40}$"))
|
||||
'
|
||||
}
|
||||
staging_head="$(read_ref_sha staging)"
|
||||
[[ "$staging_head" == "$DEPLOY_SHA" ]] || { echo "migration requires current staging head" >&2; exit 1; }
|
||||
runs="$(curl --fail --silent --show-error \
|
||||
runs="$(curl --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-all-errors \
|
||||
--header "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API_URL/repos/$GITEA_REPOSITORY/actions/runs?head_sha=$DEPLOY_SHA&branch=staging&event=push&status=success&limit=100")"
|
||||
selected_run="$(jq -cer --arg sha "$DEPLOY_SHA" '
|
||||
@@ -65,10 +63,23 @@ jobs:
|
||||
)] | sort_by(.id) | reverse | first
|
||||
' <<<"$runs")"
|
||||
gate_run_id="$(jq -er '.id' <<<"$selected_run")"
|
||||
[[ "$gate_run_id" =~ ^[0-9]+$ ]]
|
||||
[[ "$gate_run_id" =~ ^[0-9]+$ ]] || { echo "no successful exact-SHA staging quality gate run found" >&2; exit 1; }
|
||||
staging_head="$(read_ref_sha staging)"
|
||||
head_check=current
|
||||
if [[ "$DEPLOY_SHA" != "$staging_head" ]]; then
|
||||
# Docs-only pushes (every change outside deploy/gated-paths.txt) no
|
||||
# longer run the gate, so staging may legitimately be ahead of the
|
||||
# tested SHA. That is decided only after the gate-attested controller
|
||||
# bundle is downloaded, by its own deploy/is-docs-only-range.sh, so
|
||||
# this job never executes an untested checker; anything that is not
|
||||
# a pure docs-only advance is still refused there before mutation.
|
||||
echo "staging head $staging_head differs from requested $DEPLOY_SHA; deferring the docs-only range check to the attested controller"
|
||||
head_check=deferred
|
||||
fi
|
||||
{
|
||||
echo "sha=$DEPLOY_SHA"
|
||||
echo "gate_run_id=$gate_run_id"
|
||||
echo "head_check=$head_check"
|
||||
} >>"$GITHUB_OUTPUT"
|
||||
|
||||
- name: Prepare pinned Node tooling
|
||||
@@ -181,7 +192,7 @@ jobs:
|
||||
python3 - "$controller_tar" <<'PY'
|
||||
import pathlib, sys, tarfile
|
||||
archive = pathlib.Path(sys.argv[1])
|
||||
required = {"deploy/run-staging-migration.sh", "frontend/scripts/staging-image-manifest.mjs"}
|
||||
required = {"deploy/run-staging-migration.sh", "deploy/is-docs-only-range.sh", "frontend/scripts/staging-image-manifest.mjs"}
|
||||
with tarfile.open(archive, "r:") as bundle:
|
||||
members = bundle.getmembers()
|
||||
names = [member.name for member in members]
|
||||
@@ -199,6 +210,34 @@ jobs:
|
||||
node artifacts/staging-image/extracted/frontend/scripts/staging-image-manifest.mjs \
|
||||
"$manifest" "$DEPLOY_SHA" "$IMAGE_REPOSITORY" >>"$GITHUB_OUTPUT"
|
||||
|
||||
- name: Refuse stale staging revision unless only docs advanced
|
||||
env:
|
||||
DEPLOY_SHA: ${{ steps.revision.outputs.sha }}
|
||||
HEAD_CHECK: ${{ steps.revision.outputs.head_check }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
staging_head="$(curl --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-all-errors \
|
||||
--header "Authorization: token $GITEA_TOKEN" \
|
||||
"$GITEA_API_URL/repos/$GITEA_REPOSITORY/git/refs/heads/staging" |
|
||||
jq -er 'select(type == "array" and length == 1) | .[0] |
|
||||
select(.ref == "refs/heads/staging") | .object.sha |
|
||||
select(test("^[0-9a-f]{40}$"))')"
|
||||
if [[ "$staging_head" == "$DEPLOY_SHA" ]]; then
|
||||
echo "staging head is the tested revision $DEPLOY_SHA (initial check: $HEAD_CHECK)"
|
||||
exit 0
|
||||
fi
|
||||
# Only the gate-attested controller's checker and path list are trusted;
|
||||
# it proves DEPLOY_SHA is an ancestor of the head and that every path in
|
||||
# between is outside deploy/gated-paths.txt via the Gitea compare API.
|
||||
checker=artifacts/staging-image/extracted/deploy/is-docs-only-range.sh
|
||||
[[ -f "$checker" ]] || { echo "gate-attested controller bundle lacks deploy/is-docs-only-range.sh; cannot accept an advanced staging head" >&2; exit 1; }
|
||||
if bash "$checker" --api "$DEPLOY_SHA" "$staging_head"; then
|
||||
echo "staging advanced to $staging_head by docs-only commits; releasing tested $DEPLOY_SHA"
|
||||
else
|
||||
echo "stale staging revision refused; migrate the current gated SHA" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Apply digest-pinned migration under host lock
|
||||
env:
|
||||
SSH_PRIVATE_KEY_BASE64: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
|
||||
@@ -227,7 +266,10 @@ jobs:
|
||||
jq -er 'select(type == "array" and length == 1) | .[0] |
|
||||
select(.ref == "refs/heads/staging") | .object.sha |
|
||||
select(test("^[0-9a-f]{40}$"))')"
|
||||
[[ "$current_head" == "$DEPLOY_SHA" ]] || { echo "staging advanced during migration; refusing stale mutation" >&2; exit 1; }
|
||||
[[ "$current_head" == "$DEPLOY_SHA" ]] && return
|
||||
# Docs-only pushes may land while a migration is in flight; the attested checker decides.
|
||||
bash artifacts/staging-image/extracted/deploy/is-docs-only-range.sh --api "$DEPLOY_SHA" "$current_head" ||
|
||||
{ echo "staging advanced during migration; refusing stale mutation" >&2; exit 1; }
|
||||
}
|
||||
cleanup() {
|
||||
if [[ -n "$incoming" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user