fix(release): authenticate bump-tap-formula via secrets.TAP_WRITE_TOKEN, retire the runner credential helper #243

Closed
opened 2026-06-24 05:39:17 +00:00 by coilyco-ops · 2 comments
Member

Goal

Move the bump-tap-formula job's push auth in .forgejo/workflows/release.yml from the tap-writer runner's credential helper to an explicit secrets.TAP_WRITE_TOKEN, mirroring how publish-binaries already uses secrets.CI_RELEASE_TOKEN. One rotatable, visible place for the credential instead of a hidden runner config that broke silently (the root cause behind ward#237).

Background

bump-tap-formula clones coilyco-flight-deck/homebrew-tap, rewrites Formula/ward.rb, and git push origin HEAD:main. Today the push auth comes from the runner's credential helper ("never from env" — a deliberate keep-token-off-env choice). That credential broke after v0.96.0 and the tap has been frozen there since (the tap formula still serves v0.96.0; releases v0.97+ ship no formula bump). It's invisible and not rotatable from CI. publish-binaries already solved the same problem cleanly with a repo Actions secret.

Change (.forgejo/workflows/release.yml, bump-tap-formula job)

  1. Add env: TAP_WRITE_TOKEN: ${{ secrets.TAP_WRITE_TOKEN }} to the push step.
  2. Guard up front, mirroring publish-binaries: if TAP_WRITE_TOKEN is empty, echo "::error::TAP_WRITE_TOKEN not set; cannot push tap formula" >&2; exit 1.
  3. Authenticate the push with the token (token-in-URL or an ephemeral credential, never echoed/logged): push to https://<bot-user>:${TAP_WRITE_TOKEN}@forgejo.coilysiren.me/coilyco-flight-deck/homebrew-tap.git HEAD:main. Use a placeholder bot user consistent with the repo's other token pushes (e.g. coilysiren / the release bot — match what publish-binaries/git use elsewhere).
  4. Preserve the existing safety logic verbatim: set -euo pipefail, the sha256 tarball pin + 64-hex validation, the idempotent awk rewrite, and the read-back verification that the tap main serves ${TAG} after push.
  5. Drop runs-on: tap-writerruns-on: docker only if the tap-writer runner existed solely to carry that credential helper (check; if it has another purpose, leave the runner label and just switch the auth source).
  6. Retire the now-dead "auth comes from the runner's credential helper" comment.

Docs

Update docs/release.md: document TAP_WRITE_TOKEN as a required repo Actions secret alongside CI_RELEASE_TOKEN (scope: push to homebrew-tap), and note the tap-writer runner credential helper is retired.

Operator follow-up (NOT part of this issue — state it, don't attempt it)

This change delivers the mechanism. The pipeline stays red until Kai sets the TAP_WRITE_TOKEN secret (and CI_RELEASE_TOKEN, also currently broken) in ward → Settings → Actions → Secrets. The agent cannot set secrets and cannot prove the live push; acceptance is the workflow edit + docs, validated by YAML/shell review, with the operator step called out in the PR/commit body.

Acceptance

  1. bump-tap-formula reads TAP_WRITE_TOKEN from secrets, fails loudly if unset, and uses it for the push only (no token in logs).
  2. sha256 pin + 64-hex check + idempotent rewrite + post-push read-back verification all preserved.
  3. docs/release.md documents the new secret and the retired runner helper.
  4. YAML parses; shell is set -euo pipefail-clean.
  5. Conventional commit, closes via the full Forgejo URL trailer, push to canonical main.

ward#237 (the silent-stall this addresses at the root), publish-binaries job (the CI_RELEASE_TOKEN pattern to mirror).

## Goal Move the `bump-tap-formula` job's push auth in `.forgejo/workflows/release.yml` from the **`tap-writer` runner's credential helper** to an explicit **`secrets.TAP_WRITE_TOKEN`**, mirroring how `publish-binaries` already uses `secrets.CI_RELEASE_TOKEN`. One rotatable, visible place for the credential instead of a hidden runner config that broke silently (the root cause behind ward#237). ## Background `bump-tap-formula` clones `coilyco-flight-deck/homebrew-tap`, rewrites `Formula/ward.rb`, and `git push origin HEAD:main`. Today the push auth comes from the runner's credential helper ("never from env" — a deliberate keep-token-off-env choice). That credential broke after v0.96.0 and the tap has been frozen there since (the tap formula still serves v0.96.0; releases v0.97+ ship no formula bump). It's invisible and not rotatable from CI. publish-binaries already solved the same problem cleanly with a repo Actions secret. ## Change (`.forgejo/workflows/release.yml`, `bump-tap-formula` job) 1. Add `env: TAP_WRITE_TOKEN: ${{ secrets.TAP_WRITE_TOKEN }}` to the push step. 2. Guard up front, mirroring publish-binaries: if `TAP_WRITE_TOKEN` is empty, `echo "::error::TAP_WRITE_TOKEN not set; cannot push tap formula" >&2; exit 1`. 3. Authenticate the push with the token (token-in-URL or an ephemeral credential, **never echoed/logged**): push to `https://<bot-user>:${TAP_WRITE_TOKEN}@forgejo.coilysiren.me/coilyco-flight-deck/homebrew-tap.git HEAD:main`. Use a placeholder bot user consistent with the repo's other token pushes (e.g. `coilysiren` / the release bot — match what `publish-binaries`/git use elsewhere). 4. Preserve the existing safety logic verbatim: `set -euo pipefail`, the sha256 tarball pin + 64-hex validation, the idempotent `awk` rewrite, and the **read-back verification** that the tap main serves `${TAG}` after push. 5. Drop `runs-on: tap-writer` → `runs-on: docker` **only if** the `tap-writer` runner existed solely to carry that credential helper (check; if it has another purpose, leave the runner label and just switch the auth source). 6. Retire the now-dead "auth comes from the runner's credential helper" comment. ## Docs Update `docs/release.md`: document `TAP_WRITE_TOKEN` as a required repo Actions secret alongside `CI_RELEASE_TOKEN` (scope: push to `homebrew-tap`), and note the `tap-writer` runner credential helper is retired. ## Operator follow-up (NOT part of this issue — state it, don't attempt it) This change delivers the **mechanism**. The pipeline stays red until Kai sets the `TAP_WRITE_TOKEN` secret (and `CI_RELEASE_TOKEN`, also currently broken) in ward → Settings → Actions → Secrets. The agent cannot set secrets and cannot prove the live push; acceptance is the workflow edit + docs, validated by YAML/shell review, with the operator step called out in the PR/commit body. ## Acceptance 1. `bump-tap-formula` reads `TAP_WRITE_TOKEN` from secrets, fails loudly if unset, and uses it for the push only (no token in logs). 2. sha256 pin + 64-hex check + idempotent rewrite + post-push read-back verification all preserved. 3. `docs/release.md` documents the new secret and the retired runner helper. 4. YAML parses; shell is `set -euo pipefail`-clean. 5. Conventional commit, closes via the full Forgejo URL trailer, push to canonical main. ## Related ward#237 (the silent-stall this addresses at the root), `publish-binaries` job (the CI_RELEASE_TOKEN pattern to mirror).
Author
Member

🔒 Reserved by ward agent claude — container ward-ward-issue-243-claude-a4b8f737 on host kais-macbook-pro-2.local is carrying this issue (reserved 2026-06-24T05:39:33Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent claude` — container `ward-ward-issue-243-claude-a4b8f737` on host `kais-macbook-pro-2.local` is carrying this issue (reserved 2026-06-24T05:39:33Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Owner

Landed on main as 6bc0d2e.

What shipped (the mechanism):

  • bump-tap-formula now reads TAP_WRITE_TOKEN from secrets, guards up front (fails loudly with ::error:: if unset), and authenticates the push with the token in the URL (coilysiren:<token>@…, never echoed; git masks credentials in any URL it prints).
  • runs-on: tap-writerruns-on: docker (that runner existed solely to carry the credential helper; this job does a raw git clone, no JS uses: action).
  • All safety logic preserved verbatim: set -euo pipefail, sha256 tarball pin + 64-hex check, idempotent awk rewrite, post-push read-back verification.
  • Dead "auth comes from the runner's credential helper" comment retired.
  • docs/release.md documents TAP_WRITE_TOKEN (scope: push to homebrew-tap) alongside CI_RELEASE_TOKEN and notes the runner helper is retired.

Validated by full pre-commit suite + shellcheck -s bash (clean) on the run block.

Operator follow-up (cannot be done by the agent): the pipeline stays red until Kai sets TAP_WRITE_TOKEN (and CI_RELEASE_TOKEN, also currently broken) in ward → Settings → Actions → Secrets. The next push-to-main release will fail at the TAP_WRITE_TOKEN guard with a clear ::error:: until then — by design.

— Claude (she/her), via ward agent

Landed on `main` as `6bc0d2e`. **What shipped (the mechanism):** - `bump-tap-formula` now reads `TAP_WRITE_TOKEN` from `secrets`, guards up front (fails loudly with `::error::` if unset), and authenticates the push with the token in the URL (`coilysiren:<token>@…`, never echoed; git masks credentials in any URL it prints). - `runs-on: tap-writer` → `runs-on: docker` (that runner existed solely to carry the credential helper; this job does a raw `git clone`, no JS `uses:` action). - All safety logic preserved verbatim: `set -euo pipefail`, sha256 tarball pin + 64-hex check, idempotent `awk` rewrite, post-push read-back verification. - Dead "auth comes from the runner's credential helper" comment retired. - `docs/release.md` documents `TAP_WRITE_TOKEN` (scope: push to `homebrew-tap`) alongside `CI_RELEASE_TOKEN` and notes the runner helper is retired. Validated by full pre-commit suite + `shellcheck -s bash` (clean) on the run block. **Operator follow-up (cannot be done by the agent):** the pipeline stays red until Kai sets `TAP_WRITE_TOKEN` (and `CI_RELEASE_TOKEN`, also currently broken) in ward → Settings → Actions → Secrets. The next push-to-main release will fail at the `TAP_WRITE_TOKEN` guard with a clear `::error::` until then — by design. — Claude (she/her), via `ward agent`
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#243
No description provided.