From b4d856e523df8ca9f824081cecf6945e1d8f2a9a Mon Sep 17 00:00:00 2001 From: asto Date: Sun, 18 Oct 2026 02:53:32 +0800 Subject: [PATCH 1/2] ci(sync): skip the CNB mirror when its token secret is absent `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 --- .github/workflows/sync-cnb.yml | 29 +++++++++++++++++++++++++++++ docs/CNB_MIRROR.md | 13 ++++++++++++- docs/zh_hans/CNB_MIRROR.md | 10 +++++++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync-cnb.yml b/.github/workflows/sync-cnb.yml index d4bd1222fa..fcd4d113da 100644 --- a/.github/workflows/sync-cnb.yml +++ b/.github/workflows/sync-cnb.yml @@ -61,7 +61,31 @@ 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' runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -81,6 +105,11 @@ 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 4db4c724a9..077a8bf196 100644 --- a/docs/CNB_MIRROR.md +++ b/docs/CNB_MIRROR.md @@ -53,7 +53,12 @@ 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. + 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. - **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 @@ -157,6 +162,12 @@ 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 a02bb30355..23804fc1df 100644 --- a/docs/zh_hans/CNB_MIRROR.md +++ b/docs/zh_hans/CNB_MIRROR.md @@ -48,7 +48,10 @@ sha256sum -c codewhale-artifacts-sha256.txt --ignore-missing `workflow_dispatch`。仅推送标签不会触发发布标签镜像。手动恢复标签也必须 验证 GitHub 已公开的完整资产清单及不可变的源码提交。 - **认证:** HTTPS basic auth,用户名为 `cnb`,密码是仓库 secret - `CNB_GIT_TOKEN`。 + `CNB_GIT_TOKEN`。secret 缺失时(fork,或本仓库尚未配置——Actions secret + 永远不会跨 fork 继承),`credentials` 门禁作业报告 `configured=false`, + `sync` 作业被**跳过**而非失败,运行会发出指名缺失 secret 的 notice:镜像是 + 可选的出站同步,未配置的仓库不应报告任何贡献者都无法修复的红检查。 - **范围:** 只推送触发本次运行的那个 ref。标签推送就推送该标签。分支推送镜像 `main`、第一方 `fix/*`/`rebrand/*` 分支,或显式匹配的发布分支。其他功能分支 和 dependabot ref 有意*不*镜像。 @@ -144,6 +147,11 @@ 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。 From ca4f919cd8cfdaaa73af1f0824279ec234b7a70d Mon Sep 17 00:00:00 2001 From: asto Date: Tue, 20 Oct 2026 09:58:22 +0800 Subject: [PATCH 2/2] ci(sync): gate the CNB mirror on repository owner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/workflows/sync-cnb.yml | 30 +----------------------------- docs/CNB_MIRROR.md | 16 ++++------------ docs/zh_hans/CNB_MIRROR.md | 12 +++--------- 3 files changed, 8 insertions(+), 50 deletions(-) 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。