Conversation
… bundle The script copied the product binary and icons into the bundle but never the SwiftPM resource bundle, so every app it assembled died at startup with 'unable to find bundle named CodeBurnMenubar_CodeBurnMenubar'. It also exited 1 before compiling on any SDK newer than 14.x, and required a swift.org toolchain that is unnecessary when Xcode or the Command Line Tools provide swift. Selection is now capability-driven, preferring Xcode's SDK because libSwiftUIMacros.dylib, which expands SwiftUI's @State macro on macOS 15+ SDKs, ships only inside Xcode. Gates the @mainactor rewrite to the 14.x SDK it was written for, and drops the redundant per-arch builds plus lipo merge in favour of one two-arch build.
ozymandiashh
left a comment
There was a problem hiding this comment.
Ran the script on a CLT-only Mac (macOS 26.6, CLT 26.6, SDK 26.5, no Xcode). Three things need to change before this comes out of draft; note that CI green says nothing here since no workflow runs build-local.sh.
-
Dropping per-arch +
lipobreaks every CLT-only host.swift build --arch arm64 --arch x86_64shells out to xcbuild, which ships only with Xcode:error: xcbuild executable at '/Library/Developer/SharedFrameworks/XCBuild.framework/.../xcbuild' does not exist, 1.3 s in. The comment you removed said exactly this. That also kills the macOS 14 + swift.org path your new comment block says is supported. Keep the two-build +lipofallback when xcbuild is absent;package-app.shcan stay on the two-arch form because CI has Xcode. -
The SDK gate is version-driven and blocks hosts that build fine.
bash Scripts/build-local.sh dev-reviewhere exits 1 demanding Xcode, yetswift build -c release --arch arm64on the staged sources completes (rc 0,minos 14.0) with nolibSwiftUIMacros.dylibanywhere in CLT. The macro failure is specific to CLT 27.0 / SDK 27.0, not "newer than 14.x", so the gate refuses the entire 15.x to 26.x range, which is most Macs and the same configurationmac-menubar-ci.ymluses. Make it capability-driven: probe for the plugin, or attempt the build and only then advise installing Xcode. -
Hardcoded
/Applications/Xcode.appignoresDEVELOPER_DIR,xcode-select -pand Xcode-beta (those hosts get a misleading "Install Xcode"), and where it does match it silently overrides the user'sxcode-select, making the 14.x legacy path unreachable and skipping the@MainActorpatch. Derive the developer dir fromxcode-select -p/xcrun --find xcodebuild.
The resource-bundle copy is correct (verified the bundle lands in Contents/Resources against a real build), but with no nullglob and no post-copy check the loop no-ops silently when the glob misses, so the crash this fixes can come back unnoticed. package-app.sh:53-58 already has the named-bundle + if [[ -d ]] pattern; mirror it.
Small: shellcheck SC2155 on line 73, and mac/README.md:81 plus the script header still describe the old flow.
Groundwork for getagentseal#1219. The menubar app hardcoded ~530 English strings across Views/, the presentation structs, the status-item menu and the update alerts, so there was nothing for a translator to translate. Every user-facing literal now goes through `L(_:)` / `L(_:_:)`, which look the string up in `Localizable.strings` in the SwiftPM target resource bundle. The key *is* the English copy, so English stays the development language and a missing translation degrades to correct English instead of a dotted identifier. `en.lproj` is therefore an identity table; it exists so the bundle advertises `en` and so a translation can be diffed against it. Why the lookups are explicit rather than relying on SwiftUI's implicit `LocalizedStringKey`: SwiftPM emits target resources into a sibling bundle (`CodeBurnMenubar_CodeBurnMenubar.bundle`) that the packaging scripts copy into `Contents/Resources`. `Bundle.main` has no `.lproj` at all, so `Text("literal")` would always miss. Naming `Bundle.module` is the one form that resolves the same way in `swift run`, in `swift test` and in the packaged `.app`. Enum raw values that double as identity (`Period`, `MenubarScope`, `InsightMode`, `AccentPreset`, `ProviderFilter`) keep their raw value and gain a `displayLabel`, so persistence and cache keys are untouched by translation. Three display-only date formatters that were pinned to `en_US_POSIX` with fixed patterns now use `setLocalizedDateFormatFromTemplate`, and the calendar popover's weekday row comes from the locale's own symbols. The `yyyy-MM-dd` formatter stays POSIX: it parses and builds data keys, not display text. Not extracted, deliberately: provider, model and plan names; units; currency codes; shell commands and paths; quota window labels that policy code matches on by English substring (`QuotaSummary.headlineWindow`); and anything the `codeburn` CLI produces. Also copies the SwiftPM resource bundle into the app in build-local.sh, using the named-bundle + `[[ -d ]]` pattern package-app.sh already uses. Without it the assembled app finds no strings table (and already trapped on first icon load, the resource-bundle half of getagentseal#1262).
iamtoruk
left a comment
There was a problem hiding this comment.
Checked this one locally. Ran the script on a real Xcode box with no swift.org toolchain, output redirected to a temp dir. Build succeeds, universal binary, ad-hoc sign passes. So the core idea works.
Three problems before it can go in:
-
Ozy's review from 11 Sep is still open. The lipo fallback is gone (single
swift build --arch arm64 --arch x86_64needs full Xcode, breaks CLT-only hosts), and/Applications/Xcode.appis still hardcoded in two places instead ofDEVELOPER_DIR/xcode-select -p. The SDK gate now falls back instead of failing, which is better, but only if that hardcoded path exists. -
The resource-bundle fix this PR is named for already landed on main on 12 Sep with the zh-Hans work (named bundle path, hard fail if missing). Your branch still carries the older glob loop, so it now conflicts on rebase. Main's version should win.
-
Your base predates the localization commit, so there is no
.lprojin this build to check. After the rebase, please post the terminal output of a run pluslsof the bundle's Resources dir so we can see the locales copied.
Rebase onto main, drop the bundle hunk, add the lipo fallback and xcode-select fix from Ozy's review, then it should be a small script and I am happy to take it.
Summary
mac/Scripts/build-local.shcannot produce a working app on a current macOS toolchain. This pull request corrects 2 defects. Neither is specific to any package manager or host configuration: the first breaks every app the script assembles, and the second stops the script before it compiles anything.1. The assembled app could not launch, because the resource bundle was never copied
The script copies the product binary and the generated icons into the bundle, but not the SwiftPM resource bundle. Any app it assembled terminated during startup:
Bundle.moduleresolves that bundle from the app'sContents/Resources. SwiftPM emits it as a sibling of the executable in the build products directory, so it has to be copied in alongside the binary. The script now copies every*.bundlefrom that directory.This became reachable when target resources entered
Package.swift; the script was never updated to match. It is independent of defect 2 and of any host specifics.2. The script exited before compiling on any SDK newer than 14.x
Two hard exits made the script unusable on a current toolchain:
~/Library/Developer/Toolchains/and exited 1 when none was present, even though theswiftprovided by Xcode or the Command Line Tools is sufficient.14.xand exited 1 otherwise.Selection is now capability-driven rather than pinned. The compiler is a swift.org toolchain when one is installed, otherwise
swiftfromPATH. The SDK is Xcode's when/Applications/Xcode.appis present, otherwise whateverxcrunreports. When the SDK is newer than 14.x and Xcode is absent, the script fails with an actionable message instead of a version mismatch.Preferring Xcode's SDK is not cosmetic. On macOS 15+ SDKs SwiftUI's
@Stateis a macro, and the plugin that expands it,libSwiftUIMacros.dylib, ships only inside Xcode. Command Line Tools 27.0 carries onlylibObservationMacros.dylibandlibSwiftMacros.dylib, so compiling any view against the CLT SDK fails:An SDK and its macro plugins must be used as a matched pair. Supplying Xcode 26.6's plugin to the CLT 27.0 SDK expands
@Stateinto a member that SDK does not declare:Selecting Xcode's own SDK avoids this entirely: there
@Stateis an ordinary property wrapper and no macro plugin participates in the build.Two consequences follow. The
@MainActorsource rewriting is now gated to the14.xSDK it was written for, because later SDKs already carry those annotations; the14.*branch keeps the originalperlrewrite verbatim. And the two-arch build is a single invocation, which routes through xcbuild and emits a fat binary directly, so the separate per-arch builds and thelipomerge that followed them are redundant.Change evidence
*.bundlefrom the products directory intoContents/Resourcesresource_bundle_accessor.swift:44: Fatal error: unable to find bundle named CodeBurnMenubar_CodeBurnMenubar, process exit code -5, no child process spawned. After the change,Contents/ResourceslistsAppIcon.icns,CodeBurnMenubar_CodeBurnMenubar.bundle,menubar-logo.png, and the same launch stays alive and spawns itscodeburn serve --stdiochild, confirmed bypgrep -P <app pid>reporting a matching ppid.mac/Scripts/build-local.sh dev-nixfixend to end: rc 0 in 41 s. Output recordsToolchain : Apple Swift version 6.4 (swiftlang-6.4.0.33.1 clang-2100.3.33.1)andSDK : /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk (26.5), then✓ Installed. Before the change the same invocation exited 1 at the toolchain probe on this host, having compiled nothing.lipo -infoon the assembled binary reportsx86_64 arm64.vtool -show-buildreportsminos 14.0. Both match the script's prior contract.liposwift build -c release --arch arm64 --arch x86_64returned rc 0 in 38 s andlipo -infoon the product reportedx86_64 arm64, with nolipo -createstep involved.--show-bin-pathunder the same flags resolves to.build/out/Products/Release, which is also where the resource bundle is emitted, so one query locates both copy sources.@MainActorrewriting gated to the 14.x SDKSDK 26.5 already annotates SwiftUI, skipping @MainActor patchand still produced a launchable app, so the rewrite is not required on the newer SDK. The14.*branch retains the originalperlinvocation unchanged, so the legacy path is untouched.mac/, so the existing suite must stay green.npm test: 272 test files passed, 2 skipped; 3738 tests passed, 5 skipped, 0 failed; duration 34.30 s. Rannpm run build: rc 0, emittingdist/main.js(2.29 MB),dist/parse-worker.js(864.47 KB), and the dash bundle.Environment
Verified on macOS 27.0, Apple Silicon, with no swift.org toolchain installed.
xcode-select -p/Library/Developer/CommandLineToolscom.apple.pkg.CLTools_Executablesversion 27.0.0.0.1787197235libObservationMacros.dylib,libSwiftMacros.dylibonly; nolibSwiftUIMacros.dylibanywhere under CLT or its SDKlibSwiftUIMacros.dylib~/Library/Developer/ToolchainsNote that
xcode-selectstill points at the Command Line Tools in the verified run. The script selects Xcode's SDK explicitly, so a globalxcode-selectswitch is not required and neither issudo xcodebuild -license accept.Notes
The script's header comment is unchanged and still describes the Sonoma case it was written for; the
14.xpath behaves exactly as before.The
x86_64slice was cross-compiled and not executed; only thearm64slice was run.Companion pull request #1263 fixes a separate runtime defect in
CodeburnCLI.swift. The two do not overlap in files. This one is a prerequisite for building and verifying that change locally on a modern toolchain, so it should land first.Testing
npm testpassesnpm run buildsucceedsReal-data check: the app the fixed script assembles was installed to
~/Applications/CodeBurnMenubar.app, launched, and rendered a flame icon with a live dollar figure in the menu bar against the local corpus, with itscodeburn serve --stdiochild confirmed alive by ppid.Files (1)