You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ci(test-app): key the fixture build cache on the Expo fingerprint
The fixture cache key was a hand-maintained `hashFiles` list, and it already
had a hole: `accessory-setup.config.json` was absent from it, but app.config.js
reads that file into `NSAccessorySetupBluetoothServices`. Editing the service
UUID changed the Info.plist without changing the key, so the cache would serve
an app built from the old value. The Expo fingerprint enumerates the native
inputs instead of us restating them, and covers that file as a config-plugin
source. @expo/fingerprint ships a CLI that prints it; passing no --platform is
deliberate, since its default is what @expo/cli hashes for its own build cache,
so the key agrees with what `expo run:*` looks up.
The fingerprint hashes the native build and nothing else — it exists to decide
native/JS compatibility, so the bundle this Release build embeds is outside it.
Rather than bolt a second hash onto the key to cover the JS, the build is now
repacked: @expo/repack-app rebuilds the bundle from current source and swaps it
into the cached app. Keying on the fingerprint alone is then exactly right, and
a JS-only change costs seconds instead of another ~22 minute build. Measured on
a real Release bundle: 13.5s, Hermes bytecode preserved, native binary byte
-identical. --js-bundle-only is precisely the gap the fingerprint leaves; every
other thing repack would refresh is hashed into the key already, so a hit proves
it unchanged.
Gitignoring /ios and /android is what makes this work across machines, and it is
all that is needed: @expo/fingerprint resolves the project workflow by asking the
VCS whether the platform markers are ignored, and appends `ios/**/*` and
`android/**/*` itself once it concludes CNG. The prebuild output exists on
developer machines but never in a fresh CI checkout, so without that the
fingerprint was machine-specific — ab99f403 locally against 14d08bcd in CI — and
no cache could ever have been shared between the two.
On top of that, serve developers the dev-client build from Actions artifacts
keyed on the same fingerprint, so a checkout with no native changes installs
CI's binary instead of building. iOS builds on a macOS runner, Android on a
Linux one. Android has no build-only mode — the CLI resolves a device before
gradle because a debug build is narrowed to that device's ABI — so the emulator
is there to satisfy it, and --all-arch opts out of the narrowing so the artifact
is not pinned to the emulator's x86_64. Every AVD on the maintainer's Apple
Silicon machine reports arm64-v8a only, so a single-ABI artifact could not serve
both it and CI.
Only CI publishes: GitHub has no API for creating an artifact outside a workflow
run, and the fingerprint does not capture the toolchain, so one known toolchain
is the only safe producer. The provider refuses release builds outright rather
than hand back a stale bundle, and a missing token or a failed lookup only ever
costs a local build.
The provider must set a User-Agent explicitly. @expo/cli replaces global fetch
with fetch-nodeshim, which sends none, and GitHub rejects those with a 403 —
which the error handling would otherwise have swallowed into "build locally"
forever.
Finally, `expo run:ios --output` replaces the DerivedData scan in the build
step. `find ... | head -1` picked an arbitrary bundle when more than one
matched; this machine has two Release-iphonesimulator AgentDeviceTester.app
builds, so that was reachable rather than theoretical.
0 commit comments