HH-443: establish production CI and supply-chain evidence (#93)

* ci: enforce production release gates

* ci: close release gate review blockers

* ci: preserve verified digests during promotion

* ci: serialize and verify release cleanup

* ci: build govulncheck with Shangwutong toolchain

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-22 03:26:38 +08:00
committed by GitHub
co-authored by rogee
parent 798ea43c2f
commit 02a188dc29
17 changed files with 483 additions and 89 deletions
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
delete_package_version() {
local endpoint=$1 tag=$2 digest=$3 attempt versions version_id
local retries=${RELEASE_RETRY_COUNT:-5} delay=${RELEASE_RETRY_DELAY:-2}
for ((attempt = 1; attempt <= retries; attempt++)); do
if versions=$(gh api --paginate --slurp "$endpoint?per_page=100") &&
version_id=$(jq -er --arg tag "$tag" --arg digest "$digest" \
'[.[][] | select(.name == $digest and ((.metadata.container.tags // []) | index($tag)))] | if length == 1 then .[0].id else empty end' \
<<< "$versions") &&
[[ $version_id =~ ^[0-9]+$ ]] &&
gh api --method DELETE "$endpoint/$version_id"; then
return 0
fi
((attempt == retries)) || sleep "$delay"
done
echo "Could not delete package version for $tag at $digest" >&2
return 1
}
wait_for_tag_absent() {
local target=$1 attempt output
local retries=${RELEASE_RETRY_COUNT:-5} delay=${RELEASE_RETRY_DELAY:-2}
for ((attempt = 1; attempt <= retries; attempt++)); do
if output=$(crane digest "$target" 2>&1); then
:
elif grep -Eiq 'MANIFEST_UNKNOWN|manifest unknown|not found|404' <<< "$output"; then
return 0
fi
((attempt == retries)) || sleep "$delay"
done
[[ -z $output ]] || printf '%s\n' "$output" >&2
echo "Tag still resolves after cleanup: $target" >&2
return 1
}
+64
View File
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -euo pipefail
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
source "$root/.github/scripts/release_helpers.sh"
export RELEASE_RETRY_COUNT=5 RELEASE_RETRY_DELAY=0
delete_attempts=0
deleted_path=
gh() {
if [[ $* == *'--paginate --slurp'* ]]; then
printf '%s\n' '[[
{"id": 11, "name": "sha256:correct", "metadata": {"container": {"tags": ["rollback-other"]}}},
{"id": 22, "name": "sha256:wrong", "metadata": {"container": {"tags": ["rollback-123-1"]}}},
{"id": 33, "name": "sha256:correct", "metadata": {"container": {"tags": ["rollback-123-1"]}}}
]]'
return
fi
((++delete_attempts))
deleted_path=${4:-}
((delete_attempts >= 3))
}
delete_package_version /users/owner/packages/container/repo/versions rollback-123-1 sha256:correct
[[ $delete_attempts -eq 3 ]]
[[ $deleted_path == /users/owner/packages/container/repo/versions/33 ]]
counter_file=$(mktemp)
trap 'rm -f "$counter_file"' EXIT
printf '0\n' > "$counter_file"
crane() {
local count
count=$(< "$counter_file")
((++count))
printf '%s\n' "$count" > "$counter_file"
if ((count < 3)); then
printf '%s\n' sha256:rollback
return
fi
echo MANIFEST_UNKNOWN >&2
return 1
}
wait_for_tag_absent ghcr.io/owner/repo:1.2.3
[[ $(< "$counter_file") -eq 3 ]]
python3 - "$root/.github/workflows/ci.yml" <<'PY'
import re
import sys
from pathlib import Path
workflow = Path(sys.argv[1]).read_text()
release = re.search(r"(?ms)^ release:\n(.*?)(?=^ [a-zA-Z0-9_-]+:\n|\Z)", workflow)
assert release, "release job missing"
body = release.group(1)
assert re.search(
r"(?m)^ concurrency:\n group: release-promotion\n cancel-in-progress: false$",
body,
), "all release tags must share one non-cancelling promotion lock"
assert "github.ref" not in re.search(r"(?ms)^ concurrency:\n(.*?)(?=^ \S)", body).group(1)
PY
echo "release promotion checks passed"