Skip to content

ci(sync): skip the CNB mirror when its token secret is absent - #7016

Open
asto18089 wants to merge 3 commits into
codewhale-hq:mainfrom
asto18089:up/cnb-sync-optional-secret
Open

asto18089 wants to merge 3 commits into
codewhale-hq:mainfrom
asto18089:up/cnb-sync-optional-secret

Conversation

@asto18089

@asto18089 asto18089 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What

sync fails on every push to a mirrored branch when the CNB_GIT_TOKEN repository secret is absent: "CNB_GIT_TOKEN secret is not set; cannot push to CNB." That is a repository-secret state, not a property of the commit — forks can never inherit the secret, so they report a red check no contributor can fix.

A credentials gate job probes the secret and skips the push when it is absent, emitting a notice that names the missing secret. Configured repositories keep the previous behavior, including the existing hard failure inside the push step, which still covers a caller pinned to an older revision of this workflow plus the window where the secret is deleted between the gate's probe and the push step.

docs/CNB_MIRROR.md documents the skip path and distinguishes "skipped because absent or misspelled" from "failed because expired" in the rotation section.

Tests

  • Workflow-parse level: jobs credentials + sync, sync.if = needs.credentials.outputs.configured == 'true' (verified locally; CI exercises the rest).
  • The skip path is exercised by forks of the repository, where the secret cannot exist.

Co-authored with @zhuowp (original patch in the Pinvou/CodeWhale fork, #64); adapted to this workflow's retry/concurrency/workflow_call plumbing.

No-Issue: CI-only; forks cannot inherit CNB_GIT_TOKEN, so Sync to CNB fails on every fork push

`sync` fails on every push to a mirrored branch with "CNB_GIT_TOKEN secret
is not set; cannot push to CNB". That is a repository-secret state, not a
property of the commit: forks cannot inherit the secret at all, so they can
never make the check green, and an unconfigured repository reports a red
check no contributor can fix.

Probe the secret in a `credentials` gate job and skip the push when it is
absent, with a notice naming the missing secret. Configured repositories
keep the previous behavior, including the existing hard failure inside the
push step, which still covers a caller pinned to an older revision of this
workflow plus the window where the secret is deleted between the gate's
probe and the push step. docs/CNB_MIRROR.md documents the skip path and
distinguishes "skipped because absent or misspelled" from "failed because
expired" in the rotation section.

Co-authored-by: zhuowp <9426526+zhuowp@users.noreply.github.com>
Signed-off-by: asto <asto18089@126.com>
@asto18089
asto18089 requested a review from Hmbown as a code owner October 18, 2026 02:06

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@github-actions github-actions Bot added the contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm label Oct 18, 2026
Hmbown pushed a commit that referenced this pull request Oct 18, 2026
#7016, #7018 and #7019 from @asto18089 carry a Co-authored-by trailer for
@zhuowp (the work was ported from a fork they contributed to).
scripts/check-contributor-credit.py, run as the per-PR "Version drift"
job, fails those PRs because @zhuowp is absent from
web/lib/release-credits.ts. docs/CONTRIBUTORS.md and CHANGELOG.md already
credit them. This adds the handle to UNRELEASED_CONTRIBUTORS, which the
release cut moves into the release block.

Refs #7016
Refs #7018
Refs #7019

Verified: python3 scripts/check-contributor-credit.py ends "Every
contributor in the window is credited on all three surfaces";
web check-facts OK; npx tsc --noEmit OK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
@Hmbown

Hmbown commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator

🤖 This reply is from Claude Opus 5.5, an AI agent working on behalf of the Codewhale maintainers. It is posted through @Hmbown's account, but Hunter did not write it.

Thanks @asto18089. The fork failure is real: your run 35319246671 shows every fork push going red on CNB_GIT_TOKEN, and no contributor can fix that.

One change before merge. Gating on "is the secret set" also covers this repository. Here, a deleted or misspelled CNB_GIT_TOKEN would turn the main mirror, and the release-tag mirror that release.yml calls, into a green run with only a notice. AGENTS.md asks for misconfiguration to fail loud, and release.yml already reverted the same silent skip for Homebrew. Forks are what this PR is for, and they can never inherit the secret. So gate on the owner instead:

jobs:
  sync:
    if: github.repository_owner == 'codewhale-hq'
    runs-on: ubuntu-latest
  • Drop the credentials job and the two new comment blocks. The existing hard failure on an empty token stays, for this repository.
  • Docs: in both CNB_MIRROR files, say that forks skip the job and that a missing secret here still fails. Drop the new "skipped rather than failing" paragraph under Rotating.

I built that commit (f99e0fdb24): it nets to a one-line gate against main. actionlint is clean, and release-workflows.test.js passes on a main snapshot carrying it. Pushing it to up/cnb-sync-optional-secret was refused (HTTP 403), even with "allow edits by maintainers" on. Could you apply it? Alternatively, check that maintainer edits are allowed for workflow files on your fork, and I'll push it.

I added a No-Issue: line to the body for the "PR closes an issue" check. I also added @zhuowp to web/lib/release-credits.ts on main (88edb22). Your co-author trailer for them was failing "Version drift" on this PR and two others.

— Written by Claude Opus 5.5 (AI agent) on behalf of the maintainers.

@Hmbown

Hmbown commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

🤖 This reply is from Claude Opus 5.5, an AI agent working on behalf of the Codewhale maintainers. It is posted through @Hmbown's account, but Hunter did not write it.

Follow-up to the review above, @asto18089: I couldn't push the change to your branch (GitHub refuses maintainer pushes that touch .github/workflows/ on a fork), and the commit I named earlier only exists locally, so you couldn't fetch it. Here is the full patch against your current head a1331ee8. From the repository root, git apply it, commit and push:

Patch (3 files, +8/−50)
diff --git a/.github/workflows/sync-cnb.yml b/.github/workflows/sync-cnb.yml
index fcd4d113da..37c07ba92b 100644
--- a/.github/workflows/sync-cnb.yml
+++ b/.github/workflows/sync-cnb.yml
@@ -61,31 +61,8 @@ permissions:
   contents: read
 
 jobs:
-  # The mirror is optional egress, and the token is a repository secret. On a
-  # fork — or on this repository before the secret is configured — the push
-  # cannot succeed no matter what the commit contains, so probe first and skip
-  # instead of failing a check no contributor can fix. `secrets` is unavailable
-  # in a job-level `if`, hence the output hand-off.
-  credentials:
-    runs-on: ubuntu-latest
-    outputs:
-      configured: ${{ steps.probe.outputs.configured }}
-    steps:
-      - id: probe
-        env:
-          CNB_TOKEN: ${{ secrets.CNB_GIT_TOKEN }}
-        shell: bash
-        run: |
-          if [ -n "${CNB_TOKEN:-}" ]; then
-            echo "configured=true" >> "${GITHUB_OUTPUT}"
-          else
-            echo "configured=false" >> "${GITHUB_OUTPUT}"
-            echo "::notice::CNB_GIT_TOKEN is not set for this repository; skipping the CNB mirror push."
-          fi
-
   sync:
-    needs: credentials
-    if: needs.credentials.outputs.configured == 'true'
+    if: github.repository_owner == 'codewhale-hq'
     runs-on: ubuntu-latest
     timeout-minutes: 15
     steps:
@@ -105,11 +82,6 @@ jobs:
           set -euo pipefail
 
           if [ -z "${CNB_TOKEN:-}" ]; then
-            # Whenever this revision of the workflow runs, the gate above has
-            # already skipped this job. This guard still covers runs that carry
-            # an older revision of this file (a push from a branch that
-            # predates the gate), plus the narrow window where the secret is
-            # deleted between the gate's probe and this step.
             echo "::error::CNB_GIT_TOKEN secret is not set; cannot push to CNB." >&2
             exit 1
           fi
diff --git a/docs/CNB_MIRROR.md b/docs/CNB_MIRROR.md
index 077a8bf196..44298307c1 100644
--- a/docs/CNB_MIRROR.md
+++ b/docs/CNB_MIRROR.md
@@ -53,12 +53,10 @@ GitHub Actions workflow:
   recovery. A tag push alone does not mirror a release tag. Manual tag recovery
   also verifies the complete public GitHub asset inventory and immutable source.
 - **Auth:** HTTPS basic auth as user `cnb` with the `CNB_GIT_TOKEN`
-  repository secret as the password. When the secret is absent (a fork, or
-  this repository before it was configured — Actions secrets never inherit
-  across a fork), the `credentials` gate job reports `configured=false`, the
-  `sync` job is **skipped** rather than failed, and the run emits a notice
-  naming the missing secret: the mirror is optional egress, so an
-  unconfigured repository must not report a red check no contributor can fix.
+  repository secret as the password. Forks never inherit the secret, so the
+  `sync` job runs only when the repository owner is `codewhale-hq`; on a fork
+  it is skipped instead of failing. In this repository a missing or misspelled
+  secret still fails the push step.
 - **Scope:** only the ref that triggered the run is pushed. Tag pushes
   push exactly that tag. Branch pushes mirror `main`, first-party
   `fix/*`/`rebrand/*` branches, or explicitly matched release branches. Other
@@ -162,12 +160,6 @@ release assets exist before directing users to CNB.
 
 ## Rotating `CNB_GIT_TOKEN`
 
-If a run is *skipped* rather than failing, with the notice
-`CNB_GIT_TOKEN is not set for this repository`, the secret is absent (or
-misspelled) instead of expired — configure it as below; there is no red check
-to clear. An expired or invalid secret still fails the push step with an auth
-error, which is the signal this section is about.
-
 If the workflow starts failing with auth errors and the token has
 expired:
 
diff --git a/docs/zh_hans/CNB_MIRROR.md b/docs/zh_hans/CNB_MIRROR.md
index 23804fc1df..90135e380c 100644
--- a/docs/zh_hans/CNB_MIRROR.md
+++ b/docs/zh_hans/CNB_MIRROR.md
@@ -48,10 +48,9 @@ sha256sum -c codewhale-artifacts-sha256.txt --ignore-missing
   `workflow_dispatch`。仅推送标签不会触发发布标签镜像。手动恢复标签也必须
   验证 GitHub 已公开的完整资产清单及不可变的源码提交。
 - **认证:** HTTPS basic auth,用户名为 `cnb`,密码是仓库 secret
-  `CNB_GIT_TOKEN`。secret 缺失时(fork,或本仓库尚未配置——Actions secret
-  永远不会跨 fork 继承),`credentials` 门禁作业报告 `configured=false`,
-  `sync` 作业被**跳过**而非失败,运行会发出指名缺失 secret 的 notice:镜像是
-  可选的出站同步,未配置的仓库不应报告任何贡献者都无法修复的红检查。
+  `CNB_GIT_TOKEN`。fork 永远不会继承这个 secret,所以 `sync` 作业只在仓库所有者为
+  `codewhale-hq` 时运行;在 fork 上会被跳过而不是失败。本仓库中 secret 缺失或拼写
+  错误时,push 步骤仍会失败。
 - **范围:** 只推送触发本次运行的那个 ref。标签推送就推送该标签。分支推送镜像
   `main`、第一方 `fix/*`/`rebrand/*` 分支,或显式匹配的发布分支。其他功能分支
   和 dependabot ref 有意*不*镜像。
@@ -147,11 +146,6 @@ gh workflow run sync-cnb.yml --repo codewhale-hq/CodeWhale --ref vX.Y.Z
 
 ## 轮换 `CNB_GIT_TOKEN`
 
-如果一次运行是被*跳过*而非失败,并带有 notice
-`CNB_GIT_TOKEN is not set for this repository`,说明 secret 缺失(或拼写错误)
-而非过期——按下文配置即可;没有需要清除的红检查。过期或无效的 secret 仍会让
-push 步骤以认证错误失败,那才是本节针对的信号。
-
 如果工作流开始报认证错误,而 token 已经过期:
 
 1. 登录 `cnb.cool`,生成一个带 `repo`(push)权限的新个人访问 token。

With it, sync runs only when the repository owner is codewhale-hq. Forks skip cleanly, which is the failure you hit in run 35319246671, and a missing or misspelled CNB_GIT_TOKEN here still fails loudly. That matters more than it looks: main's Linux test lane runs on CNB, so a silent skip would also hide those tests.

The other red checks aren't yours. Version drift failed on a contributor-credit entry that main has since fixed (88edb22), and the single Linux failure is a timing test in exec_persistent_service that passes on current main. Your next push re-runs CI against current main. Main is held at the qualified v0.10.2 candidate until that tag is cut, so the merge itself may wait until just after the release.

Review asked for an owner gate instead of a secret probe. A
secret-presence gate also swallows misconfiguration in this repository:
a deleted or misspelled CNB_GIT_TOKEN would turn the main mirror, and
the release-tag mirror that release.yml calls, into a green run with
only a notice, while misconfiguration is supposed to fail loud — and
main's Linux test lane runs on CNB, so a silent skip would also hide
those tests.

Forks can never inherit the secret, so the sync job now runs only when
the repository owner is codewhale-hq; the credentials gate job is
dropped. The existing hard failure on an empty token stays for this
repository, and both CNB_MIRROR docs describe the two paths.

Co-authored-by: Hmbown <101357273+Hmbown@users.noreply.github.com>
Signed-off-by: asto <asto18089@126.com>
@asto18089

Copy link
Copy Markdown
Contributor Author

Applied your patch verbatim and pushed — new head is ca4f919 (up/cnb-sync-optional-secret), diff nets to +8/−50 across the three files, matching your patch exactly. The sync job now gates on github.repository_owner == 'codewhale-hq', the credentials job and both comment blocks are gone, and the empty-token hard failure stays for this repository.

Local checks on the patched tree: actionlint .github/workflows/sync-cnb.yml exits 0, and node --test .github/scripts/release-workflows.test.js reports # tests 1 / # pass 1 / # fail 0 (it reads sync-cnb.yml for the workflow_call and inventory-ordering assertions). I also confirmed release.yml's cnb job passes the secret explicitly and reads no gate outputs, so the release-tag mirror is unaffected by the dropped job.

Thanks for the patches and for untangling the Version drift red.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants