feat: a CI mode for the gate, and a gate report publishing can rely on
--ci records an upgrade no room could take (master between releases, its VERSION already on canary) as SKIP instead of failing every commit; its report can never stand in for a release's. --still-current accepts a report only for these exact bytes, passed, from exactly what canary and stable serve now. update_canary.sh takes one through RELEASE_GATE_REPORT_PASSED, so Preview's publish job needs the key and not Docker. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0f96019714
commit
583f9b1809
+73
-3
@@ -2,6 +2,8 @@
|
|||||||
# THE RELEASE GATE: may this exact file go to a classroom?
|
# THE RELEASE GATE: may this exact file go to a classroom?
|
||||||
#
|
#
|
||||||
# tools/release-gate.sh PACKAGE.deb [FROM_VERSION...]
|
# tools/release-gate.sh PACKAGE.deb [FROM_VERSION...]
|
||||||
|
# tools/release-gate.sh --ci PACKAGE.deb for CI; see below
|
||||||
|
# tools/release-gate.sh --still-current REPORT PACKAGE.deb
|
||||||
#
|
#
|
||||||
# Runs tools/validate-trixie-release.sh against PACKAGE as
|
# Runs tools/validate-trixie-release.sh against PACKAGE as
|
||||||
#
|
#
|
||||||
@@ -22,6 +24,18 @@
|
|||||||
#
|
#
|
||||||
# It stops at the first failure: a gate that carries on reports a pass
|
# It stops at the first failure: a gate that carries on reports a pass
|
||||||
# for a machine it never finished building.
|
# for a machine it never finished building.
|
||||||
|
#
|
||||||
|
# --ci is for a commit, not a release. Between releases master's VERSION
|
||||||
|
# is what canary already serves, and no room can upgrade to a version it
|
||||||
|
# has; such an upgrade is recorded as SKIP, with why, instead of failing
|
||||||
|
# every commit. Its report says it is a CI report and can never stand in
|
||||||
|
# for a release's.
|
||||||
|
#
|
||||||
|
# --still-current REPORT PACKAGE asks whether a gate report may still be
|
||||||
|
# relied on to publish PACKAGE: it has to be a release gate's report, for
|
||||||
|
# exactly these bytes, that passed, and whose upgrades were from exactly
|
||||||
|
# what canary and stable serve now. A report made before either channel
|
||||||
|
# moved is not current, and the gate has to be run again. Exit 0 or 1.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# The fetches below run in $(...), which otherwise ignores set -e: a
|
# The fetches below run in $(...), which otherwise ignores set -e: a
|
||||||
# manifest that failed to download would go on to compare an empty hash
|
# manifest that failed to download would go on to compare an empty hash
|
||||||
@@ -31,11 +45,27 @@ shopt -s inherit_errexit
|
|||||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
source "$ROOT/tools/release-lib.sh"
|
source "$ROOT/tools/release-lib.sh"
|
||||||
|
|
||||||
if [[ $# -lt 1 ]]; then
|
usage() {
|
||||||
echo "Usage: $0 PACKAGE.deb [FROM_VERSION...]" >&2
|
echo "Usage: $0 [--ci] PACKAGE.deb [FROM_VERSION...]" >&2
|
||||||
|
echo " $0 --still-current REPORT PACKAGE.deb" >&2
|
||||||
exit 2
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
mode=release
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
--ci) mode=ci; shift ;;
|
||||||
|
--still-current) mode=check; shift ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$mode" == check ]]; then
|
||||||
|
[[ $# -eq 2 ]] || usage
|
||||||
|
gate_report="$1"
|
||||||
|
shift
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
[[ $# -ge 1 ]] || usage
|
||||||
|
|
||||||
candidate="$(realpath "$1")"
|
candidate="$(realpath "$1")"
|
||||||
shift
|
shift
|
||||||
extra_from=("$@")
|
extra_from=("$@")
|
||||||
@@ -53,6 +83,36 @@ if [[ "$(basename "$candidate")" != "$(release_package_name "$version")" ]]; the
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
hash="$(sha256sum "$candidate" | awk '{print $1}')"
|
hash="$(sha256sum "$candidate" | awk '{print $1}')"
|
||||||
|
|
||||||
|
# The version a channel serves, read the way a classroom reads it.
|
||||||
|
served() {
|
||||||
|
curl -fsS -H 'Cache-Control: no-cache' "$RELEASE_URL/$1/manifest.json" \
|
||||||
|
| python3 -c 'import json, sys; print(json.load(sys.stdin)["version"])'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$mode" == check ]]; then
|
||||||
|
stale() { echo "The gate report $gate_report cannot be relied on: $1" >&2; exit 1; }
|
||||||
|
name="$(basename "$candidate")"
|
||||||
|
|
||||||
|
[[ -f "$gate_report" ]] || stale "there is no such file"
|
||||||
|
grep -qx "release gate for $name" "$gate_report" || stale "it is not about $name"
|
||||||
|
grep -qx "mode release" "$gate_report" || stale "it is not a release gate's report"
|
||||||
|
grep -qx "sha256 $hash" "$gate_report" || stale "it is about other bytes than $hash"
|
||||||
|
[[ "$(tail -n 1 "$gate_report")" == *": $name passed the release gate" ]] \
|
||||||
|
|| stale "it does not end in a pass"
|
||||||
|
! grep -q '^SKIP' "$gate_report" || stale "it skipped something"
|
||||||
|
grep -q "^PASS fresh install of $version " "$gate_report" || stale "it has no fresh install"
|
||||||
|
|
||||||
|
for channel in canary stable; do
|
||||||
|
now="$(served "$channel")"
|
||||||
|
grep -qF "PASS upgrade $now -> $version on a $channel room ($channel serves $now)" "$gate_report" \
|
||||||
|
|| stale "$channel serves $now now, and the report did not upgrade from that"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "The gate report for $name, sha256 $hash, is current: canary and stable are where it left them."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
report="${RELEASE_GATE_REPORT:-}"
|
report="${RELEASE_GATE_REPORT:-}"
|
||||||
work="$(mktemp -d)"
|
work="$(mktemp -d)"
|
||||||
trap 'rm -rf "$work"' EXIT
|
trap 'rm -rf "$work"' EXIT
|
||||||
@@ -65,6 +125,7 @@ record() {
|
|||||||
[[ -z "$report" ]] || : > "$report"
|
[[ -z "$report" ]] || : > "$report"
|
||||||
record "release gate for $(basename "$candidate")"
|
record "release gate for $(basename "$candidate")"
|
||||||
record "sha256 $hash"
|
record "sha256 $hash"
|
||||||
|
record "mode $mode"
|
||||||
record "started $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
record "started $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
|
||||||
# A channel's current package, fetched as a classroom fetches it and
|
# A channel's current package, fetched as a classroom fetches it and
|
||||||
@@ -129,6 +190,11 @@ for channel in canary stable; do
|
|||||||
from="$(release_package_version "$previous")"
|
from="$(release_package_version "$previous")"
|
||||||
|
|
||||||
if ! release_version_gt "$version" "$from"; then
|
if ! release_version_gt "$version" "$from"; then
|
||||||
|
if [[ "$mode" == ci ]]; then
|
||||||
|
record "SKIP upgrade $from -> $version on a $channel room: $channel serves $from, not older than this commit's VERSION (a CI build, not a release)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
echo "$channel is on $from; $version is not newer, so no room could take it." >&2
|
echo "$channel is on $from; $version is not newer, so no room could take it." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -146,4 +212,8 @@ for from in "${extra_from[@]}"; do
|
|||||||
"$ROOT/tools/validate-trixie-release.sh" "$candidate" "$previous"
|
"$ROOT/tools/validate-trixie-release.sh" "$candidate" "$previous"
|
||||||
done
|
done
|
||||||
|
|
||||||
record "finished $(date -u +%Y-%m-%dT%H:%M:%SZ): $(basename "$candidate") passed the release gate"
|
if [[ "$mode" == ci ]]; then
|
||||||
|
record "finished $(date -u +%Y-%m-%dT%H:%M:%SZ): $(basename "$candidate") passed the CI gate, which is not a release gate"
|
||||||
|
else
|
||||||
|
record "finished $(date -u +%Y-%m-%dT%H:%M:%SZ): $(basename "$candidate") passed the release gate"
|
||||||
|
fi
|
||||||
|
|||||||
+10
-1
@@ -75,7 +75,16 @@ fi
|
|||||||
# to fail here, not on a Sunday. See tools/release-gate.sh.
|
# to fail here, not on a Sunday. See tools/release-gate.sh.
|
||||||
hash="$(sha256sum "$package_path" | awk '{print $1}')"
|
hash="$(sha256sum "$package_path" | awk '{print $1}')"
|
||||||
|
|
||||||
"$ROOT/tools/release-gate.sh" "$package_path" ${RELEASE_GATE_FROM:-}
|
# RELEASE_GATE_REPORT_PASSED=REPORT relies on a gate already run on this
|
||||||
|
# file instead — Preview's gate job, which has Docker, where the publish
|
||||||
|
# job has the key and no Docker. The report is only accepted if it is a
|
||||||
|
# pass for exactly these bytes, from exactly what canary and stable serve
|
||||||
|
# now; anything else and nothing is published.
|
||||||
|
if [[ -n "${RELEASE_GATE_REPORT_PASSED:-}" ]]; then
|
||||||
|
"$ROOT/tools/release-gate.sh" --still-current "$RELEASE_GATE_REPORT_PASSED" "$package_path"
|
||||||
|
else
|
||||||
|
"$ROOT/tools/release-gate.sh" "$package_path" ${RELEASE_GATE_FROM:-}
|
||||||
|
fi
|
||||||
|
|
||||||
# Checked again after the gate, which takes long enough for somebody else
|
# Checked again after the gate, which takes long enough for somebody else
|
||||||
# to have published in the meantime — or for the file to have changed.
|
# to have published in the meantime — or for the file to have changed.
|
||||||
|
|||||||
Reference in New Issue
Block a user