Skip to content

feat: report linked libraries whose Git metadata enters the fingerprint - #615

Merged
janicduplessis merged 2 commits into
mainfrom
@janic/issue-595-linked-library-git
Sep 10, 2026
Merged

janicduplessis merged 2 commits into
mainfrom
@janic/issue-595-linked-library-git

Conversation

@janicduplessis

@janicduplessis janicduplessis commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Description

stim doctor now names any .git inside a directory the native fingerprint hashes, such as a symlinked link: or file: library under node_modules, and prints the exact .fingerprintignore lines for it.

A linked dependency is a symlink into another checkout, so that checkout's .git lands in a directory @expo/fingerprint hashes whole. Git rewrites that metadata on every commit, checkout, or worktree of the linked checkout, so workspaces rarely compute the same fingerprint even when the library's native sources match, and every worktree compiles instead of reusing the artifact. Issue #595 measured this on the react-native-safe-area-context example: 231 differing files, all under the linked package's .git, and equal fingerprints once that one path was ignored.

Solution

The check first looks for a symlinked package whose target holds a .git entry in every node_modules from the app directory up to the repository root, so hoisted workspace links count; that is a handful of lstat calls. only then does it run the project fingerprint once in debug mode and walk its dir sources, reporting a directory only when the fingerprint's own debug output shows it hashed a .git child. The printed path is therefore the one the fingerprint uses (the node_modules alias or a real path, whichever autolinking reported), packages that never enter the fingerprint are not mentioned, and a .git the project already excludes through .fingerprintignore or fingerprint.config.js is not reported, whatever pattern it used. It inspects the main checkout like the sibling doctor checks and honours --platform. fingerprintProject also passes silent: true now, because with DEBUG set @expo/fingerprint prints profiling lines to stdout, which would break doctor --json.

Two lines are printed per directory, <dir>/.git and <dir>/.git/**/*. The installed @expo/fingerprint matches ignore patterns per file and only skips a directory for patterns ending in /**/*, /** or /, so the first form covers the pointer file a worktree has and the second the directory a main checkout has.

Doctor does not write the file. The remedy holds only when the native build does not read Git state, which is the project's judgment, so the note says to ignore those entries alone, never the package. The guide's fingerprint section, the agent guide, and the commands page describe the finding.

Test plan

  • doctor.test.ts drives detectLinkedLibraryGitMetadata with a stub fingerprint whose debug output lists a scoped package that hashed .git, one that did not, and android. It asserts only the first is named with both ignore lines, that --platform, debug and silent reach the fingerprint call, the plural form with a real-path source, silence once no directory hashes .git, silence on a failing fingerprint, that no fingerprint runs when no node_modules holds a symlinked package with .git, and a hoisted link found from an app directory inside a repository.
  • Ran the built CLI at 11c0ec9 against the real safe-area-context example with its linked native library. doctor --platform android --json reported only linked-library-git-metadata before the narrow ignore; after applying its exact printed .git and .git/**/* entries, findings were empty. Both outputs parsed as one JSON payload with DEBUG=expo:fingerprint. The disposable fixture was restored to its clean baseline afterward; no native build or original repository changed.

Fixes #595

@janicduplessis
janicduplessis force-pushed the @janic/issue-595-linked-library-git branch 4 times, most recently from 796edd3 to 7a07a77 Compare September 10, 2026 03:46
A link: or file: dependency is a symlink into another checkout, so its
.git lands under node_modules where @expo/fingerprint hashes it. Creating
a worktree rewrites that metadata, and the main checkout and its
worktrees never agree on a fingerprint even when the library's native
sources match. Doctor now names the exact node_modules/<package>/.git
entry to list in .fingerprintignore when the native build does not read
Git state.

Fixes #595
@janicduplessis
janicduplessis force-pushed the @janic/issue-595-linked-library-git branch from 7a07a77 to 11c0ec9 Compare September 10, 2026 03:52
@janicduplessis
janicduplessis marked this pull request as ready for review September 10, 2026 04:04
@janicduplessis

Copy link
Copy Markdown
Collaborator Author

CI note: GitHub stopped creating pull_request runs for this branch after the 03:39Z push (a reopen did not re-trigger them either). A workflow_dispatch run of ci.yml on the exact head commit 11c0ec9 passed: https://github.com/appandflow/stim/actions/runs/34435144431. A fresh push should re-attach checks to the PR.

@janicduplessis

Copy link
Copy Markdown
Collaborator Author

Fresh independent code review at 11c0ec9 is clear: actual Expo ignore-matcher checks cover .git pointer files/directories and preserve native sources. One evidence correction before merge: the Trailhead run has no linked library, so the new gate skips fingerprinting; it cannot prove the fingerprint call runs inside doctor as the body says. Please correct that sentence and preferably run the built doctor on an affected linked-library fixture, showing the finding before the narrow ignore and no finding afterward. Do not overlap native builds on this Mac; doctor-only metadata is fine. Root app-config607 full checks now complete; PR forthcoming.

@janicduplessis

Copy link
Copy Markdown
Collaborator Author

Codex completed the missing real affected-project check at11c0ec9: built doctor on the safe-area-context example reported the linked .git note before its exact ignore, then findings[] afterward. DEBUG=expo:fingerprint stayed off stdout, both outputs parsed as one JSON object. Disposable fixture restored clean. I replaced only the inaccurate Trailhead validation bullet with these observed results; source branch unchanged. Independent code review is clear. Proceeding with merge after exact-head required CI verification.

@janicduplessis

Copy link
Copy Markdown
Collaborator Author

PR615 is currently merge-conflicted and has no exact-head checks, so it cannot merge yet. I am taking the integration follow-up in a separate local checkout: merge current main without rewriting your commits, resolve overlapping guide/config additions, rerun checks and fresh review, then push only as a normal fast-forward. No force-push; a concurrent remote update will be preserved and reconciled. The actual linked-library diagnostic is now verified and the body corrected.

@janicduplessis

Copy link
Copy Markdown
Collaborator Author

CI clarification: the PR rollup was empty, but the exact 11c0ec9 head does have a successful manually dispatched CI run (34435144431). The merge conflict still requires an integration commit and new exact-head checks; no prior CI failure is implied.

@janicduplessis

Copy link
Copy Markdown
Collaborator Author

Final independent integration review is CLEAR at b4718a6. Current main config/scheme/reset changes are retained; the sole import conflict was resolved correctly. Reviewer checked the real before/after doctor evidence and single-JSON output. All local checks and required Node22/24/runtime20 CI passed. Merging this exact head.

@janicduplessis
janicduplessis merged commit 89e9280 into main Sep 10, 2026
5 checks passed
@janicduplessis
janicduplessis deleted the @janic/issue-595-linked-library-git branch September 10, 2026 04:27
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.

Linked native-library Git metadata prevents worktree artifact cache reuse

1 participant