diff --git a/scripts/check-phase19.7.sh b/scripts/check-phase19.7.sh index f1f0c668..90d746c3 100755 --- a/scripts/check-phase19.7.sh +++ b/scripts/check-phase19.7.sh @@ -64,6 +64,14 @@ # canonical checkout (USER_PLUGIN, OAUTHSERVER_PLUGIN or JOURNAL_PLUGIN), # and is reported as unreleased commits; anything else fails. # +# Accepted unreleased drift is not a release: the consumers stage then ends +# with "phase19.7 consumers passed WITH UNRELEASED PINS", and --all ends with +# "Phase 19.7 gate passed WITH UNRELEASED PINS: the release pins are not yet +# proven" instead of "Phase 19.7 gate passed" (the exit status stays 0, so +# the pre-release verification can run). A GOWORK=off release build of the +# demo compiles the older pinned code until the plugins are tagged and the +# pins bumped; only a PHASE197_RELEASED=1 run proves the release pins. +# # Environment overrides (default: the sibling checkouts next to this # repository): PHASE197_ROOT (this repository), USER_PLUGIN, # OAUTHSERVER_PLUGIN, JOURNAL_PLUGIN, DEMO_APP, FONOTEKA, BM_APP, the @@ -568,6 +576,8 @@ removed_exports() { run_self_test() { bash -n "${BASH_SOURCE[0]}" + # The planted pin checks below must not reach run_all's verdict. + unset PHASE197_PINS_FILE expect_detector detect_go pass 0 '{"Action":"pass","Package":"p","Test":"TestToolsList"}' expect_detector detect_go fail 1 '{"Action":"pass","Package":"p","Test":"TestA"} @@ -775,14 +785,24 @@ run_self_test() { "$(git -C "$sub" rev-parse --short=12 HEAD)" "${pin:0:12}" >"$demo/scripts/check-pins.sh" } planted_pins - expect_mode pins-test-only 0 "by test-only commits" --demo-pins "DEMO_APP=$demo" "USER_PLUGIN=$sub" PHASE197_RELEASED=0 + expect_mode pins-test-only 0 "by test-only commits" --demo-pins "DEMO_APP=$demo" "USER_PLUGIN=$sub" PHASE197_RELEASED=0 "PHASE197_PINS_FILE=$demo/pins.txt" + if [[ -s "$demo/pins.txt" ]]; then + echo "refuse: self-test pins-test-only-unrecorded: test-only drift was recorded as unreleased" >&2 + return 1 + fi + echo "PASS self-test pins-test-only-unrecorded" expect_mode pins-released-test-only 1 "refuse: PHASE197_RELEASED=1" --demo-pins "DEMO_APP=$demo" "USER_PLUGIN=$sub" PHASE197_RELEASED=1 printf 'package user\n\nfunc Added() {}\n' >"$sub/user.go" git -C "$sub" -c user.name=t -c user.email=t@example.test commit -q -am feature planted_pins canon="$demo/canonical" git clone -q "$sub" "$canon" - expect_mode pins-unreleased-accepted 0 "is ahead of its pin by unreleased commits" --demo-pins "DEMO_APP=$demo" "USER_PLUGIN=$canon" PHASE197_RELEASED=0 + expect_mode pins-unreleased-accepted 0 "is ahead of its pin by unreleased commits" --demo-pins "DEMO_APP=$demo" "USER_PLUGIN=$canon" PHASE197_RELEASED=0 "PHASE197_PINS_FILE=$demo/pins.txt" + if ! grep -qx "plugins/golem15/user" "$demo/pins.txt" 2>/dev/null; then + echo "refuse: self-test pins-unreleased-recorded: accepted unreleased drift was not recorded for the verdict" >&2 + return 1 + fi + echo "PASS self-test pins-unreleased-recorded" git -C "$canon" checkout -q "$pin" expect_mode pins-unreleased-refused 1 "does not hold" --demo-pins "DEMO_APP=$demo" "USER_PLUGIN=$canon" PHASE197_RELEASED=0 git -C "$canon" checkout -q - @@ -790,6 +810,24 @@ run_self_test() { unset -f planted_pins rm -rf "$demo" + # The final verdict: unqualified only without unreleased drift. + local verdict_file verdict + verdict_file="$(mktemp)" + verdict="$(gate_verdict "$verdict_file")" + if [[ "$verdict" != "Phase 19.7 gate passed" ]]; then + rm -f "$verdict_file" + echo "refuse: self-test verdict-released: \"$verdict\"" >&2 + return 1 + fi + printf 'plugins/golem15/user\n' >"$verdict_file" + verdict="$(gate_verdict "$verdict_file" 2>/dev/null)" + rm -f "$verdict_file" + if [[ "$(tail -n 1 <<<"$verdict")" != "Phase 19.7 gate passed WITH UNRELEASED PINS: the release pins are not yet proven (plugins/golem15/user)" ]]; then + echo "refuse: self-test verdict-unreleased: \"$verdict\"" >&2 + return 1 + fi + echo "PASS self-test verdict (unqualified only without unreleased pins)" + local mode for mode in self-test go plugins consumers docker admin docs marker all; do grep -q -- "^ --$mode" "${BASH_SOURCE[0]}" || { @@ -922,9 +960,40 @@ run_consumers() { vue_checked # T-19.7-37: the demo's vhost opens exactly the MCP paths, unbuffered. (cd "$DEMO_APP" && bash scripts/check-deploy.sh) + if [[ "${#UNRELEASED_PINS[@]}" -gt 0 ]]; then + echo "phase19.7 consumers passed WITH UNRELEASED PINS: ${UNRELEASED_PINS[*]} (the release pins are not yet proven)" + return 0 + fi echo "phase19.7 consumers passed" } +# UNRELEASED_PINS lists the demo plugin submodules demo_pins accepted ahead +# of their pins by non-test commits; with PHASE197_PINS_FILE set, demo_pins +# also appends each name there, so run_all's verdict sees them. +UNRELEASED_PINS=() + +# gate_verdict FILE prints the --all verdict: "Phase 19.7 gate passed" when +# FILE lists no unreleased pin, else a qualified line naming them. +gate_verdict() { + local file="$1" names + names="$(sort -u "$file" 2>/dev/null | paste -sd ' ' -)" + if [[ -z "$names" ]]; then + echo "Phase 19.7 gate passed" + return 0 + fi + cat >&2 </dev/null; then - echo "pins: $name is ahead of its pin by unreleased commits (release after verification)" + echo "pins: $name is ahead of its pin by unreleased commits (release after verification; the release pins are not yet proven)" + UNRELEASED_PINS+=("$name") + if [[ -n "${PHASE197_PINS_FILE:-}" ]]; then + printf '%s\n' "$name" >>"$PHASE197_PINS_FILE" + fi continue fi printf '%s\n' "$changed" >&2 @@ -1156,7 +1229,11 @@ run_docs() { } run_all() { - local stage + local stage pins_file + pins_file="$(mktemp)" + # shellcheck disable=SC2064 # expand now: the path is local to run_all + trap "rm -f '$pins_file'" EXIT + export PHASE197_PINS_FILE="$pins_file" for stage in self-test go plugins consumers admin docs marker; do if bash "${BASH_SOURCE[0]}" "--$stage"; then echo "PASS $stage" @@ -1166,7 +1243,7 @@ run_all() { fi done docker_gate || exit 1 - echo "Phase 19.7 gate passed" + gate_verdict "$pins_file" } case "${1:---all}" in