Repository navigation
[COVAL-7631] Open a Homebrew tap pull request instead of pushing to main - #136
Merged
Merged
Conversation
The tap's rulesets reject direct pushes to main, so v0.9.0 and v0.9.1 both failed at update-homebrew and needed hand-made tap PRs. The release now pushes release/coval-vX.Y.Z and opens or updates a PR with the release URLs and checksums. Reruns reuse the branch and PR, and validate checks that the token can open pull requests on the tap before building.
update-homebrew holds HOMEBREW_TAP_TOKEN and runs the formula renderer, so check out the workflow's own ref instead of RELEASE_REF. CodeQL flagged the release-ref checkout as untrusted code in a privileged workflow_run context.
validate checked out RELEASE_REF and ran release_version.py in the same job that received HOMEBREW_TAP_TOKEN (CodeQL alert #1). The token check now runs in its own job with no checkout and no GITHUB_TOKEN permissions; build waits for both.
With the token check moved out, CodeQL flagged validate for cache poisoning: it still ran release_version.py from the RELEASE_REF checkout in a default-branch context. validate now runs the workflow's own copy of the script against Cargo.toml and Cargo.lock fetched from RELEASE_REF as data.
callumreid
marked this pull request as ready for review
October 15, 2026 13:43
ben-coval
approved these changes
Oct 15, 2026
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.
Problem
update-homebrewpushedFormula/coval.rbstraight tocoval-ai/homebrew-tapmain. Three active rulesets on the tap now require a pull request for every change tomain, so the push is rejected withGH013: Repository rule violations ... Changes must be made through a pull request. The three rebase-and-retry attempts cannot help.v0.9.0 failed this way and was recovered by hand in coval-ai/homebrew-tap#7. v0.9.1 failed the same way on 2026-10-14 (release run 38583417559) and was recovered in coval-ai/homebrew-tap#8. Each failure reopens #133.
Change
release/coval-vX.Y.ZwithHOMEBREW_TAP_TOKEN, then opens a PR againstmain, or updates the open one. The PR body links the release, theSHA256SUMSasset and each tarball, with its SHA-256, plus the run that opened it. The PR body comes from a newrender_pull_request_bodyinscripts/render_homebrew_formula.py, which shares the formula's version and checksum validation.main. If the release branch already carries the rendered formula, it is reused without a push. If the release assets were rebuilt and the checksums changed, the branch is replaced with a fresh commit onmain. That push uses--force-with-leaseagainst the SHA the job observed. An open PR for the branch is updated, not duplicated. The direct-push retry loop is gone, because nothing races on a bot-owned branch, and the release concurrency group already serializes runs for one tag.report-failurefires only when the push or PR call fails.close-recovered-failurestill closes cli#133, and its comment now names the tap PR that still has to merge. The PR URL is also exposed as thetap_pr_urljob output and written to the step summary.validatechecks Pull requests access.permissions.pushalone does not show that the token can open a PR. The step now POSTs to/pullswith nonexistent head and base branches, so it can never create a PR. GitHub returns 422 only after it authorizes the write. Any other status fails the release before the build starts, with an error that names the missing access. This is the same probeapi-parity-audit.ymluses forREGEN_PR_TOKEN.update-homebrewnow checks out the workflow's own ref (main, or the pushed tag) instead ofRELEASE_REF, because it holdsHOMEBREW_TAP_TOKENand runs the formula renderer. The token check moves out ofvalidateinto a newverify-homebrew-tokenjob with no checkout and noGITHUB_TOKENpermissions;buildwaits for both.validateitself now runs the workflow's ownrelease_version.py --rootagainstCargo.tomlandCargo.lockfetched fromRELEASE_REFthrough the contents API, so no release-ref code runs there either (CodeQL otherwise moved it to a cache-poisoning alert). I checked the fetch-and-validate path locally for bothrefs/tags/v0.9.1and the bare SHA: it acceptsv0.9.1and rejectsv0.9.2. CodeQL flagged both jobs as untrusted code in a privilegedworkflow_runcontext (open onmainas alerts docs: remove development section from readme #3 and docs: remove homebrew install #1 since 2026-10-01). Alert docs: update homebrew tap name #2 inrelease-on-version-bump.ymlis handled separately under COVAL-7697.HOMEBREW_TAP_TOKENprerequisite (Contents and Pull requests read/write).Before the next release
HOMEBREW_TAP_TOKENappears to be Callum's token:callumreidwas the push actor for the last automated tap update (0.8.3). I could not read its granted permissions from here. If it was created with Contents access only, add Pull requests: Read and write forcoval-ai/homebrew-tap. Otherwise the next release fails invalidatewith that message, before anything is built.delete_branch_on_merge: false, so mergedrelease/coval-*branches stay until someone deletes them. They do no harm.Validation
gh, in four cases:POST /pulls.PATCHes the open PR.PATCHes the PR.mainprints "already matches; nothing to push" and makes noghcalls.--force-with-leaseis rejected (stale info) when the branch already exists.ghchecks:pulls?head=coval-ai:<branch>&base=mainfinds the tap's chore: bump version to 0.1.1 #8 when queried withstate=closed, and returns empty with exit 0 when no PR matches.-F body=@filesends the file verbatim, checked throughPOST /markdown.gh pr view <branch> --repo coval-ai/homebrew-tapresolves.HTTP 422 Validation Failed(base and head invalid), and no PR was created.actionlint(with shellcheck) is clean. Script unit tests: 63 pass, including two new PR-body tests.ruff check/ruff format --check scriptsandpython3 scripts/release_version.pypass.No database queries change.
🤖 Generated with Claude Code