Skip to content

fix(homebrew,scoop): GH-259 publish the manifests to their tap repos - #270

Open
joaodinissf wants to merge 4 commits into
fix/publisher-entity-kindfrom
fix/259-publish-tap-manifests
Open

fix(homebrew,scoop): GH-259 publish the manifests to their tap repos#270
joaodinissf wants to merge 4 commits into
fix/publisher-entity-kindfrom
fix/259-publish-tap-manifests

Conversation

@joaodinissf

Copy link
Copy Markdown
Collaborator

Stacked on #269 — merge that first, then this retargets to main.

Neither publisher has ever proposed anything. Three things had to be true at once and none of them were.

The task was never emitted. frontseat.yaml configured a tap but no name and no repo, so DefaultName("", "") returned "", EntityRefFor matched nothing, and Tasks() returned empty with no error and no log line. ResolvedName now falls back to the workspace name — injected for every plugin and read by nobody until now — and both publishers name their package and repo explicitly.

The push had no credential. ProposeFile shells out to gh, which authenticates from GH_TOKEN — in CI the repo-scoped secrets.GITHUB_TOKEN, which cannot write to frontseat-dev/homebrew-tap. TAP_TOKEN was already declared in release_secrets and exported onto three steps, and simply never read. A token_env option points gh at it, falling back to GH_TOKEN so a local frontseat ship still works.

Neither would have taken effect. git tag --contains is empty for every commit in the publish work — the pinned 0.12.0 publishers predate cross-repo publishing entirely, with no tap support and the formula rendered in-tree. A release cut by them cannot publish, which is exactly the deadlock bootstrap_from_source exists for. bootstrap_packages names what it builds in the workspace rather than the template, since that template is synced into consumer repositories.

Also in scope:

  • Preflight at checksum. Publishing is the last thing a release does, and release has already pushed the tag and undrafted the GitHub release by then, with no rollback and no continue-on-error. A read-only gh api repos/<tap> four phases earlier fails the ship while that is still free. Read-only because the token this needs opens a pull request and need not carry push permission.
  • ProposeFile no longer reports success having opened nothing. The open-PR listing discarded its error, and gh returns stderr there, which matches neither branch of the test — so an API blip skipped opening the pull request and still returned true.
  • Prereleases render but are not proposed. Neither channel has a prerelease notion, so a preview would be offered as the current formula.
  • Both plugins' package docs still described the self-tap that no longer exists, and gh was documented as absent from the mise registry when mise.toml pins it — so it is now declared as a tool and resolves through mise rather than ambient PATH.

Verified with frontseat ls tasks: both publish tasks and both preflights now bind to component:default/frontseat, with the token prefix and every flag present.

Relates to #259

This pull request was published with assistance from Claude.

@joaodinissf
joaodinissf requested a review from jbadeau July 30, 2026 09:30
joaodinissf and others added 4 commits August 1, 2026 22:23
Neither publisher has ever proposed anything. Three things had to be true
at once and none of them were.

The task was never emitted. frontseat.yaml configured a tap but no name and
no repo, so DefaultName("", "") returned "" and no entity matched. Resolve
the name from the workspace when neither is set, and give both publishers
the name and repo explicitly.

The push had no credential. ProposeFile shells out to gh, which reads
GH_TOKEN — in CI the repo-scoped default token, which cannot write to the
tap. TAP_TOKEN was already declared and exported and simply never read.
Point gh at it through a token_env option, falling back to GH_TOKEN so a
local ship still works.

Neither would have taken effect. The pinned 0.12.0 publishers predate
cross-repo publishing entirely — no tap support, formula rendered in-tree —
so a release cut by them cannot publish, which is the deadlock
bootstrap_from_source exists for. Name the packages it builds in the
workspace rather than the template, and add both publishers.

Also: check the tap is reachable at the checksum phase, four phases before
the tag is pushed, since publishing is the last thing a release does and
nothing rolls a public release back. Fail a release that lists open pull
requests unsuccessfully instead of reporting success having opened none.
Render prereleases without proposing them, as neither channel has a
prerelease notion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…sing

ProposeFile decided whether a manifest was already current by comparing
against the default branch. While a proposal is open the manifest exists
only on the release branch, so that lookup 404s, the short-circuit never
fires, and every retried release re-sent identical bytes — adding a second
commit to a pull request already under review. Its own contract says a
re-run "neither opens an empty pull request nor disturbs an open one"; it
honoured the first half.

Compare the branch as well, skip the write when it already carries these
bytes, and report changed only when this run wrote or opened something.

Found by rehearsing the publish path against a scratch tap: two runs, two
identical commits thirty seconds apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The preflight probed the manifest repository with a read. Both taps are
public, so that read succeeds with CI's repo-scoped default token — the
probe passes whether or not the release credential is configured, and only
the write fails, by which point the tag is pushed and the release is
public. It checked the one thing that was never going to be wrong.

Require the configured token env to be non-empty first. An unset repository
secret renders as the empty string, which is the failure that actually
occurs: TAP_TOKEN is declared in release_secrets and exported by the
workflow, but is not set on the repository today.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guard shipped without the test that was meant to accompany it, and the
Scoop render path had no coverage at all — its manifest is what `scoop
install` reads, so a wrong key or a missing shim is a broken install.

Both prerelease tests point --push-to at a repository that does not exist,
so a regressed guard fails loudly rather than passing quietly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@joaodinissf
joaodinissf force-pushed the fix/259-publish-tap-manifests branch from d7e37d8 to 183f3be Compare August 1, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant