#!/usr/bin/env bash # scripts/ci-detect-already-validated.sh — emit `skip=true|false` to $GITHUB_OUTPUT for the # docker-build.yml cross-run tree-identity gate (ersatztv#420). On a merge-to-main push, if the # merged tree is byte-identical to a PR head that ALREADY passed `test`+`migrations` (a GREEN # combined commit status), the heavy compile/test/migrations work is redundant -- the exact same # source was already validated. `build` still runs and still builds+pushes the image, so no image # ever ships from unvalidated source. # # The bias is ALWAYS toward running MORE, never less: a false 'true' would ship (or claim to # validate) unreviewed/unvalidated source, so every ambiguous or unverifiable case resolves to # skip=false. It is fine (just wasteful) to re-run on an identical tree; it is a correctness bug # to skip validation on a tree that differs or was never proven green. # # Runs identically locally and in CI. Locally (no $GITHUB_OUTPUT) it prints the decision to # stdout; e.g. GITHUB_EVENT_NAME=push GITHUB_REF=refs/heads/main scripts/ci-detect-already-validated.sh set -euo pipefail out="${GITHUB_OUTPUT:-/dev/stdout}" event="${GITHUB_EVENT_NAME:-}" ref="${GITHUB_REF:-}" emit() { echo "skip=$1" >> "$out" echo "-> skip=$1" } # Only a push directly to main can possibly be a merge-to-main we can cross-check against an # already-validated PR head. Everything else (pull_request, tag push, workflow_dispatch, a push # to any other branch) -> always run. if [ "$event" != "push" ] || [ "$ref" != "refs/heads/main" ]; then echo "event='${event:-}' ref='${ref:-}' (need push to refs/heads/main); running full validation (safe default)" emit false exit 0 fi # Deepen history so HEAD's second parent (and its tree) are resolvable -- a shallow checkout may # have neither. Mirrors the docs-only/docs-reminder jobs' fetch style. Non-fatal: a failed # deepen still falls through to the HEAD^2 check below, which then fails safe. git fetch --deepen=2 origin 2>/dev/null || git fetch --unshallow origin 2>/dev/null || true # HEAD must be a real merge commit with a second parent -- that second parent is the PR head CI # actually validated. No second parent (a direct/fast-forward/squash push) -> nothing to compare # against -> always run. pr_head="$(git rev-parse --verify -q HEAD^2 || true)" if [ -z "$pr_head" ]; then echo "HEAD has no second parent (not a merge commit); running full validation (safe default)" emit false exit 0 fi merge_tree="$(git rev-parse --verify -q 'HEAD^{tree}' || true)" pr_tree="$(git rev-parse --verify -q "${pr_head}^{tree}" || true)" if [ -z "$merge_tree" ] || [ -z "$pr_tree" ]; then echo "could not resolve HEAD or HEAD^2 tree; running full validation (safe default)" emit false exit 0 fi if [ "$merge_tree" != "$pr_tree" ]; then echo "merged tree ($merge_tree) != PR head $pr_head tree ($pr_tree) -- main advanced since the PR was validated; running full validation" emit false exit 0 fi # Merge tree matches the PR head tree exactly. Confirm that PR head was actually validated green # before trusting it -- query the Gitea combined commit status API. Auth is required (private # instance); a missing/failing/non-success response always falls through to skip=false. if [ -z "${ETV_STATUS_AUTH:-}" ]; then echo "ETV_STATUS_AUTH not set; cannot verify PR head status; running full validation (safe default)" emit false exit 0 fi status_url="http://192.168.1.95:3000/api/v1/repos/timothy/ersatztv/commits/${pr_head}/status" status_json="$(curl -sf -u "$ETV_STATUS_AUTH" "$status_url" || true)" if [ -z "$status_json" ]; then echo "status API request for PR head ${pr_head} failed; running full validation (safe default)" emit false exit 0 fi state="$(printf '%s' "$status_json" | jq -r '.state // empty' 2>/dev/null || true)" if [ "$state" != "success" ]; then echo "PR head ${pr_head} combined status is '${state:-}', not 'success'; running full validation (safe default)" emit false exit 0 fi echo "merged tree == green PR head ${pr_head} (status=success) -> skipping re-validation" emit true