Repository navigation
Refuse to bump the tap when the version string disagrees with the tag - #7
Closed
k3KAW8Pnf7mkmdSMPHz27 wants to merge 1 commit into
Closed
k3KAW8Pnf7mkmdSMPHz27 wants to merge 1 commit into
k3KAW8Pnf7mkmdSMPHz27 wants to merge 1 commit into
Conversation
Sources/EventKitService/EventKitService.swift hardcodes eventKitServiceVersion. The binary reports that string from --version and in its MCP serverInfo, while Homebrew takes the version from the git tag. If a release is tagged without bumping the string, `brew list` and the server disagree about which version is installed, and nothing else catches it: no test pins the string. Every release passes through bump-tap.yml, so check there. The new step reads the file at the commit the tag resolves to, not at the run's own ref, which under workflow_dispatch is a branch. It fails unless the file declares the string exactly once and that string equals the tag without its leading "v". It runs before the tap token is minted, so a bad tag stops before any credential exists. The file is fetched with `gh api .../contents/<path>?ref=<sha>` using the default GITHUB_TOKEN and the existing contents: read permission. That is one request for one file. It adds no new action, fetches no tree, and leaves no token behind in a .git/config, which a checkout would do unless told otherwise.
Owner
Author
|
Closing without merging. Superseded by #8. This guard checks that the tagged commit's The mismatch it was meant to catch is now caught in two other places:
|
k3KAW8Pnf7mkmdSMPHz27
added a commit
that referenced
this pull request
Sep 23, 2026
Every releasable merge to `main` is released automatically by [semantic-release](https://semantic-release.org). It runs as a `release` job in `ci.yml` after `build-and-test` passes on the pushed commit. It tags the version, publishes a GitHub Release with generated notes, and the tag triggers `bump-tap.yml`. ## Pieces - **Titles.** `pr-title.yml` (check `conventional-title`) requires the PR title to be a conventional commit. Squash-only merging makes the PR title the commit subject on `main`. - **Versioning.** semantic-release reads the commit subjects since the last tag: - `feat` releases a minor version. - `fix`, `perf` and `revert` release a patch. - A `!` after the type, or a breaking-change footer, releases a major. - Any other type releases nothing. - **`.releaserc.yml`** - Uses the `conventionalcommits` preset. With the default `angular` preset, `feat!:` releases nothing: I dry-ran it and got "no new version" instead of 2.0.0. - Lists the plugins explicitly and leaves out `@semantic-release/npm`. - Turns off the failure issue and the `released` label, because the token has no issues permission. - **Supply chain.** - `package.json` and `package-lock.json` pin the release tooling, 445 packages. CI installs them with `--ignore-scripts`. - `npm audit signatures` must pass before anything runs with write access. - `node_modules/` is ignored. - **Auth.** The job mints a token from the existing GitHub App, narrowed to this repo with `contents` and `pull-requests` write. A tag pushed with `GITHUB_TOKEN` can't trigger `bump-tap.yml`; the App's tag can. - **Dependabot.** Swift package bumps are titled `fix(deps)` and release a patch. Actions bumps are `ci(deps)`, and so are npm bumps (weekly, grouped); neither releases anything. Major updates of `conventional-changelog-conventionalcommits` are ignored. Preset 10 needs `conventional-changelog-writer` 9, but semantic-release's notes generator still ships writer 8, so release notes crash and nothing gets tagged. - **Version string.** `eventKitServiceVersion` becomes the placeholder `0.0.0-dev`. The formula writes the tag's version in before building (k3KAW8Pnf7mkmdSMPHz27/homebrew-mcp#1), so a release never commits to `main`. ## Verification - I ran `semantic-release --dry-run` in a throwaway clone against the real remote. All 10 cases completed with exit 0 and no errors, and every releasable case rendered release notes: - This branch as it stands releases nothing. - `fix(deps)`, `revert` and `perf` each give 1.0.1. - `feat` plus `fix` gives 1.1.0. The notes have Features and Bug Fixes sections, and the `docs:` commit is hidden. - `feat!`, `feat(api)!` and a breaking-change footer each give 2.0.0. - `ci(deps)`, and `docs` plus `refactor`, release nothing. - The first attempt pinned preset 10.4.0. The version calculation was correct, but notes generation crashed. That's why the preset is pinned to 9.3.1 and Dependabot ignores its majors. - `npm audit signatures` shows 445 registry signatures verified. ## After merging This merge releases nothing, because the commits since `v1.0.0` predate the convention. `v1.1.0`, the modernization, needs one tag and GitHub Release made by hand. semantic-release counts from that tag onward. Merge homebrew-mcp#1 first, so that `v1.1.0` reports its real version. Supersedes #7.
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.
Why
Sources/EventKitService/EventKitService.swifthardcodespublic let eventKitServiceVersion = "1.0.0". The binary reports that string from--version(the ArgumentParserCommandConfiguration) and in MCPserverInfo(EventKitMCPServer.swift). Homebrew takes its version from the git tag. So ifv1.1.0is tagged without bumping the string,brew listsays 1.1.0 while the server tells Claude Desktop it is 1.0.0. No test pins the string.Every release passes through
bump-tap.yml, so the check goes there.What
A new step, Check the version string matches the tag, runs right after "Resolve tag and commit" and before "Mint a scoped token for the tap". It:
Reads
EventKitService.swiftat the resolved commit SHA (steps.release.outputs.sha), not at the run's own ref. Underworkflow_dispatchthe run's ref is a branch, not the tag.Fails closed. The step errors, naming the file, when:
public let eventKitServiceVersion = "..."declaration matches zero times or more than once.Compares the string to
${TAG#v}. On a mismatch it prints both values and what to do:Because it runs before the token step, a bad tag fails before any credential is minted. The tag, SHA and repo reach the script through
env:, the same pattern as the existing steps. The file's contents never touch a${{ }}expression.How the file is fetched:
gh api, notactions/checkoutThis uses the default
GITHUB_TOKENunder the existingcontents: read, so permissions are unchanged. I chose it over a sparse checkout because:with:settings (ref,sparse-checkout,sparse-checkout-cone-mode,persist-credentials,path).actions/checkoutwrites the token into.git/configunlesspersist-credentials: falseis set, and this job later checks out the tap with a different token.::error::.The response goes to
$RUNNER_TEMP, andgrep/cutread it from there.cut -d '"' -f 2takes the value between the first pair of quotes on the one matching line.Testing
Real run.
gh workflow run bump-tap.yml --ref bump-tap-version-guard -f tag=v1.0.0ran run 35894192997, which succeeded:Pinning v1.0.0 -> dc1316c…)eventKitServiceVersion at v1.0.0 is "1.0.0"; matches the tag.)Formula already pinned to v1.0.0; nothing to do.)No PR was opened on the tap.
Local runs. I ran the step's parse and compare lines verbatim against the real file and against edited copies in
/tmp. The fetch line ran separately with its values filled in.v1.0.0vs the real file atdc1316ceventKitServiceVersion at v1.0.0 is "1.0.0"; matches the tag.v9.9.9vs the real filepublic varExpected exactly one 'public let eventKitServiceVersion = "..."' in Sources/EventKitService/EventKitService.swift at v1.0.0, found 0found 2found 0Sources/Nope.swift)gh: Not Found (HTTP 404), then exit 1:Could not read Sources/Nope.swift at v1.0.0 (dc1316c…)ruby -ryamlparses the workflow, and the new step is at index 1, ahead of the token step. actionlint and shellcheck were not run because they are not installed.