ReviewOS

also looking at this

craft-native/craft

fix(ios): give the generator a runtimeDir option, and run its tests in CI

#108
Merged glennmichael123 wants to merge fix/ios-generator-runtime-dir into main
3 files +272 -12
10 days ago

Four findings from the #103 seam review, each reproduced before being fixed.

The suite depended on the developer's shell

init() read CRAFT_IOS_RUNTIME straight from process.env with no way to override it:

CRAFT_IOS_RUNTIMEbun test src/index.test.ts
unset12 pass, 0 fail
set to an empty dir8 pass, 4 fail

InitOptions.runtimeDir is the escape hatch CLAUDE.md already documents for the sibling variable — AppConfig.craftPath is to CRAFT_BIN what this is to CRAFT_IOS_RUNTIME — and null means no runtime, whatever the environment says. Every init() in the suite now declares it, and the result is identical either way: 22 pass, 0 fail.

And nothing ran that suite

No CI job touched packages/ios. That is how the above survived. The new ios-builder job runs it with CRAFT_IOS_RUNTIME explicitly empty — so it is the job that notices if the dependency returns — and publish-commit gates on it.

installRuntime destroyed a working install before validating the source

rmSync(Runtime/) ran first; the "none of these archives" check ran after. Pointing at an incomplete runtime directory deleted the archives already in place on the way to throwing, leaving project.yml linking -lcraft-ios against a directory that no longer existed. Every SDK is resolved before anything is removed, and there is a test that installs, then fails an install, then asserts the first one survived.

A single simulator slice was copied silently

RUNTIME_ARCHIVES' own comment calls a single-slice archive the break that "only shows up on someone else's laptop" — and the code then shipped one without a word. It still copies, because a one-architecture dev loop is legitimate, but it now names the missing slice and says the project will not link on the other architecture.

Re-running init without a runtime orphaned the archives

The link settings came out of project.yml and Runtime/ stayed on disk, so the directory and the project disagreed. Whatever a run decides, the tree agrees with it.

build() relinked a stale runtime

installRuntime had one caller: init. build never touched Runtime/, and run just calls build. Because the archives are copied rather than symlinked, the dev loop this option exists for did not work — edit Zig, zig build build-ios-all, craft ios run, and xcodebuild relinks the copy from init. The build succeeds, the app launches, the change is absent, and there is no error to chase.

build now refreshes a runtime the project already links, taking the same runtimeDir override. Refresh only: it never installs into a project that links none (those archives would be unreferenced — that is init's decision) and never removes one when the variable is simply unset. Both pinned by tests.

Not covered

The two-slice lipo path needs genuine Mach-O input and lipo does not exist on the Linux runner, so it is not unit-tested. It is covered by building a real generated app, which is how the fat archive was verified when the seam landed.

3 changed files on the files tab, with 0 review threads.

Sign in to comment on this pull request.