ci(reusables): gate Radicle mirror + Instant Sync dispatch on secret presence (auto-fixes 65 repos)#305
Merged
Conversation
…presence ## Two source-level fixes — zero fan-out needed ### mirror-reusable.yml: mirror-radicle Adds `if: secrets.RADICLE_KEY != ''` to Setup Rust / Install Radicle / Mirror to Radicle steps + `mkdir -p ~/.radicle/keys` before the key write (previously `echo > ~/.radicle/keys/radicle` errored with "No such file or directory" on a never-created dir). Adds a final advisory "Skipped" step on the inverse gate so the workflow prints a one-line :::notice instead of failing. Caught **26 estate repos** on the 2026-05-30 audit. Net effect: repos with RADICLE_MIRROR_ENABLED=true but no RADICLE_KEY secret get a green informational notice instead of a red "No such file" failure on every push. ### instant-sync.yml: dispatch Adds `if: secrets.FARM_DISPATCH_TOKEN != ''` to the Trigger Propagation step + an inverse :::notice step. Without the PAT, `peter-evans/repository-dispatch` cannot dispatch cross-repo and returns HTTP 401 — failing every push to every repo where the secret has not been propagated. Caught **39 estate repos** on the 2026-05-30 audit. Net effect: repos without the org-level PAT get a green notice instead of a red 401. ## Why source-level Both reusables are called via `secrets: inherit` from ~289 mirror.yml + instant-sync.yml caller wrappers across the estate. A source-level gate auto-resolves all callers — no per-repo fan-out PR needed. ## Owner action for full activation - mirror-radicle: configure `RADICLE_KEY` (per-repo or org-level secret); the gate then unblocks the existing mirror flow. - instant-sync: configure `FARM_DISPATCH_TOKEN` (org-level PAT with repo scope); the gate then unblocks the cross-repo dispatch. This PR makes those owner actions OPTIONAL — without the secret the workflow is no-op; with the secret it works. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath
added a commit
to hyperpolymath/hypatia
that referenced
this pull request
May 30, 2026
…gate (#396) ## Summary Forward-detection rule for the pattern just root-fixed in hyperpolymath/standards#305 (Mirror radicle + Instant Sync secret-presence gates, 65 estate repos auto-fixed). ## What WF017 detects A workflow step that: - `uses:` one of `@secret_consuming_actions` (curated, data-driven) - reads `\${{ secrets.X }}` as its primary input - lacks an `if: secrets.X != ''` gate (on the step or as an enclosing wrapper) ## Curated action list (initial) | Action | Primary secret param | |---|---| | `webfactory/ssh-agent` | `ssh-private-key` | | `peter-evans/repository-dispatch` | `token` | | `peter-evans/create-pull-request` | `token` | | `actions-ecosystem/action-create-comment` | `github_token` | Add new entries to `@secret_consuming_actions` (no code change). ## Sensitivity / specificity smoke tests | Case | Expected | Result | |---|---|---| | Ungated `webfactory/ssh-agent` | fires | ✓ | | Gated `webfactory/ssh-agent` (`- if: secrets.X != ''`) | silent | ✓ | | Ungated `peter-evans/repository-dispatch` | fires | ✓ | | Gated `peter-evans/repository-dispatch` | silent | ✓ | | Generic `actions/checkout` (not in list) | silent | ✓ | The gate regex handles both step-leading `- if:` and interior `if:`, both quote styles, and inline-`\${{ … }}` expressions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🔍 Hypatia Security ScanFindings: 190 issues detected
View findings[
{
"reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
"type": "unpinned_action",
"file": "governance.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in affinescript-verify.yml",
"type": "missing_timeout_minutes",
"file": "affinescript-verify.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in boj-build.yml",
"type": "missing_timeout_minutes",
"file": "boj-build.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in casket-pages.yml",
"type": "missing_timeout_minutes",
"file": "casket-pages.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in casket-pages.yml",
"type": "missing_timeout_minutes",
"file": "casket-pages.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in changelog-reusable.yml",
"type": "missing_timeout_minutes",
"file": "changelog-reusable.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in codeql-reusable.yml",
"type": "missing_timeout_minutes",
"file": "codeql-reusable.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in codeql.yml",
"type": "missing_timeout_minutes",
"file": "codeql.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in deno-ci-reusable.yml",
"type": "missing_timeout_minutes",
"file": "deno-ci-reusable.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in doc-format.yml",
"type": "missing_timeout_minutes",
"file": "doc-format.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two source-level fixes in shared workflows. Zero fan-out PRs needed — both fixes auto-propagate to every caller via the existing reusable/standalone patterns.
Fix 1:
mirror-reusable.ymlmirror-radicle (26 repos auto-fixed)Root cause: mirror-radicle's three working steps fired unconditionally when
vars.RADICLE_MIRROR_ENABLED == 'true', even whensecrets.RADICLE_KEYwas empty. Theecho > ~/.radicle/keys/radiclewrite also failed because the parent directory~/.radicle/keys/doesn't exist by default.Fix:
if: \${{ secrets.RADICLE_KEY != '' }}on Setup Rust / Install Radicle / Mirror to Radicle stepsmkdir -p ~/.radicle/keysbefore the key write::noticeinstead of failingFix 2:
instant-sync.ymldispatch (39 repos auto-fixed)Root cause:
peter-evans/repository-dispatchran unconditionally on every push; withoutsecrets.FARM_DISPATCH_TOKENthe action falls back toGITHUB_TOKEN, which cannot dispatch cross-repo and returns HTTP 401 "Bad credentials".Fix:
if: \${{ secrets.FARM_DISPATCH_TOKEN != '' }}on the Trigger Propagation step::noticeadvisory stepWhy source-level
Both files are imported via
workflow_call/ direct-paste templates across ~289 estate caller wrappers. Source-level gate = automatic per-repo fix.Owner action for full activation (optional)
RADICLE_KEY(per-repo or org-level secret).FARM_DISPATCH_TOKENPAT with repo scope.This PR makes those owner actions optional — without the secret the workflow is no-op; with the secret it works.
🤖 Generated with Claude Code