#!/usr/bin/env bash # Write an immutable promotion receipt for a commit under # .doc/test-reports/assets/promotion-receipts/.json # # Usage: # ./scripts/promotion-receipt-write.sh \ # --verdict pass|product_fail|infra_fail \ # [--commit SHA] [--suite NAME] [--failure-class none|product|infra] \ # [--runner-url URL] [--model-id ID] [--protocol NAME] \ # [--evidence PATH ...] [--force] set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" RECEIPT_DIR="${ROOT}/.doc/test-reports/assets/promotion-receipts" VERDICT="" COMMIT="" SUITE="nightly-real-pi" FAILURE_CLASS="" RUNNER_URL="${PI_AGENT_RUNNER_URL:-http://127.0.0.1:18865}" MODEL_ID="${PI_AGENT_MODEL:-MiniMax-M2.7-highspeed}" PROTOCOL="${PI_AGENT_PROTOCOL:-typed-result-v1}" FORCE=0 EVIDENCE_REFS=() usage() { sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//' exit 1 } while [[ $# -gt 0 ]]; do case "$1" in --verdict) VERDICT="${2:-}"; shift 2 ;; --commit) COMMIT="${2:-}"; shift 2 ;; --suite) SUITE="${2:-}"; shift 2 ;; --failure-class) FAILURE_CLASS="${2:-}"; shift 2 ;; --runner-url) RUNNER_URL="${2:-}"; shift 2 ;; --model-id) MODEL_ID="${2:-}"; shift 2 ;; --protocol) PROTOCOL="${2:-}"; shift 2 ;; --evidence) EVIDENCE_REFS+=("${2:-}"); shift 2 ;; --force) FORCE=1; shift ;; -h|--help) usage ;; *) echo "unknown arg: $1" >&2; usage ;; esac done case "${VERDICT}" in pass|product_fail|infra_fail) ;; *) echo "error: --verdict must be pass|product_fail|infra_fail" >&2; exit 1 ;; esac if [[ -z "${COMMIT}" ]]; then COMMIT="$(git -C "${ROOT}" rev-parse HEAD)" fi COMMIT="$(echo "${COMMIT}" | tr -d '[:space:]' | tr 'A-F' 'a-f')" if [[ ! "${COMMIT}" =~ ^[0-9a-f]{7,64}$ ]]; then echo "error: invalid commit sha: ${COMMIT}" >&2 exit 1 fi if [[ -z "${FAILURE_CLASS}" ]]; then case "${VERDICT}" in pass) FAILURE_CLASS="none" ;; product_fail) FAILURE_CLASS="product" ;; infra_fail) FAILURE_CLASS="infra" ;; esac fi digest_for() { local name="$1" local env_val="${2:-}" if [[ -n "${env_val}" ]]; then printf '%s' "${env_val}" return 0 fi if ! command -v docker >/dev/null 2>&1; then printf '' return 0 fi if ! docker inspect "${name}" >/dev/null 2>&1; then printf '' return 0 fi local repo repo="$(docker inspect --format '{{index .RepoDigests 0}}' "${name}" 2>/dev/null || true)" if [[ -n "${repo}" && "${repo}" != "" ]]; then printf '%s' "${repo}" return 0 fi docker inspect --format '{{.Id}}' "${name}" 2>/dev/null || true } # Prefer candidate-images-build manifest when present. IMAGES_JSON="" if [[ -f "${RECEIPT_DIR}/${COMMIT}.images.json" ]]; then IMAGES_JSON="${RECEIPT_DIR}/${COMMIT}.images.json" else # Resolve abbreviated commit to images.json via prefix. shopt -s nullglob for f in "${RECEIPT_DIR}"/*.images.json; do base="$(basename "${f}" .images.json)" if [[ "${COMMIT}" == "${base}"* || "${base}" == "${COMMIT}"* ]]; then IMAGES_JSON="${f}" break fi done fi if [[ -n "${IMAGES_JSON}" ]]; then APP_DIGEST="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("image_digests",{}).get("app") or "")' "${IMAGES_JSON}")" EVENTS_DIGEST="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("image_digests",{}).get("events") or "")' "${IMAGES_JSON}")" PI_DIGEST="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("image_digests",{}).get("pi-agent") or "")' "${IMAGES_JSON}")" else APP_DIGEST="$(digest_for shuimu-agent-app-dev "${RECEIPT_IMAGE_APP:-}")" EVENTS_DIGEST="$(digest_for shuimu-agent-events-dev "${RECEIPT_IMAGE_EVENTS:-}")" PI_DIGEST="$(digest_for shuimu-pi-agent-dev "${RECEIPT_IMAGE_PI_AGENT:-}")" # Also try candidate tags for this commit. SHORT12="${COMMIT:0:12}" [[ -z "${APP_DIGEST}" ]] && APP_DIGEST="$(digest_for "shuimu-agent-app:candidate-${SHORT12}" "")" [[ -z "${EVENTS_DIGEST}" ]] && EVENTS_DIGEST="$(digest_for "shuimu-agent-events:candidate-${SHORT12}" "")" [[ -z "${PI_DIGEST}" ]] && PI_DIGEST="$(digest_for "shuimu-pi-agent:candidate-${SHORT12}" "")" fi mkdir -p "${RECEIPT_DIR}" OUT="${RECEIPT_DIR}/${COMMIT}.json" if [[ -f "${OUT}" && "${FORCE}" -ne 1 ]]; then echo "error: receipt already exists (immutable): ${OUT}" >&2 echo "hint: use --force only for local fixture regeneration" >&2 exit 1 fi RECORDED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)" if ((${#EVIDENCE_REFS[@]})); then EVIDENCE_JSON="$(python3 -c 'import json,sys; print(json.dumps(sys.argv[1:]))' "${EVIDENCE_REFS[@]}")" else EVIDENCE_JSON='[]' fi export VERDICT COMMIT RECORDED_AT RUNNER_URL MODEL_ID PROTOCOL SUITE FAILURE_CLASS export APP_DIGEST EVENTS_DIGEST PI_DIGEST EVIDENCE_JSON OUT python3 -c ' import json, os payload = { "verdict": os.environ["VERDICT"], "commit": os.environ["COMMIT"], "recorded_at": os.environ["RECORDED_AT"], "runner_url": os.environ["RUNNER_URL"], "model_id": os.environ["MODEL_ID"], "protocol": os.environ["PROTOCOL"], "image_digests": { "app": os.environ.get("APP_DIGEST") or None, "events": os.environ.get("EVENTS_DIGEST") or None, "pi-agent": os.environ.get("PI_DIGEST") or None, }, "suite": os.environ["SUITE"], "failure_class": os.environ["FAILURE_CLASS"], "evidence_refs": json.loads(os.environ.get("EVIDENCE_JSON") or "[]"), } out = os.environ["OUT"] with open(out, "w", encoding="utf-8") as f: json.dump(payload, f, indent=2, ensure_ascii=False) f.write("\n") print(out) ' echo "wrote promotion receipt: ${OUT}"