From c53d625744b8128f2958e1d671f07459675c3955 Mon Sep 17 00:00:00 2001 From: Dev Agent Date: Sun, 7 Jun 2026 12:11:07 +0300 Subject: [PATCH] ORCH-058: --build-staging runs staging_check.py --mode stub vs fresh 8501 (AC-4) Per ADR-001 step 3 / AC-4: after the freshly rebuilt staging container is healthy, run staging_check.py --mode stub against the fresh 8501 stand BEFORE reporting success, so the EXACT artefact BUILD-ONCE retagged to prod is the one validated on staging. Fail-closed: staging_check rc!=0 -> exit 1 (not promoted). - Invoked inside the container (docker exec $TARGET_SERVICE) per the canonical signature in scripts/staging_check.py header, --base-url http://localhost:$TARGET_PORT. - Targets ONLY 8501 (staging), never 8500 (prod) - AC-9. - --mode stub: fast, deterministic, no LLM spend (ADR). - Static regression test test_tc07_build_staging_runs_staging_check_stub_after_health: asserts staging_check.py + --mode stub present, runs after health, before exit 0, fail-closed, and never hard-codes prod 8500. --- scripts/orchestrator-deploy-hook.sh | 17 ++++++++++++++-- tests/test_deploy_hook_provenance.py | 29 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/scripts/orchestrator-deploy-hook.sh b/scripts/orchestrator-deploy-hook.sh index 75b855b..5d3d302 100755 --- a/scripts/orchestrator-deploy-hook.sh +++ b/scripts/orchestrator-deploy-hook.sh @@ -175,8 +175,21 @@ if [[ "${1:-}" == "--build-staging" ]]; then fi log "BUILD-STAGING: running health-check on port $TARGET_PORT (10x6s)" if health_check 10 6 "build-staging-health"; then - log "BUILD-STAGING: $TARGET_SERVICE healthy on the fresh image (exit 0)" - exit 0 + log "BUILD-STAGING: $TARGET_SERVICE healthy on the fresh image" + # AC-4 / ADR-001 step 3: validate the EXACT fresh artefact that will be + # BUILD-ONCE retagged to prod by running staging_check.py against the + # freshly recreated STAGING stand (8501, never prod 8500 - AC-9). + # --mode stub: fast, deterministic, no LLM spend (ADR). Run INSIDE the + # container so B6 reads the running instance own env (.env.staging). + log "BUILD-STAGING: running staging_check.py --mode stub against fresh 8501 (port $TARGET_PORT)" + if docker exec "$TARGET_SERVICE" \\ + python3 /repos/orchestrator/scripts/staging_check.py \\ + --base-url "http://localhost:$TARGET_PORT" --mode stub >> "$LOG" 2>&1; then + log "BUILD-STAGING: staging_check --mode stub PASS on fresh image (exit 0)" + exit 0 + fi + log "BUILD-STAGING: staging_check --mode stub FAILED on fresh image - not promoting (exit 1)" + exit 1 fi log "BUILD-STAGING: health FAILED after rebuild (exit 1)" exit 1 diff --git a/tests/test_deploy_hook_provenance.py b/tests/test_deploy_hook_provenance.py index 24416a1..b4652bb 100644 --- a/tests/test_deploy_hook_provenance.py +++ b/tests/test_deploy_hook_provenance.py @@ -95,6 +95,35 @@ def test_tc07_build_staging_recreates_and_health_checks_8501(): assert "exit 1" in block +def test_tc07_build_staging_runs_staging_check_stub_after_health(): + """AC-4 / ADR-001 step 3: after the fresh staging container is healthy, the + --build-staging mode MUST run staging_check.py --mode stub against the fresh + 8501 stand BEFORE reporting success, and fail-closed (exit 1) if it fails - + so the EXACT artefact promoted to prod is the one that passed staging.""" + block = _build_staging_block() + # staging_check is invoked in --mode stub (fast, no LLM spend per ADR). + assert "staging_check.py" in block + assert "--mode stub" in block + # It targets the fresh STAGING stand (8501 / TARGET_PORT), never prod 8500. + assert '--base-url "http://localhost:$TARGET_PORT"' in block + # AC-9: the staging_check invocation must NOT hard-code the prod port (8500). + invocation_lines = [ + ln for ln in block.splitlines() + if "staging_check.py" in ln or "--base-url" in ln + ] + assert invocation_lines, "expected a staging_check.py invocation line" + assert all("8500" not in ln for ln in invocation_lines) + # Ordering: staging_check runs AFTER the health-check, BEFORE the final exit 0. + health_idx = block.index('health_check 10 6 "build-staging-health"') + check_idx = block.index("staging_check.py") + assert health_idx < check_idx, "staging_check must run after health_check" + exit0_idx = block.index("staging_check --mode stub PASS") + success_exit = block.index("exit 0", exit0_idx) + assert check_idx < success_exit, "staging_check must precede the success exit 0" + # Fail-closed: a non-zero staging_check surfaces as exit 1 (no prod promote). + assert "staging_check --mode stub FAILED" in block + + # --------------------------------------------------------------------------- # TC-08: Dockerfile stamps the OCI revision label from a build-arg # ---------------------------------------------------------------------------