Skip to content

fix(ios): stop committing path-dependent Podfile.lock checksums - #469

Merged
RonenMars merged 1 commit into
mainfrom
fix/podfile-lock-path-noise
Jul 31, 2026
Merged

fix(ios): stop committing path-dependent Podfile.lock checksums#469
RonenMars merged 1 commit into
mainfrom
fix/podfile-lock-path-noise

Conversation

@RonenMars

Copy link
Copy Markdown
Owner

Why

bundle exec pod install rewrites three SPEC CHECKSUM entries in ios/Podfile.lock on every machine, and they never converge.

A pod's SPEC CHECKSUM is the SHA1 of its generated podspec, and exactly three pods bake the checkout's absolute path into theirs:

Pod Baked-in absolute path
hermes-engine HERMES_CLI_PATH…/node_modules/hermes-compiler/hermesc/osx-bin/hermesc
ExpoModulesCore precompiled tarball file:///…/expo-modules-core/prebuilds/output/debug/…tar.gz, plus the same path inside prepare_command
ExpoWidgets bundle copy script → …/node_modules/expo-widgets/ios/../bundle/build/ExpoWidgets.bundle

Verified locally: the SHA1 of each Pods/Local Podspecs/<pod>.podspec.json equals the value in Podfile.lock, and rewriting only the repo path changes all three and nothing else.

hermes-engine      here=9ebc7f0a6313  other-worktree=52f5a0ae7268  DIFFERENT
ExpoModulesCore    here=1d8a6b6a8e43  other-worktree=70dd546de9d6  DIFFERENT
ExpoWidgets        here=644574099c60  other-worktree=39e24930b454  DIFFERENT

So the committed values can only ever match one checkout path.
git log -- ios/Podfile.lock shows the ping-pong exactly as predicted: github-actions[bot] commits (the runner's checkout path) alternating with local commits, flipping those three lines each time.

This also corrects the premise of #455. bundle exec does pin the COCOAPODS: line, but it cannot stabilise these three — they are a function of the path, not of the CocoaPods version.

What changed

scripts/reset-podfile-lock-path-noise.sh reverts ios/Podfile.lock when those three lines are the only drift, and runs right after pod install in scripts/ship-ios.sh, the deploy workflow, and scripts/dev-tunnel-native-reset.sh.

Real drift is left alone: a genuine pod change also moves that pod's version line, so the diff is never limited to those three checksums.
This keeps land-version-bump.sh and the deploy job's NEED_LOCK check working unchanged — they simply stop seeing a dirty lockfile when nothing real moved.

CLAUDE.md now describes the actual cause instead of attributing the churn to bare pod install.

Verification

  • npm run test:scripts — 59 passed, including 4 new cases (noise-only reverts, mixed drift preserved, staged noise reverted, clean lockfile is a no-op).
  • End-to-end against the real 3200-line ios/Podfile.lock: mutating only the three checksums is reverted; mutating ReachabilitySwift alongside them is preserved.
  • npm run lint — 0 errors.
  • deploy.yml parses; bash -n clean on all three touched scripts.

ExpoModulesCore, ExpoWidgets and hermes-engine generate their podspecs at install time and bake the checkout's absolute path into them (HERMES_CLI_PATH, the precompiled ExpoModulesCore tarball file:// URL, the ExpoWidgets bundle copy script).
A pod's SPEC CHECKSUM is the SHA1 of its generated podspec, so those three values are a function of where the repo is checked out and differ for every worktree, machine and CI runner.
bundle exec pins the COCOAPODS: line but does nothing for these, so ios/Podfile.lock kept ping-ponging between whoever ran pod install last — the usual source of ExpoWidgets/hermes-engine conflicts on rebases.

Add scripts/reset-podfile-lock-path-noise.sh, which reverts ios/Podfile.lock when those three lines are the only drift, and run it right after pod install in the ship script, the deploy workflow and the native dev reset.
A genuine pod change also moves that pod's version line, so real drift is left alone.
@RonenMars
RonenMars merged commit 15c8c52 into main Jul 31, 2026
10 checks passed
@RonenMars
RonenMars deleted the fix/podfile-lock-path-noise branch July 31, 2026 23:29
RonenMars added a commit that referenced this pull request Aug 1, 2026
Follow-up to #469.

`npm run dev:reset` was the last bare `pod install` left in the repo.
Without `bundle exec` it resolves a Homebrew CocoaPods instead of the
Gemfile-pinned 1.16.2 and rewrites the `COCOAPODS:` line in
`ios/Podfile.lock` — exactly what the pin exists to prevent, and what
#455 set out to fix everywhere else.

It now also runs `scripts/reset-podfile-lock-path-noise.sh` afterwards,
matching `ship-ios.sh`, `dev-tunnel-native-reset.sh` and the deploy
workflow, so a full dev reset leaves the lockfile clean instead of
carrying this checkout's path-dependent checksums.

The remaining bare `pod install` mentions in the repo are all inside
historical docs (`docs/upgrade-to-expo-56.md`, `docs/research/`,
runbooks) describing past sessions, so they are left alone.
RonenMars added a commit that referenced this pull request Aug 1, 2026
## Why

The runbook described itself as a "live procedure — edit it as PRs
land", but the chain it sequenced is gone.

Verified 2026-08-01 — every PR it orchestrated is `CLOSED` with
`mergedAt=null`, i.e. **closed, not merged**:

```
#339 #341 #343 #345 #346 #347 #353 #354 #355 #356 #357 #358 #359
#360 #361 #362 #363 #364 #368 #372 #373 #376 #385 #386 #387
```

The work reached `main` through the integration branch instead, and the
PRs were closed as redundant.
The integration branch the runbook treated as proof the set could
coexist, `integration-dev/v1.0.0-2026-07-22`, has been deleted.

That invalidates the phase order, the per-PR loop, the pre-flight sweep,
the decision to close `#355`, the conflict **A–I** resolutions, and the
definition of done.
The only PR row still accurate is `#291`, which is still open and still
excluded by standing request.

## What changed

The body becomes a tombstone: what the runbook was for, what actually
happened, and a table pointing at where each still-true trap now lives.
The file is kept rather than deleted so inbound links resolve and nobody
rebuilds the same plan from the merge report.

Two of its four traps were already in `docs/troubleshooting.md` (the
`.claude/` worktree gotcha and serial `SessionScreen` verification). The
other two are lifted there now under a new "CI signals" section, because
both are repo-wide facts rather than properties of those branches:

- a `[skip-ci]` head commit reports every required context green in
seconds having run nothing
- a stacked PR (base ≠ `main`) still gets full CI here, since
`test.yml`'s `pull_request` trigger has no `branches:` filter

Conflict **J** is obsolete for a separate reason: those
`ios/Podfile.lock` checksums encode the checkout's absolute path, so
resolving it was never meaningful. #469 added
`scripts/reset-podfile-lock-path-noise.sh`, which drops that drift.

## Two live findings, one already fixed

The runbook's `[skip-ci]` section claimed the required contexts were
`Type check`, `Unit tests`, `Integration tests`, `Lint`. The ruleset
actually required **only `Lint` and `Type check`** — unit and
integration tests could not block a merge at all, which makes the
`[skip-ci]` trap worse than documented, not better.

Its one outstanding post-merge action (add `i18n` to required checks,
never done since #368) is now complete: `Unit tests`, `Integration
tests` and `i18n` were added to the `main` ruleset on 2026-08-01.
Confirmed safe first — all three report `pass` on a real `chore/bump-**`
version-bump PR (#472), so the deploy pipeline's bumps are not
deadlocked.

Note: `.github/workflows/test.yml:25` carries the same wrong claim about
which contexts are required. Left alone here to keep this docs-only.
RonenMars added a commit that referenced this pull request Aug 1, 2026
…dates (#488)

Dependabot's #460 rebased from `main` onto `land/integration-prep`, so it lands with the rest of the integration work.

Bumps `@modelcontextprotocol/sdk` 1.29.0 to 1.30.0, `fast-uri` 3.1.2 to 3.1.5 and `hono` 4.12.25 to 4.12.33, all dev-only transitive dependencies, with `package.json` untouched.

The PR title advertises `@hono/node-server` 1.19.14 to 2.0.12 carrying GHSA-9mqv-5hh9-4cgg, but the diff does not do that.
The SDK bump widens the declared range from `^1.19.9` to `^1.19.9 || ^2.0.5`, and widening a range does not move a pin that already satisfies it, so `@hono/node-server` stays at 1.19.14.
That advisory never reached the shipped app because the package is dev-only; taking the fix is a separate `npm update` that this bump newly makes possible.

`npm ci` produced no further lockfile drift, and `bundle exec pod install` moved only the three path-dependent SPEC CHECKSUM lines documented in #469, so `ios/Podfile.lock` is deliberately excluded.
RonenMars added a commit that referenced this pull request Aug 1, 2026
…ver (#489)

Expo MCP is now configured at user scope as `expo-local` for both Claude Code and Codex, which makes the per-project install redundant rather than merely unused.

`expo-mcp` was a devDependency whose only consumer was the `expo_local` server in `.codex/config.toml`, invoked by relative path as `./node_modules/.bin/expo-mcp`, so the package and that entry go together.
It pulled in `@expo/mcp-tunnel`, `@modelcontextprotocol/sdk`, `@hono/node-server`, `hono` and `fast-uri` — 829 lockfile lines and roughly 38 MB of `node_modules` that nothing else declares and no source file imports.
It was also the origin of the churn in #488; every package that PR bumped is removed here.

`AGENTS.md` and `CLAUDE.md` record the global setup so the dependency does not get re-added: it attaches to a local Metro dev server on `http://127.0.0.1:8081`, it is for screenshots, device logs and simulator verification, and remote tunneling via `--mcp-server-url` / `@expo/mcp-tunnel` is not to be used unless asked.

Nothing shipped is affected — the subtree was dev-only, never entered the bundle, and was never invoked by an npm script or CI job.
`bundle exec pod install` moved only the three path-dependent SPEC CHECKSUM lines documented in #469, so `ios/Podfile.lock` is deliberately excluded.
RonenMars added a commit that referenced this pull request Aug 1, 2026
…dates (#488)

Dependabot's #460 rebased from `main` onto `land/integration-prep`, so it lands with the rest of the integration work.

Bumps `@modelcontextprotocol/sdk` 1.29.0 to 1.30.0, `fast-uri` 3.1.2 to 3.1.5 and `hono` 4.12.25 to 4.12.33, all dev-only transitive dependencies, with `package.json` untouched.

The PR title advertises `@hono/node-server` 1.19.14 to 2.0.12 carrying GHSA-9mqv-5hh9-4cgg, but the diff does not do that.
The SDK bump widens the declared range from `^1.19.9` to `^1.19.9 || ^2.0.5`, and widening a range does not move a pin that already satisfies it, so `@hono/node-server` stays at 1.19.14.
That advisory never reached the shipped app because the package is dev-only; taking the fix is a separate `npm update` that this bump newly makes possible.

`npm ci` produced no further lockfile drift, and `bundle exec pod install` moved only the three path-dependent SPEC CHECKSUM lines documented in #469, so `ios/Podfile.lock` is deliberately excluded.
RonenMars added a commit that referenced this pull request Aug 1, 2026
…ver (#489)

Expo MCP is now configured at user scope as `expo-local` for both Claude Code and Codex, which makes the per-project install redundant rather than merely unused.

`expo-mcp` was a devDependency whose only consumer was the `expo_local` server in `.codex/config.toml`, invoked by relative path as `./node_modules/.bin/expo-mcp`, so the package and that entry go together.
It pulled in `@expo/mcp-tunnel`, `@modelcontextprotocol/sdk`, `@hono/node-server`, `hono` and `fast-uri` — 829 lockfile lines and roughly 38 MB of `node_modules` that nothing else declares and no source file imports.
It was also the origin of the churn in #488; every package that PR bumped is removed here.

`AGENTS.md` and `CLAUDE.md` record the global setup so the dependency does not get re-added: it attaches to a local Metro dev server on `http://127.0.0.1:8081`, it is for screenshots, device logs and simulator verification, and remote tunneling via `--mcp-server-url` / `@expo/mcp-tunnel` is not to be used unless asked.

Nothing shipped is affected — the subtree was dev-only, never entered the bundle, and was never invoked by an npm script or CI job.
`bundle exec pod install` moved only the three path-dependent SPEC CHECKSUM lines documented in #469, so `ios/Podfile.lock` is deliberately excluded.
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