Skip to content

Repository files navigation

reusable-workflows

Test Test rust Deploy

A collection of reusable workflows for GitHub Actions.

Secrets contract for callers

The four install-capable workflows (test.yml, deploy.yml, release.yml, run-script.yml) declare these optional secrets:

  • VERDACCIO_TOKEN: auth token for the private Verdaccio registry (@willbooster-private/*). When set, the workflow generates git-excluded workspace registry configs before installing dependencies — .npmrc for npm/bun/yarn1 and .yarnrc.yml for Yarn Berry (which ignores .npmrc), so Berry consumers no longer need to reference ${VERDACCIO_TOKEN} from their committed .yarnrc.yml. Every caller should pass it. The generated configs stay on disk for the whole job, but the secret itself is step-scoped: ${VERDACCIO_TOKEN} only expands in the "Install dependencies" step of all four workflows, plus common/ci-setup and "Deploy" in deploy.yml, common/ci-setup and "Run script" in run-script.yml, "Release" in release.yml, and "Test release script" in test.yml. Consumer scripts running on any other step (e.g. test/ci-setup, cleanup, build, tests) see an empty VERDACCIO_TOKEN and must rely on the already-installed dependency graph instead of fetching from the private registry at run time.
  • TAKUMI_GUARD_TOKEN: auth token for the Takumi Guard registry proxy, which blocks known-malicious packages. When set, the generated registry configs (.npmrc for npm/bun/yarn1, .yarnrc.yml for Yarn Berry) route newly resolved public (default-registry) packages through https://npm.flatt.tech/ for every package manager; @willbooster-private/* packages keep resolving from Verdaccio with VERDACCIO_TOKEN. The token follows the same step-scoping as VERDACCIO_TOKEN. While either registry token is present, the generated .yarnrc.yml replaces the committed top-level plugins:, npmRegistryServer:, npmRegistries:, and npmScopes: blocks for the whole job, and the generated .npmrc replaces the managed registry=/scope lines: consumers must not rely on committed default-registry overrides, extra scopes, or third-party Berry plugins during CI. Independently of this secret, WillBooster's self-hosted runner machines carry a persistent ~/.npmrc (and ~/.yarnrc.yml) with Guard's public anonymous token, provisioned by self-host-utils, so default-registry installs on those machines route through the proxy even for callers that pass no token. Coverage: for bun, an EMPTY resolved field does not bypass the proxy — bun derives the download URL from the configured registry, so already-locked packages go through Guard too. A non-empty resolved is what bypasses it, and Yarn 1 always records one; Yarn Berry resolves through the proxy via the generated .yarnrc.yml (its yarn.lock stores registry-agnostic npm: resolutions, so no lockfile normalization is needed). Note the side effect for bun consumers: an install that UPDATES bun.lock under any Guard-routed .npmrc (workspace or machine-level) makes bun rewrite every already-locked package's resolved to the proxy host, so test.yml normalizes those URLs back to "" regardless of whether this token is set — otherwise a committed lockfile would pin every environment to the proxy and fail cold-cache installs with 401 for anyone holding a registry.npmjs.org token. The normalization runs before the diff is reported, so it appears with the other fixer changes in test.yml's failure output. Publishing repositories must declare publishConfig.registry explicitly (npmjs for public packages — wbfy injects it; Verdaccio publishers already declare theirs): without it, npm publish resolves the Guard proxy from the generated workspace .npmrc — or from the machine-level ~/.npmrc on tokenless self-hosted runs — and fails with 405 (Berry publishers likewise need npmPublishRegistry or publishConfig.registry, since yarn npm publish falls back to npmRegistryServer).
  • FNOX_AGE_KEY: age secret key that decrypts the age-encrypted secrets committed in the caller's fnox.toml. Required for repositories whose fnox.toml contains age-encrypted secrets; after mise installs fnox, deploy.yml, release.yml, and run-script.yml fail fast when the committed secrets cannot be resolved (missing or wrong key), while test.yml only warns (fork pull requests run it without secrets). A fnox.toml with only plaintext defaults needs no key, but a non-development job must still declare the selected profile (an inline [profiles.<name>] or a fnox.<name>.toml file) — an undeclared profile silently falls back to the base (development) secrets, so the check rejects it.

Do NOT pass these secrets explicitly to the other workflows (e.g. semantic-pr.yml, close-comment.yml): GitHub rejects a secrets: map entry the callee does not declare (secrets: inherit is exempt from this validation). Running wbfy (>= 3.0.0) on the caller repository injects VERDACCIO_TOKEN/FNOX_AGE_KEY automatically.

Automated fix application is retired

test.yml never pushes fixes. Its test job runs the pull request's own dependencies, so its checkout persists no push credential (persist-credentials: false); when the fixers (gen-code, cleanup, lint-fix, build) change anything, the job fails with the diff, and a developer or agent applies it. The former App-based autofix-apply.yml flow (a workflow_run job committing an uploaded patch with a GitHub App token) was retired in 2026-07: committing CI-generated patches with a bot credential is a standing risk with little benefit now that agents fix and verify before pushing, and retiring it let the App private key and its token broker be decommissioned. The autofix.ci path for public repositories (autofix.yml) and the nightly self-applying wbfy.yml were retired at the same time, so this repository hosts no workflow that writes to a caller's repository at all.

The test.yml caller's permissions must NOT be narrowed just because test.yml never pushes or dispatches; the grants are needed for other reasons:

  • contents: write — the "Test release script" step runs semantic-release --dry-run, which performs a git push --dry-run to verify write access and aborts with EGITNOPERMISSION without it, even in a dry run. Callers such as prompt-study, build-ts and agentic-workflows reach that step.
  • actions: write — the test job runs fkirc/skip-duplicate-actions with cancel_others: true, which calls cancelWorkflowRun. Dropping the grant also loses the implied actions: read, so duplicate/paths_ignore skipping silently stops working too.

Source checkouts use the automatically provided GITHUB_TOKEN (no secret needs to be passed, and self-hosted runners need no SSH deploy key). Exception: sync.yml pushes to the caller-supplied DEST_GIT_URL secret, so an SSH-form value there still needs runner SSH credentials until it is migrated to an HTTPS token URL.

Note: this repository is mirrored to WillBoosterLab/reusable-workflows with one-way-git-sync via the sync script, which maintainers run from their machines (bun run sync; renovate.jsonc and node_modules are excluded). The mirror is not synced automatically on merge, so it can lag main — run bun run sync after merging changes that WillBoosterLab callers need. The script passes -b main explicitly rather than relying on inference, and the pinned one-way-git-sync must stay at >= 6.0.45: earlier versions turned a transient clone failure into a push to a branch literally named undefined while still reporting success (and, with -b, into fatal: a branch named 'main' already exists). Verify the result by the mirror's own sync commit rather than the exit code — its title embeds the source commit it mirrored, so it should name the local main HEAD:

git rev-parse main
gh api repos/WillBoosterLab/reusable-workflows/commits/main --jq '.commit.message | split("\n")[0]'

About

A collection of reusable workflows for GitHub Actions

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages