From 193a959d01172525ab77be4e59336aca5708a31f Mon Sep 17 00:00:00 2001 From: core-devops Date: Fri, 5 Jun 2026 17:00:10 -0700 Subject: [PATCH] fix(manifest): restore seo-agent + google-adk templates; auth the existence check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2192 added scripts/check-manifest-repos-exist.sh which curled the Gitea API per manifest repo WITHOUT auth ("public endpoint, no auth needed"). But molecule-ai-workspace-template-seo-agent and -google-adk are PRIVATE repos, so the unauthenticated GET returned 404 — indistinguishable from a genuinely-missing repo — and the guard false-pruned both from manifest.json. Every tenant lost them from its workspace-template palette. The real cloner (scripts/clone-manifest.sh) authenticates with MOLECULE_GITEA_TOKEN, so the templates cloned fine until the prune. Fixes: - Re-add the two workspace_templates entries (byte-identical to the pre-#2192 manifest blob). Does NOT re-add free-beats-all / medo-smoke, which #2192 correctly removed (truly-deleted org templates). - check-manifest-repos-exist.sh now sends `Authorization: token ${MOLECULE_GITEA_TOKEN}` when the token is set, so a private repo is no longer mistaken for a missing one. A 404 WITH a valid token still means truly-missing — the guard's real purpose is preserved. Falls back to an unauthenticated request when the token is unset (local dev). - Wire MOLECULE_GITEA_TOKEN (secrets.AUTO_SYNC_TOKEN, same as the clone step) into the "Validate manifest entries exist" workflow step, which previously had no token in its env. Verified: unauth GET of both repos returns 404 (the false-prune trigger); script smoke-test confirms the Authorization header is sent for every entry when the token is set, and omitted when unset; manifest.json is valid JSON; bash -n + shellcheck clean. Note: manifest.json is baked into the tenant image (workspace-server/Dockerfile.tenant:121), so templates reappear in tenant palettes only after merge -> tenant image rebuild -> fleet redeploy, not instantly on merge. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../publish-workspace-server-image.yml | 7 +++++++ manifest.json | 4 +++- scripts/check-manifest-repos-exist.sh | 18 ++++++++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/publish-workspace-server-image.yml b/.gitea/workflows/publish-workspace-server-image.yml index e050b5bc8..6b54bea73 100644 --- a/.gitea/workflows/publish-workspace-server-image.yml +++ b/.gitea/workflows/publish-workspace-server-image.yml @@ -123,7 +123,14 @@ jobs: # with a per-entry ::error:: annotation naming the missing repo # (issue #2192). This is the push-time complement to PR #2186's # PR-time manifest-entry-existence gate. + # + # Token: workspace-template-* repos are PRIVATE, so the existence check + # must authenticate (same AUTO_SYNC_TOKEN as the clone step). Without it + # an unauthenticated GET 404s on private repos and false-prunes them + # (regression that dropped seo-agent/google-adk from the palette). - name: Validate manifest entries exist + env: + MOLECULE_GITEA_TOKEN: ${{ secrets.AUTO_SYNC_TOKEN }} run: | set -euo pipefail bash scripts/check-manifest-repos-exist.sh manifest.json diff --git a/manifest.json b/manifest.json index 5024514f2..75395fcca 100644 --- a/manifest.json +++ b/manifest.json @@ -28,7 +28,9 @@ {"name": "claude-code-default", "repo": "molecule-ai/molecule-ai-workspace-template-claude-code", "ref": "main"}, {"name": "hermes", "repo": "molecule-ai/molecule-ai-workspace-template-hermes", "ref": "main"}, {"name": "openclaw", "repo": "molecule-ai/molecule-ai-workspace-template-openclaw", "ref": "main"}, - {"name": "codex", "repo": "molecule-ai/molecule-ai-workspace-template-codex", "ref": "main"} + {"name": "codex", "repo": "molecule-ai/molecule-ai-workspace-template-codex", "ref": "main"}, + {"name": "google-adk", "repo": "molecule-ai/molecule-ai-workspace-template-google-adk", "ref": "main"}, + {"name": "seo-agent", "repo": "molecule-ai/molecule-ai-workspace-template-seo-agent", "ref": "main"} ], "org_templates": [ {"name": "molecule-dev", "repo": "molecule-ai/molecule-ai-org-template-molecule-dev", "ref": "main"}, diff --git a/scripts/check-manifest-repos-exist.sh b/scripts/check-manifest-repos-exist.sh index 278d86a2c..4c56d4b16 100755 --- a/scripts/check-manifest-repos-exist.sh +++ b/scripts/check-manifest-repos-exist.sh @@ -50,8 +50,22 @@ check_category() { repo=$(echo "$MANIFEST_JSON" | jq -r ".${category}[$i].repo") TOTAL=$((TOTAL + 1)) - # Check repo existence via Gitea API (public endpoint, no auth needed) - http_code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 "${GITEA_API}/${repo}" 2>/dev/null || true) + # Check repo existence via Gitea API. Many manifest repos are PRIVATE + # (e.g. the workspace templates), so an *unauthenticated* GET returns + # 404 even when the repo exists — indistinguishable from a genuinely + # missing repo. We therefore authenticate with the same token + # clone-manifest.sh uses (MOLECULE_GITEA_TOKEN). A 404 *with* a valid + # token still means the repo is truly missing, which is what we want + # to catch. If the token is unset (local dev), fall back to an + # unauthenticated request — private repos will then 404, so run the + # check in CI where the token is present. + if [ -n "${MOLECULE_GITEA_TOKEN:-}" ]; then + http_code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \ + -H "Authorization: token ${MOLECULE_GITEA_TOKEN}" \ + "${GITEA_API}/${repo}" 2>/dev/null || true) + else + http_code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 "${GITEA_API}/${repo}" 2>/dev/null || true) + fi if [ "$http_code" != "200" ]; then echo "::error::manifest.json ${category} entry '${name}' → repo '${repo}' returned HTTP ${http_code} (expected 200). Delete the manifest entry BEFORE deleting the repo." >&2 -- 2.52.0