Skip to content

ci: ship the macOS app built Release, like the iOS one - #2396

Merged
ryanbr merged 1 commit into
mainfrom
2393-macos-release
Sep 22, 2026
Merged

ryanbr merged 1 commit into
mainfrom
2393-macos-release

Conversation

@ryanbr

@ryanbr ryanbr commented Sep 22, 2026

Copy link
Copy Markdown
Owner

The build-configuration half of #2393, found by @nichtlegacy.

Both shipping workflows built the macOS app -configuration Debug while the iOS leg in the same file built Release (fork-release.yml:218 vs :273, fork-testing-build.yml:156 vs :202). The artefact showed it plainly: a 120 KB stub plus a 195 MB .debug.dylib and a __preview.dylib, against 53 MB for the same commit built Release.

It was not a decision

The comment above the step is about #51, where an arm64-only binary would not launch on Intel Macs, and "unchanged shipping behaviour" there means that PR was not ALSO changing the configuration. The explicit ARCHS / ONLY_ACTIVE_ARCH settings it added override whatever the configuration says, so the universal build holds across this switch, and the verify step looks for the single non-dylib executable that a Release build produces.

The cost is not theoretical. The reporter measured the shipped build at a third to half a CPU core at idle, and unoptimised SwiftUI compounds a hot path that already ran every frame (the other two halves of this report are #2394 and #2395).

What Release removes besides the overhead

#if DEBUG code: previews, sample data, and the --demo-sync screenshot harness, whose own doc already states it "is stripped from every Release build". The iOS artefact has shipped without all of it since it went Release, so this brings macOS into line rather than into new territory. A local Debug build still has the harness if you use it for captures.

The testing workflow moves too

On purpose. A testing build that is not built the way the release is cannot answer how the release performs, which is the one thing a testing build is for. It also means the next testing build exercises this change before a release does, which is the only real way to verify a CI-only change: nothing here can be run locally.

Watch on the first run

The macOS job has timeout-minutes: 40 and a Release build is slower than the Debug one it replaces. I would rather flag that now than discover it on a release. If it does run close, the fix is a higher timeout on that job, not a return to Debug.

Not addressed here

The stable macOS artefact also carries the com.noopapp.noop.staging bundle id and a NOOP Staging executable, which the report flags. That is NOT a Debug artefact: project.yml:140-141 sets PRODUCT_BUNDLE_IDENTIFIER and PRODUCT_NAME unconditionally, so this change does not touch either. It deserves its own discussion rather than riding along here.

Both shipping workflows built the macOS app -configuration Debug while the iOS
leg in the same file built Release. The artefact showed it: a 120 KB stub plus
a 195 MB .debug.dylib and a __preview.dylib, against 53 MB for the same commit
built Release.

Nobody chose this. #51 was about architecture, not configuration, and its
"unchanged shipping behaviour" meant that PR was not also switching the config
while it fixed an arm64-only binary that would not launch on Intel Macs. The
explicit ARCHS / ONLY_ACTIVE_ARCH settings it added override whatever the
configuration says, so the universal build holds either way, and the verify
step looks for the single non-dylib executable a Release build produces.

A reporter measured the shipped build at a third to half a CPU core at idle on
an M1 Pro. Unoptimised SwiftUI is expensive in general and compounds a hot path
that already ran every frame.

Beyond the optimisation, Release drops #if DEBUG code: previews, sample data,
and the --demo-sync screenshot harness, whose own doc already says it is
stripped from every Release build. The iOS artefact has shipped without all of
that since it went Release, so this brings macOS into line rather than into new
territory. A local Debug build still has the harness.

The testing workflow moves too, on purpose: a testing build that is not built
the way the release is cannot answer how the release performs, which is what a
testing build is for. It also exercises this change before a release does.

Worth watching on the first run rather than assumed: the macOS job has a
40-minute timeout and a Release build is slower than the Debug one it replaces.
@nichtlegacy

Copy link
Copy Markdown

One correction on the cost argument, since it is my number being cited.

The cost is not theoretical. The reporter measured the shipped build at a third to half a CPU core at idle, and unoptimised SwiftUI compounds a hot path that already ran every frame

The first half is right, the second half is not, and I am the reason it looked that way. My original report did claim the Debug configuration made the CPU cost noticeably worse. I had not actually measured that. I had compared a lifetime average of the shipped build, which included periods where the machine was asleep, against an awake-window measurement of a Release build, and attributed the gap to the configuration.

When I measured it properly, same version, both visible, motion preference off, 120 s windows:

Build CPU
NOOP-macos-v11.8.0.zip as shipped (Debug) 27.51 %
Same commit built -configuration Release 26.49 %

One percentage point, inside the run-to-run spread I saw elsewhere (28.76 % and 34.15 % for two Release runs in the same state). The hot path is SwiftUI, CoreAnimation and Canvas, so it is precompiled framework code either way, and whether the app's own Swift is optimised barely enters into it.

That is Run 3 in #2393. I added it at 12:07Z along with a rewrite that reframed the build configuration as a separate observation rather than a contributing cause, which was about fifteen minutes before this PR, so it was easy to miss.

None of this argues against the change. Shipping macOS Release for consistency with iOS, a 53 MB binary instead of a 120 KB stub plus a 195 MB .debug.dylib, and stripping the #if DEBUG harness all stand on their own. It is worth getting the reason right in the history, though, because someone reading this later could reasonably conclude the configuration was where the idle CPU went. It was not. #2394 is.

Your note about timeout-minutes: 40 is worth taking seriously. My local Release build of the same commit took noticeably longer than the Debug one, single-arch on an M1 Pro, and the CI leg builds universal.

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