diff --git a/.gitea/workflows/manifest-entry-existence-check.yml b/.gitea/workflows/manifest-entry-existence-check.yml new file mode 100644 index 000000000..928ac83d3 --- /dev/null +++ b/.gitea/workflows/manifest-entry-existence-check.yml @@ -0,0 +1,82 @@ +name: manifest-entry-existence-check + +# Gitea Actions port of the manual "GET /api/v1/repos/ for each +# manifest entry" audit that the engineer-b identity ran on 2026-06-04 to +# fix #2183 (main-red on 0b91c18031 — publish-workspace-server-image +# failed at the Pre-clone manifest deps step because manifest.json +# referenced 2 non-existent repos: free-beats-all + medo-smoke). +# +# The bug class is "latent 404 in manifest.json only surfaces on the +# next push to main, which is too late". This workflow moves the check +# to PR-review time so the bad entry never reaches main. +# +# Trigger: only on PRs that modify manifest.json. The `paths:` filter +# keeps the workflow out of the PR-CI test matrix for every other PR. +# +# Auth: anonymous Gitea API. Per the 2026-05-08 OSS-surface contract +# (commit 15935143c8d2 _comment), every entry in manifest.json is +# public on git.moleculesai.app. No new secrets required. +# +# Failure surface: each broken entry is named in an ::error:: annotation +# so the PR author sees exactly which line to fix. Mirrors the +# `Pre-clone manifest deps` step's per-entry error format in +# publish-workspace-server-image.yml. +# +# NOT a required status check today. Open design question tracked in +# #2185 — making it required is a CTO ruling (admin-only branch-protection +# config). Watchdog + post-merge RCA is the current backstop. + +on: + pull_request: + paths: + - manifest.json + +permissions: + contents: read + +jobs: + # Design question: whether this should be a hard required check. + # The PR-time emit is asymmetric until the CTO rules. Tracking: #2185. + # bp-required: pending #2185 — do NOT flip to yes until BP lists the context. + check-entries: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Verify each manifest entry resolves on Gitea + run: | + set -euo pipefail + # Strip JSON5 // comments first to match the publish workflow's + # `Pre-clone manifest deps` parsing path — Integration Tester + # appends `// Triggered by ...` which breaks `jq` otherwise. + sed '/^[[:space:]]*\/\//d' manifest.json > /tmp/manifest.json + # Anonymous API is enough: per the 2026-05-08 OSS-surface contract + # (15935143c8d2 _comment), every entry is public on Gitea. + count=$(jq -r '(.plugins + .workspace_templates + .org_templates) | length' /tmp/manifest.json) + echo "Checking $count manifest entries against Gitea..." + missing=() + for i in $(seq 0 $((count-1))); do + name=$(jq -r "(.plugins + .workspace_templates + .org_templates)[$i].name" /tmp/manifest.json) + repo=$(jq -r "(.plugins + .workspace_templates + .org_templates)[$i].repo" /tmp/manifest.json) + for attempt in 1 2 3; do + http_code=$(curl -s -o /dev/null -w "%{http_code}" "https://git.moleculesai.app/api/v1/repos/${repo}") + if [ "$http_code" = "200" ]; then + echo " OK $name -> $repo" + break + elif [ "$http_code" = "404" ]; then + echo "::error::manifest entry '$name' points at '$repo' which does not exist on Gitea (404)" + missing+=("$name:$repo") + break + else + echo " attempt $attempt: $name -> $repo returned HTTP $http_code, retrying in $((attempt * 2))s" + sleep $((attempt * 2)) + fi + done + done + if [ "${#missing[@]}" -gt 0 ]; then + echo "::error::${#missing[@]} manifest entries are broken:" + printf ' - %s\n' "${missing[@]}" + exit 1 + fi + echo "All $count manifest entries resolve on Gitea."