From 25bfc3a6453d94e65fb869570c832e1441f5cb24 Mon Sep 17 00:00:00 2001 From: kagura-agent Date: Fri, 21 Aug 2026 10:46:44 +0800 Subject: [PATCH 1/3] ci(release): require GAIA_HUB_PUBLISH_URL for hub publish, drop dead fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The publish steps picked their target URL from a three-level chain ${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-https://hub.amd-gaia.ai}}. GAIA_HUB_BASE_URL exists nowhere in the repo (dead middle branch), and the hardcoded URL was a latent fallback: if GAIA_HUB_PUBLISH_URL is ever unset or renamed, a release would silently publish to the hardcoded address instead of failing. Drop the dead branch and hardcoded default from every publish --base-url, and add a fail-loudly assert for GAIA_HUB_PUBLISH_URL mirroring the existing 'Require the publish token' / 'Assert hub publish token present' steps: a missing variable is now a startup-time error naming what is missing and how to set it, not a silent wrong-origin publish. GAIA_HUB_BASE_URL is kept where it is the documented download/lock origin (the ver steps' base_url output, GET-verify steps) — only the publish target is now explicit and required. Closes #2937 --- .claude/skills/agent-hub-release/SKILL.md | 3 ++- .github/workflows/release_agent_chat.yml | 24 +++++++++++++---- .github/workflows/release_agent_email.yml | 28 +++++++++++++------ .github/workflows/release_agent_gaia.yml | 20 +++++++++++--- .github/workflows/release_components.yml | 33 ++++++++++++++++++----- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/.claude/skills/agent-hub-release/SKILL.md b/.claude/skills/agent-hub-release/SKILL.md index b0ed6c101..c09d7bfbe 100644 --- a/.claude/skills/agent-hub-release/SKILL.md +++ b/.claude/skills/agent-hub-release/SKILL.md @@ -177,7 +177,8 @@ Per the workflow header (`release_agent_email.yml`), these are **maintainer setu `baseUrl` (default `https://hub.amd-gaia.ai`). - **Var `GAIA_HUB_PUBLISH_URL`** — the Worker's **workers.dev** URL for uploads. The free-plan WAF on the proxied `hub.amd-gaia.ai` custom domain blocks large binary - uploads (but not GETs). Unset → uploads fall back to the custom domain and **403**. + uploads (but not GETs). **Required** — the release fails loudly if it is unset (no + silent fallback to the custom domain). - **Railway `HUB_CATALOG_URL=https://hub.amd-gaia.ai`** so the website rebuild reflects the new entry. diff --git a/.github/workflows/release_agent_chat.yml b/.github/workflows/release_agent_chat.yml index 334714cb9..14ee75c87 100644 --- a/.github/workflows/release_agent_chat.yml +++ b/.github/workflows/release_agent_chat.yml @@ -55,11 +55,12 @@ # restricted to `main` AND the `agent-pkg-*` tag pattern). # Secrets: GAIA_HUB_TOKEN (Agent Hub Bearer publish token, defined as # an ENVIRONMENT secret on agent-publish, NOT a repo secret). -# Variables: GAIA_HUB_BASE_URL (default https://hub.amd-gaia.ai), -# GAIA_HUB_PUBLISH_URL (the Worker's workers.dev origin -- large uploads +# Variables: GAIA_HUB_BASE_URL (default https://hub.amd-gaia.ai — downloads + the lock +# baseUrl), GAIA_HUB_PUBLISH_URL (the Worker's workers.dev origin -- large uploads # are blocked on the proxied custom domain's managed WAF; a wheel is # small so this matters less than for email's binaries, but the Worker -# endpoint is the same one either way). +# endpoint is the same one either way). REQUIRED for publishing — the +# release fails loudly if it is unset (no silent fallback). # # Tag namespace: `agent-pkg-chat-*` (NOT `v*` -- avoids firing publish.yml / # the paused publish_agents.yml). Example: @@ -284,6 +285,20 @@ jobs: exit 1 fi + - name: Assert hub publish URL present + if: steps.dry.outputs.dry_run == 'false' + shell: bash + # Variable goes through env (never inlined into the script text) so a + # value with shell metacharacters can't break parsing. + env: + GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} + run: | + set -euo pipefail + if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + exit 1 + fi + - name: Install requests + PyYAML (publish_to_r2.py deps) if: steps.dry.outputs.dry_run == 'false' run: python -m pip install --upgrade requests pyyaml @@ -296,7 +311,6 @@ jobs: # the command line or disk. AGENT_HUB_PUBLISH_TOKEN: ${{ secrets.GAIA_HUB_TOKEN }} GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} - GAIA_HUB_BASE_URL: ${{ vars.GAIA_HUB_BASE_URL }} run: | set -euo pipefail whl="$(find dist -maxdepth 1 -name '*.whl' | head -n1)" @@ -313,7 +327,7 @@ jobs: # only special-cases 'email-agent-*'/'.zip' filenames — a bare # gaia_agent_chat-*.whl has no inferrable prefix. python hub/agents/email/python/packaging/publish_to_r2.py \ - --base-url "${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-https://hub.amd-gaia.ai}}" \ + --base-url "${GAIA_HUB_PUBLISH_URL}" \ --manifest "${MANIFEST}" \ --artifact "${whl}=wheel" \ --readme "${README}" \ diff --git a/.github/workflows/release_agent_email.yml b/.github/workflows/release_agent_email.yml index adf1bf344..a3d966e0d 100644 --- a/.github/workflows/release_agent_email.yml +++ b/.github/workflows/release_agent_email.yml @@ -62,8 +62,9 @@ # GAIA_HUB_PUBLISH_URL — origin CI POSTs uploads to. MUST be the Worker's # workers.dev URL: the free-plan managed WAF on the # proxied hub.amd-gaia.ai custom domain blocks large -# binary uploads, but not GET downloads. Unset → -# falls back to GAIA_HUB_BASE_URL (uploads will 403). +# binary uploads, but not GET downloads. REQUIRED — +# the release fails loudly if it is unset (no silent +# fallback). # npm trusted publisher for @amd-gaia/agent-email registered against THIS # workflow file name: release_agent_email.yml (repo amd/gaia). The OIDC subject # is tied to the exact filename — renaming this file breaks publish. @@ -522,6 +523,19 @@ jobs: exit 1 fi + - name: Assert hub publish URL present + shell: bash + # Variable goes through env (never inlined into the script text) so a + # value with shell metacharacters can't break parsing. + env: + GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} + run: | + set -euo pipefail + if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + exit 1 + fi + - name: Download all platform artifacts uses: actions/download-artifact@v8 with: @@ -593,10 +607,9 @@ jobs: # blocks large binary multipart uploads (the POST /publish path). # workers.dev is a separate zone with no such ruleset and hits the same # Worker + bucket. Downloads and the lock baseUrl stay on - # hub.amd-gaia.ai (GETs aren't blocked). Falls back to the public origin - # if the var is unset. + # hub.amd-gaia.ai (GETs aren't blocked). Required — the release fails + # loudly if the variable is unset. GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} - GAIA_HUB_BASE_URL: ${{ vars.GAIA_HUB_BASE_URL }} run: | set -euo pipefail args=() @@ -632,7 +645,7 @@ jobs: capability_matrix_args+=(--capability-matrix "${CAPABILITY_MATRIX}") fi python hub/agents/email/python/packaging/publish_to_r2.py \ - --base-url "${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-https://hub.amd-gaia.ai}}" \ + --base-url "${GAIA_HUB_PUBLISH_URL}" \ --manifest "${MANIFEST}" \ --readme "${README}" \ --changelog "${CHANGELOG}" \ @@ -692,7 +705,6 @@ jobs: env: AGENT_HUB_PUBLISH_TOKEN: ${{ secrets.GAIA_HUB_TOKEN }} GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} - GAIA_HUB_BASE_URL: ${{ vars.GAIA_HUB_BASE_URL }} run: | set -euo pipefail VER="${{ steps.ver.outputs.version }}" @@ -714,7 +726,7 @@ jobs: echo "=== package contents ===" && (cd "${STAGE}" && find . -type f | sort) # Publish the zip as the version's package artifact + its file listing. python hub/agents/email/python/packaging/publish_to_r2.py \ - --base-url "${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-https://hub.amd-gaia.ai}}" \ + --base-url "${GAIA_HUB_PUBLISH_URL}" \ --manifest "${MANIFEST}" \ --artifact "agent-email-${VER}.zip=package" \ --package-files package-files.json diff --git a/.github/workflows/release_agent_gaia.yml b/.github/workflows/release_agent_gaia.yml index 86fda0938..f35a64496 100644 --- a/.github/workflows/release_agent_gaia.yml +++ b/.github/workflows/release_agent_gaia.yml @@ -110,8 +110,9 @@ # GAIA_HUB_PUBLISH_URL — origin CI POSTs uploads to. MUST be the Worker's # workers.dev URL: the free-plan managed WAF on the # proxied hub.amd-gaia.ai custom domain blocks large -# binary uploads, but not GET downloads. Unset → -# falls back to GAIA_HUB_BASE_URL (uploads 403). +# binary uploads, but not GET downloads. REQUIRED — +# the release fails loudly if it is unset (no silent +# fallback). # npm trusted publisher for @amd-gaia/gaia registered against THIS workflow # file name: release_agent_gaia.yml (repo amd/gaia). The OIDC subject is tied # to the exact filename — RENAMING THIS FILE BREAKS THE npm PUBLISH. @@ -774,6 +775,19 @@ jobs: exit 1 fi + - name: Assert hub publish URL present + shell: bash + # Variable goes through env (never inlined into the script text) so a + # value with shell metacharacters can't break parsing. + env: + GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} + run: | + set -euo pipefail + if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + exit 1 + fi + - name: Download sidecar artifacts uses: actions/download-artifact@v8 with: @@ -925,7 +939,7 @@ jobs: # Fail here instead, where the error says to bump the version. Drop # this flag if the lock is ever switched to --summary-out. python "${PACKAGING}/publish_to_r2.py" \ - --base-url "${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-https://hub.amd-gaia.ai}}" \ + --base-url "${GAIA_HUB_PUBLISH_URL}" \ --manifest "${MANIFEST}" \ --strict-immutable \ "${doc_args[@]}" \ diff --git a/.github/workflows/release_components.yml b/.github/workflows/release_components.yml index 2e35bfd20..413d3e1b6 100644 --- a/.github/workflows/release_components.yml +++ b/.github/workflows/release_components.yml @@ -254,16 +254,15 @@ jobs: - name: Verify the deployed build is live env: GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} - GAIA_HUB_BASE_URL: ${{ vars.GAIA_HUB_BASE_URL }} run: | set -euo pipefail # No default. Falling back to the custom domain would verify a # DIFFERENT origin than the publish jobs upload to (they go through # workers.dev because the WAF fronts hub.amd-gaia.ai), so a silent # default here reports freshness for something we are not publishing to. - base="${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-}}" + base="${GAIA_HUB_PUBLISH_URL:-}" if [ -z "${base}" ]; then - echo "::error::neither GAIA_HUB_PUBLISH_URL nor GAIA_HUB_BASE_URL resolved for this job. Both must be REPOSITORY variables — an environment-scoped one resolves empty here, and this check would then target the custom domain rather than the origin the publish jobs POST to." + echo "::error::GAIA_HUB_PUBLISH_URL resolved empty for this job. It must be a REPOSITORY variable — an environment-scoped one resolves empty here, and this check would then target the custom domain rather than the origin the publish jobs POST to." exit 1 fi for i in 1 2 3 4 5 6; do @@ -369,6 +368,17 @@ jobs: exit 1 fi + - name: Require the publish URL + if: needs.version.outputs.dry_run == 'false' + env: + GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} + run: | + set -euo pipefail + if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + exit 1 + fi + - name: Install publisher deps if: needs.version.outputs.dry_run == 'false' run: python -m pip install --upgrade requests pyyaml boto3 @@ -379,7 +389,6 @@ jobs: env: AGENT_HUB_PUBLISH_TOKEN: ${{ secrets.GAIA_HUB_TOKEN }} GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} - GAIA_HUB_BASE_URL: ${{ vars.GAIA_HUB_BASE_URL }} # Artifacts at/over 90 MiB cannot travel through the Worker at all # (Cloudflare caps request bodies at 100 MB on Free/Pro), so the # publisher PUTs them straight into R2 and publishes them by @@ -392,7 +401,7 @@ jobs: # Explicit = on every artifact: the publisher's # inference only special-cases email-agent-* filenames. python hub/agents/email/python/packaging/publish_to_r2.py \ - --base-url "${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-https://hub.amd-gaia.ai}}" \ + --base-url "${GAIA_HUB_PUBLISH_URL}" \ --manifest "${TERMINAL_HUB_MANIFEST}" \ --artifact "bin/gaia-linux-x64=linux-x64" \ --artifact "bin/gaia-linux-arm64=linux-arm64" \ @@ -456,6 +465,17 @@ jobs: exit 1 fi + - name: Require the publish URL + if: needs.version.outputs.dry_run == 'false' + env: + GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} + run: | + set -euo pipefail + if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + exit 1 + fi + - name: Wait for the installer assets on the release if: needs.version.outputs.dry_run == 'false' shell: bash @@ -512,7 +532,6 @@ jobs: env: AGENT_HUB_PUBLISH_TOKEN: ${{ secrets.GAIA_HUB_TOKEN }} GAIA_HUB_PUBLISH_URL: ${{ vars.GAIA_HUB_PUBLISH_URL }} - GAIA_HUB_BASE_URL: ${{ vars.GAIA_HUB_BASE_URL }} # Artifacts at/over 90 MiB cannot travel through the Worker at all # (Cloudflare caps request bodies at 100 MB on Free/Pro), so the # publisher PUTs them straight into R2 and publishes them by @@ -527,7 +546,7 @@ jobs: # for apt users — publishing it as a second linux-x64 object would # make the catalog's per-platform download ambiguous. python hub/agents/email/python/packaging/publish_to_r2.py \ - --base-url "${GAIA_HUB_PUBLISH_URL:-${GAIA_HUB_BASE_URL:-https://hub.amd-gaia.ai}}" \ + --base-url "${GAIA_HUB_PUBLISH_URL}" \ --manifest "${AGENT_UI_MANIFEST}" \ --artifact "dist/gaia-agent-ui-${VERSION}-x64-setup.exe=win-x64" \ --artifact "dist/gaia-agent-ui-${VERSION}-arm64.dmg=darwin-arm64" \ From 8189110fc93e6d69dc0c4dfb413a88cbbed00568 Mon Sep 17 00:00:00 2001 From: kagura-agent Date: Fri, 21 Aug 2026 14:23:08 +0800 Subject: [PATCH 2/3] ci(release): clarify GAIA_HUB_PUBLISH_URL remediation hint per review The 'an environment-scoped one resolves empty here' hint was copied from the worker-deploy job (environment: worker-deploy), where a variable scoped to agent-publish genuinely resolves empty. The publish jobs declare environment: agent-publish, where an environment-scoped variable does resolve, so the hint sent maintainers looking in the wrong place. Point to both valid locations instead. Also trim the chat workflow header: chat publishes a wheel with no binaries.lock.json, so GAIA_HUB_BASE_URL is a downloads-origin only there. --- .github/workflows/release_agent_chat.yml | 6 +++--- .github/workflows/release_agent_email.yml | 2 +- .github/workflows/release_agent_gaia.yml | 2 +- .github/workflows/release_components.yml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release_agent_chat.yml b/.github/workflows/release_agent_chat.yml index 14ee75c87..12b75eb98 100644 --- a/.github/workflows/release_agent_chat.yml +++ b/.github/workflows/release_agent_chat.yml @@ -55,8 +55,8 @@ # restricted to `main` AND the `agent-pkg-*` tag pattern). # Secrets: GAIA_HUB_TOKEN (Agent Hub Bearer publish token, defined as # an ENVIRONMENT secret on agent-publish, NOT a repo secret). -# Variables: GAIA_HUB_BASE_URL (default https://hub.amd-gaia.ai — downloads + the lock -# baseUrl), GAIA_HUB_PUBLISH_URL (the Worker's workers.dev origin -- large uploads +# Variables: GAIA_HUB_BASE_URL (default https://hub.amd-gaia.ai — downloads), +# GAIA_HUB_PUBLISH_URL (the Worker's workers.dev origin -- large uploads # are blocked on the proxied custom domain's managed WAF; a wheel is # small so this matters less than for email's binaries, but the Worker # endpoint is the same one either way). REQUIRED for publishing — the @@ -295,7 +295,7 @@ jobs: run: | set -euo pipefail if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then - echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Set it as a repository variable, or as an environment variable on the agent-publish environment. See workers/agent-hub/README.md." exit 1 fi diff --git a/.github/workflows/release_agent_email.yml b/.github/workflows/release_agent_email.yml index a3d966e0d..7787c8ae5 100644 --- a/.github/workflows/release_agent_email.yml +++ b/.github/workflows/release_agent_email.yml @@ -532,7 +532,7 @@ jobs: run: | set -euo pipefail if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then - echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Set it as a repository variable, or as an environment variable on the agent-publish environment. See workers/agent-hub/README.md." exit 1 fi diff --git a/.github/workflows/release_agent_gaia.yml b/.github/workflows/release_agent_gaia.yml index f35a64496..90dd48dea 100644 --- a/.github/workflows/release_agent_gaia.yml +++ b/.github/workflows/release_agent_gaia.yml @@ -784,7 +784,7 @@ jobs: run: | set -euo pipefail if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then - echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Set it as a repository variable, or as an environment variable on the agent-publish environment. See workers/agent-hub/README.md." exit 1 fi diff --git a/.github/workflows/release_components.yml b/.github/workflows/release_components.yml index 413d3e1b6..083befbfd 100644 --- a/.github/workflows/release_components.yml +++ b/.github/workflows/release_components.yml @@ -375,7 +375,7 @@ jobs: run: | set -euo pipefail if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then - echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Set it as a repository variable, or as an environment variable on the agent-publish environment. See workers/agent-hub/README.md." exit 1 fi @@ -472,7 +472,7 @@ jobs: run: | set -euo pipefail if [ -z "${GAIA_HUB_PUBLISH_URL:-}" ]; then - echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Publishing without it would silently target a hardcoded fallback URL. Set it as a REPOSITORY variable (an environment-scoped one resolves empty here). See workers/agent-hub/README.md." + echo "::error::missing repository variable GAIA_HUB_PUBLISH_URL — the Worker's workers.dev origin CI POSTs uploads to (the proxied hub.amd-gaia.ai custom domain's managed WAF blocks large uploads). Set it as a repository variable, or as an environment variable on the agent-publish environment. See workers/agent-hub/README.md." exit 1 fi From 7bc1cf76dd762df3bd9d9454851c88ee9c6530f4 Mon Sep 17 00:00:00 2001 From: kagura-agent Date: Sat, 22 Aug 2026 06:47:07 +0800 Subject: [PATCH 3/3] =?UTF-8?q?docs(ci):=20correct=20hub=20publish=20ratio?= =?UTF-8?q?nale=20per=20review=20=E2=80=94=20one=20Worker,=20two=20doors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The publish URL is not a second hub: hub.amd-gaia.ai and the workers.dev URL are two front doors onto the same Worker + R2 bucket. The managed WAF fronting the custom domain 403s large uploads, so POST /publish goes through the workers.dev origin; a publish through the WAF-fronted door fails loudly with a 403, it does not land somewhere else. Rewrite the GAIA_HUB_PUBLISH_URL variable docs in the three agent release workflow headers to say this, and add a 'Publishing origins (one Worker, two doors)' section to workers/agent-hub/README.md covering both variables and why they are repository-level. --- .github/workflows/release_agent_chat.yml | 5 +++-- .github/workflows/release_agent_email.yml | 9 +++++--- .github/workflows/release_agent_gaia.yml | 9 +++++--- workers/agent-hub/README.md | 26 +++++++++++++++++++++++ 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release_agent_chat.yml b/.github/workflows/release_agent_chat.yml index 12b75eb98..680b8e1c2 100644 --- a/.github/workflows/release_agent_chat.yml +++ b/.github/workflows/release_agent_chat.yml @@ -59,8 +59,9 @@ # GAIA_HUB_PUBLISH_URL (the Worker's workers.dev origin -- large uploads # are blocked on the proxied custom domain's managed WAF; a wheel is # small so this matters less than for email's binaries, but the Worker -# endpoint is the same one either way). REQUIRED for publishing — the -# release fails loudly if it is unset (no silent fallback). +# endpoint is the same one either way — same Worker + R2 bucket, just the +# door that isn't WAF-blocked). REQUIRED for publishing — the release +# fails loudly if it is unset (no silent fallback). # # Tag namespace: `agent-pkg-chat-*` (NOT `v*` -- avoids firing publish.yml / # the paused publish_agents.yml). Example: diff --git a/.github/workflows/release_agent_email.yml b/.github/workflows/release_agent_email.yml index 7787c8ae5..b81736a76 100644 --- a/.github/workflows/release_agent_email.yml +++ b/.github/workflows/release_agent_email.yml @@ -60,9 +60,12 @@ # GAIA_HUB_BASE_URL — public Worker origin for downloads + the lock # baseUrl, default https://hub.amd-gaia.ai. # GAIA_HUB_PUBLISH_URL — origin CI POSTs uploads to. MUST be the Worker's -# workers.dev URL: the free-plan managed WAF on the -# proxied hub.amd-gaia.ai custom domain blocks large -# binary uploads, but not GET downloads. REQUIRED — +# workers.dev URL: the managed WAF fronting the +# hub.amd-gaia.ai custom domain 403s large binary +# uploads, so POST /publish goes through the +# workers.dev origin instead — same Worker + R2 +# bucket, just the door that isn't WAF-blocked +# (GET downloads stay on hub.amd-gaia.ai). REQUIRED — # the release fails loudly if it is unset (no silent # fallback). # npm trusted publisher for @amd-gaia/agent-email registered against THIS diff --git a/.github/workflows/release_agent_gaia.yml b/.github/workflows/release_agent_gaia.yml index 90dd48dea..6ce9ccc58 100644 --- a/.github/workflows/release_agent_gaia.yml +++ b/.github/workflows/release_agent_gaia.yml @@ -108,9 +108,12 @@ # GAIA_HUB_BASE_URL — public Worker origin for downloads + the lock # baseUrl. Default https://hub.amd-gaia.ai. # GAIA_HUB_PUBLISH_URL — origin CI POSTs uploads to. MUST be the Worker's -# workers.dev URL: the free-plan managed WAF on the -# proxied hub.amd-gaia.ai custom domain blocks large -# binary uploads, but not GET downloads. REQUIRED — +# workers.dev URL: the managed WAF fronting the +# hub.amd-gaia.ai custom domain 403s large binary +# uploads, so POST /publish goes through the +# workers.dev origin instead — same Worker + R2 +# bucket, just the door that isn't WAF-blocked +# (GET downloads stay on hub.amd-gaia.ai). REQUIRED — # the release fails loudly if it is unset (no silent # fallback). # npm trusted publisher for @amd-gaia/gaia registered against THIS workflow diff --git a/workers/agent-hub/README.md b/workers/agent-hub/README.md index 09969586c..f9138c391 100644 --- a/workers/agent-hub/README.md +++ b/workers/agent-hub/README.md @@ -287,6 +287,32 @@ checked into the repo: `MAX_ARTIFACT_BYTES` (a plain var, default 250 MiB) caps artifact size and can be overridden per environment without a secret. +### Publishing origins (one Worker, two doors) + +There is exactly **one Agent Hub Worker and one R2 bucket** — `hub.amd-gaia.ai` +and the `workers.dev` URL are two front doors onto the same Worker +(`workers/agent-hub/wrangler.toml`): the custom domain is the user-facing +download door, and the `workers.dev` origin is the CI upload door. The managed +WAF fronting the custom domain **403s large uploads** (the `POST /publish` +path), so CI publishes through the `workers.dev` origin, which has no such +ruleset and hits the same Worker + bucket. A publish through the wrong door +fails loudly at the WAF — it does not land somewhere else. + +CI uses two repository variables (set at **repository** level, not environment +level — the version jobs have no `environment:` and an environment-scoped +variable would resolve empty and silently fall back to the hardcode): + +| Variable | Value | Purpose | +|---|---|---| +| `GAIA_HUB_BASE_URL` | `https://hub.amd-gaia.ai` | Downloads + the lock `baseUrl` (GETs aren't WAF-blocked) | +| `GAIA_HUB_PUBLISH_URL` | `.workers.dev` | The origin CI POSTs uploads to. **Required** — the release fails loudly if unset (no silent fallback to a hardcoded URL) | + +The publish jobs (`release_agent_*.yml`, `release_components.yml`) now assert +`GAIA_HUB_PUBLISH_URL` is set before publishing, mirroring the existing +`GAIA_HUB_TOKEN` asserts. A missing variable is a startup-time `::error::` +naming what is missing and where to set it, not a 403 halfway through a +release. + ## Publishing artifacts larger than 100 MB A Worker request body is capped by the Cloudflare **account plan** — 100 MB on