Skip to content

ci(release): stop running the macOS suite twice per release and drop the unused Homebrew setup - #2208

Merged
datlechin merged 1 commit into
mainfrom
ci/release-pipeline-cleanup
Aug 18, 2026
Merged

ci(release): stop running the macOS suite twice per release and drop the unused Homebrew setup#2208
datlechin merged 1 commit into
mainfrom
ci/release-pipeline-cleanup

Conversation

@datlechin

Copy link
Copy Markdown
Member

Cleanup of the release pipeline, from profiling the v0.66.0 run (32167949766). That run took ~50 minutes against 40m35s for v0.65.0.

Speed

The macOS suite ran twice on the same commit. /release pushes the version commit to main and then tags it, so macos-tests.yml fires once directly and once through build.yml's workflow_call. Both ran SHA 8cd3514. The concurrency group is keyed on github.ref, so refs/heads/main and refs/tags/v0.66.0 never collided. The duplicate is also what left the release's own app-tests queued for 8m51s, since the account runs five macOS jobs at a time. The changes job now stands down for a push to main whose subject is a release commit, and the tag run does the work.

I did not merge the concurrency groups, which was the first thing I tried. Whichever run enters the group second cancels the other, and that ordering is not guaranteed if the two pushes are ever combined. Losing the tag run means the release dies. The guard here fails toward running twice instead.

The x86_64 job spent 5m15s on Homebrew for a library it never opened. prepare-libs.sh copies Libs/libmariadb_x86_64.a, project.yml force-loads $(SRCROOT)/Libs/libmariadb.a, the headers are vendored under Plugins/MySQLDriverPlugin/CMariaDB/include, and LIBRARY_SEARCH_PATHS names no Homebrew prefix. app-tests already proves it: that job links all 31 plugin bundles with no Homebrew mariadb installed. Rosetta went with it, since nothing x86_64 executes during a cross-compile. create-dmg is the only formula either job needs.

Also: registry-readiness moves to ubuntu-latest (it reads two integers and fetches a JSON manifest), the build jobs cache the ~/.spm-cache checkouts that build-release.sh already clones into, and lint moves to macos-26 so the workflow stops straddling two runner images.

Correctness of the release

Two steps could fail without failing the release:

  • The Sparkle steps were gated on if: env.SPARKLE_PRIVATE_KEY != ''. This job only runs for a v* tag, so a missing or rotated key was never legitimate, and it published a release no installed copy could see under a green check. Gate removed.
  • Commit appcast.xml to repo was continue-on-error: true, which made a failed push indistinguishable from a clean release: every updater keeps reporting "up to date" for a version that shipped. It now rebases and retries up to three times, and fails the job if the push still will not land.

That step also moved after Create GitHub Release, so the feed never advertises a download URL that still 404s.

Hygiene

  • Dropped paths-ignore from the tag trigger. GitHub does not evaluate path filters for tag pushes, so it never excluded anything.
  • Added concurrency with cancel-in-progress: false, so a re-tag queues instead of cancelling a run mid-notarization.
  • Cleared the Node 20 deprecation: checkout v4 to v7, upload-artifact v4 to v7, download-artifact v4 to v8, cache v4 to v6, across build.yml, macos-tests.yml and ios-tests.yml. I read each new major's action.yml and release notes first. Every input in use survives, and the breaking changes are opt-in (archive, skip-decompress) or wanted here (v8 errors on a download digest mismatch instead of warning).
  • Fixed the comment at scripts/build-release.sh:25 claiming CI pre-copies libmariadb "from Homebrew". It comes from Libs. That sentence is most likely why the Homebrew step existed.
  • .claude/skills/release/SKILL.md now says to wait for the app release before pushing plugin tags. On v0.66.0 seven plugin tags pushed two minutes after the app tag, and one plugin build did not start for 20 minutes.

Testing

No unit tests: every change is workflow configuration. Verification was:

  • actionlint on all three workflows. The only findings are 4 pre-existing SC2086 infos in the keychain steps, identical on the HEAD versions.
  • The new skip guard was run against 7 event/ref/subject combinations. It skips only push + refs/heads/main + a release: v<digit> subject. The refs/tags/v* path that workflow_call sees still returns run=true, as do workflow_dispatch on main and a prose subject like release: verify the funnel.
  • Action inputs checked against each new major's action.yml.

What cannot be exercised without a real tag is the release job's new step order and the appcast retry. gh workflow run build.yml --ref ci/release-pipeline-cleanup covers both build jobs, and so the Homebrew removal and the SPM cache, without publishing anything: registry-readiness and release are both gated on refs/tags/v.

Not done

DerivedData caching. For release builds a stale cache can yield a subtly wrong binary that then gets signed, notarized and shipped, and at multi-GB against a 10GB repo budget shared with the Libs cache it would likely cost more in restore and save time than it returns. The 15-minute compile needs a real fix, not a cache.

build-plugin.yml still uses checkout@v4. It had uncommitted changes in the working tree while I was editing, so I left it alone.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin
datlechin merged commit 2f093d3 into main Aug 18, 2026
7 checks passed
@datlechin
datlechin deleted the ci/release-pipeline-cleanup branch August 18, 2026 19:00
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