Skip to content

Make release workflow trigger on version bump merge, not dispatch - #126

Merged
alexkroman merged 2 commits into
mainfrom
claude/blurt-release-workflow-3e4bqc
Aug 12, 2026
Merged

Make release workflow trigger on version bump merge, not dispatch#126
alexkroman merged 2 commits into
mainfrom
claude/blurt-release-workflow-3e4bqc

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

What & why

The release workflow now triggers automatically when the version-bump PR merges into main, rather than requiring a manual dispatch. This simplifies the release process from three steps (bump, merge, dispatch) to two (bump, merge).

How it works:

  • Added a push trigger to release.yml that fires when App/Blurt/project.yml changes on main
  • New resolve job (Linux, cheap) runs first to decide whether to release:
    • On dispatch: validates the version matches the commit
    • On push: confirms the version actually changed (diffs against previous commit) and no vX.Y.Z tag exists yet
    • Skips the build/publish jobs if conditions aren't met (e.g., a project.yml edit that isn't a version bump)
  • Both build and publish jobs now depend on resolve and only run if it sets release=true
  • The merged-bump path never passes skip_checks, skip_smoke, or republish — those remain dispatch-only escape hatches

Security/correctness preserved:

  • Both jobs still pin github.sha, so the tag lands only on the exact reviewed commit
  • The release-publish approval gate still holds before anything reaches users
  • Manual dispatch still works for re-running failed builds, republish, and non-main dry runs
  • A GITHUB_TOKEN merge (Actions auto-merge) won't trigger release — the merge must come from a person or an agent with its own credentials, same as the PR creation rule

Updated RELEASE.md, .claude/skills/release/SKILL.md, and AGENTS.md to document the new flow and the two ways a release can start.

How it was tested

  • scripts/check.sh passes (or CI will, if I'm not on a Mac)
  • I read AGENTS.md and this doesn't reintroduce anything deliberately removed
  • Docs updated if behavior changed

https://claude.ai/code/session_01F952B3qz1QeXYv5sPGwPh3

Releasing took two deliberate acts from the maintainer that were really the
same decision: merge the version-bump PR, then go dispatch `release` with the
same version the PR just landed. The second one carried no new judgement — it
restated the first — but it was the one step that couldn't be done from a
review page, so a release stalled on someone opening the Actions tab.

`release.yml` now also triggers on a push to `main` that touches
`App/Blurt/project.yml`, and a new Linux `resolve` job decides whether that
push is actually a release: it diffs `CFBundleShortVersionString` against the
push's previous commit and checks no `vX.Y.Z` tag exists yet. A project.yml
edit that adds a source file resolves to `release=false` and skips the build
and publish jobs; only a bump landing on main goes through. Running first, on
ubuntu, means a non-release push costs seconds and never reaches the runner
holding the Developer ID key.

The dispatch path stays, and is what re-runs a failed build, republishes, and
does the non-main dry run — `skip_checks`, `skip_smoke`, and `republish` are
dispatch-only inputs, so the merged-bump path always runs the full checks. Both
jobs still pin `github.sha`, so a release is still exactly one reviewed commit,
and publishing still parks on the `release-publish` approval gate.

The `resolve` job supersedes the build job's version guard as the source of the
version; the guard stays as a re-check on the signing runner itself, so the
artifact name and the built binary can't disagree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F952B3qz1QeXYv5sPGwPh3
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Dev build

Download Blurt.app — built from 93f869d, Debug-Local,
ad-hoc signed. Expires in 14 days.

Installing it
cd ~/Downloads
unzip -o blurt-dev-build-pr-126.zip   # GitHub wraps every artifact in a zip
unzip -o Blurt-dev-93f869d.zip
find Blurt.app -exec xattr -c {} +   # clear quarantine: xattr lost -r in macOS 12.3
rm -rf /Applications/Blurt.app && cp -R Blurt.app /Applications/
open -a Blurt

It is ad-hoc signed and not notarized: Gatekeeper refuses to open it until
the quarantine flag is cleared, and macOS treats it as a different app from a
released Blurt, so you have to re-grant Microphone, Accessibility, and Input
Monitoring. Reinstall the release DMG
when you are done reviewing.

Expect that re-grant once per dev build, including a second build of this
same PR. TCC pins an Accessibility grant to the signature that took it, and an
ad-hoc signature is just a hash of the binary, so every build is a new app as far
as tccd is concerned. Blurt clears the orphaned grant at launch, which is what
keeps the Accessibility step from getting stuck on a Blurt row that is switched on
and still denied. If you are coming from a build old enough to predate that,
clear the grant yourself once:

tccutil reset Accessibility dev.alex.blurt

The last dispatch in the release flow was the first one. Dispatching a workflow
needs the Actions UI or `actions: write`; a chat or web session typically has
neither, but does have push. So the one step that could kick a release off was
the one step an agent couldn't do, and every release still began with the
maintainer opening the Actions tab.

`release-bump.yml` now also triggers on a push of `release/v[0-9]*`. The branch
name is the request — `release/v0.1.37` asks for 0.1.37, with no default_target
guessing, because a marker that named no version would be asking the workflow to
guess what its own name meant. The job checks out main, verifies the marker is
an ancestor of main, bumps on top of main's tip, and force-pushes the result
onto the same branch with the lease pinned to the sha it vetted.

The ancestor check is the load-bearing one. Without it, pushing a branch would
be a way to get a bot-authored commit sitting on top of arbitrary content, and
the PR that followed would quietly be about more than a version bump. With it,
the marker is a signal and the bump is the only commit that lands on it.

This leans on the GITHUB_TOKEN rule in the opposite direction from the rest of
the release path: the bump commit the job force-pushes does not re-fire the push
trigger that started it, which is what keeps it from looping. Noted in the
workflow, RELEASE.md, and the skill's guardrails, since moving this job to a PAT
would silently turn it into a loop.

Dispatch still works, still guards that it came from main, and remains the only
path that accepts an empty version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F952B3qz1QeXYv5sPGwPh3
@alexkroman
alexkroman enabled auto-merge August 12, 2026 05:06
@alexkroman
alexkroman added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit 4ce35d2 Aug 12, 2026
10 checks passed
@alexkroman
alexkroman deleted the claude/blurt-release-workflow-3e4bqc branch August 12, 2026 05:28
@alexkroman alexkroman mentioned this pull request Aug 12, 2026
3 tasks
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.

2 participants