Skip to content

fix(swiftpm): actually read the artifacts version pin back - #57762

Closed
chrfalch wants to merge 2 commits into
mainfrom
chrfalch/spm-read-version-pin
Closed

fix(swiftpm): actually read the artifacts version pin back#57762
chrfalch wants to merge 2 commits into
mainfrom
chrfalch/spm-read-version-pin

Conversation

@chrfalch

Copy link
Copy Markdown
Collaborator

Summary:

spm add --version <ver> pinned a value that nothing ever read back.

The marker write has been there all along, and so has the reader — readArtifactsVersionOverride() in spm/generate-spm-xcodeproj.js, exported and unit-tested. It just had zero production callers. Every reference to it was a test.

determineVersion — the only resolver — went straight from the flag to package.json:

let version = args.version;      // --version
if (version == null) {
  version = pkgJson.version;     // react-native/package.json; marker never consulted
}

That value picks which artifact slots the project gets wired to. So:

  1. spm add --version 0.88.0-nightly-… → wires the nightly's slots, pins the label ✅
  2. spm update (no flag) → silently resolves package.json's version instead, re-pointing the project at different slots, while the marker still claims the nightly ❌

--version was effectively single-use. In this monorepo package.json is 1000.0.0, which has no published artifacts, so a flagless run after a pinned add fails outright — which is why the standing advice has been to pass --version on every invocation. That advice was working around this bug.

Fix: insert the pin between the two existing sources.

--version  →  pinned override  →  react-native/package.json

15 lines of logic. spm download and the scaffold path pick it up for free, since both consume the same resolved value. Because this is persistent state, one line is logged when the pin is the source, so a stale pin is diagnosable instead of silent; there is still no way to clear it short of deinit.

Three comments were actively wrong and are corrected here: the reader's doc block, the marker-field comment, and findInjectedXcodeproj's comment all asserted that the build-time sync calls this via readArtifactsVersionOverride. It does not and never did — the sync action returns before artifacts are resolved at all. Those comments are what made the gap invisible; they misled me while investigating.

Changelog:

[Internal] [Fixed] - SwiftPM: spm --version now sticks, so a later run without the flag keeps using the pinned artifact version

Test Plan:

yarn jest packages/react-native/scripts31 suites, 684 tests, all green.

New tests, written red first — the load-bearing one failed with Expected: "0.80.0" / Received: "1000.0.0", i.e. exactly the reported bug:

  • --version given → wins, even with a different value pinned
  • no flag, pin present → the pinned version is used
  • no flag, no pin → package.json's version (unchanged behaviour)
  • no flag, corrupt or absent marker → package.json's version, no throw
  • the log line fires only when the pin is the source

The three fallback cases passed before the fix too, which is the point — they pin today's behaviour so this change can't regress it.

Note on landing order

Touches setup-apple-spm.js and spm/generate-spm-xcodeproj.js, which #57744, #57756 and #57757 also touch. All are cut independently from main; whichever lands first, I'll rebase the rest. #57756 is the closest relative — it does the same wiring for --config-command, which had the identical write-only-pin shape.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 30, 2026
@chrfalch
chrfalch requested review from cipolleschi and Copilot and removed request for Copilot July 30, 2026 09:04
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 30, 2026
@chrfalch chrfalch changed the title SPM: actually read the artifacts version pin back fix(swiftpm): actually read the artifacts version pin back Jul 30, 2026
@meta-codesync

meta-codesync Bot commented Jul 31, 2026

Copy link
Copy Markdown

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D114318107.

chrfalch and others added 2 commits August 1, 2026 09:13
`spm add --version <ver>` / `spm update --version <ver>` already wrote an
`artifactsVersionOverride` into the `.spm-injected.json` marker, and
`readArtifactsVersionOverride` already existed to read it back — but nothing
ever called it. Only the write half was wired; every reference to the reader
was a test.

The sole resolver, determineVersion, went straight from the `--version` flag to
node_modules/react-native/package.json. So after `spm add --version X`, a later
flagless `spm update` silently re-pointed the project at package.json's version
instead, while the marker went on claiming X. `--version` was effectively
single-use. In this monorepo package.json is 1000.0.0, which has no published
artifacts, so a flagless run after a pinned add fails outright — hence the
standing advice to pass `--version` on every invocation.

Insert the pin between the two existing sources:

    --version  ->  pinned override  ->  react-native/package.json

`spm download` and the scaffold path pick it up for free, since both use the
same resolved value. Since this is persistent state, log one line when the pin
is the source, so a stale pin is diagnosable rather than silent; there is still
no way to clear it short of `deinit`.

Three comments claimed the build-time sync read this override via
readArtifactsVersionOverride. It does not, and never did — the sync action
returns before artifacts are resolved at all. They now name the real consumer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The SwiftPM docs still described `--version` as "RN version (default: from
package.json)", which stopped being true once determineVersion started reading
the `artifactsVersionOverride` pin back out of `.spm-injected.json`. Document
the real resolution order and why it matters:

- The `--version` row in "CLI Options" now names all three sources in order —
  the flag, the pin a previous `--version` wrote into the marker, then
  node_modules/react-native/package.json — and says you pass it once.
- A new "Pinning the React Native version" subsection explains the failure the
  pin prevents, which is not self-evident: the resolved version selects which
  artifact slots the project is wired to, so a flagless run falling back to
  package.json re-points the project at different slots while the marker still
  advertises the pinned one. It also states that `deinit` drops the marker and
  with it the pin, so a later `add` resolves package.json again unless you pass
  `--version`.
- The `.spm-injected.json` row in "What to commit" described the marker only as
  a record of the edits `add` made. One appended sentence makes its second role
  visible; the row is otherwise untouched, since sibling PRs edit it too.

No advice to repeat `--version` on every invocation existed in the docs to
correct — that workaround was only ever passed on verbally.

The file is not Prettier-formatted on this branch, so it was deliberately not
reformatted: prose stays wrapped near 80 columns and table rows stay on one
line, matching the surrounding style.

[Internal] - Document the `spm --version` pin and its resolution order

Docs-only change; no code or tests touched. Verified against the branch's own
diff that the marker key is `artifactsVersionOverride` (read by
`readArtifactsVersionOverride` in scripts/spm/generate-spm-xcodeproj.js) and
that `determineVersion` in scripts/setup-apple-spm.js resolves flag → pin →
node_modules/react-native/package.json.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chrfalch
chrfalch force-pushed the chrfalch/spm-read-version-pin branch from dd7f1f3 to a56f200 Compare August 1, 2026 07:21
@meta-codesync meta-codesync Bot closed this in b0b5409 Aug 10, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Aug 10, 2026
@meta-codesync

meta-codesync Bot commented Aug 10, 2026

Copy link
Copy Markdown

@cipolleschi merged this pull request in b0b5409.

meta-codesync Bot pushed a commit that referenced this pull request Aug 19, 2026
…correct the rest (#58006)

Summary:
Rebased onto `main` after #57757, #57756, #57762 and #57744 landed. Because this
PR renames `__doc__` and Prettier-formats every file, a hunk-level rebase was the
wrong tool — it stopped on the second of nine commits and the reformat commit then
conflicted with everything after it. So the history is **replayed** on top of the
new `main` instead, and reordered to put Prettier *before* the content edits, which
makes the substantive diff reviewable rather than tangled with reflow.

Verified nothing those four PRs added was lost: every heading on `main` survives
except the two this PR deliberately renames, and their facts — the
`artifactsVersionOverride` pin, the autolinking-config-command pin, the
pre-injection build-setting values, and the promoted-scalar caveat — are carried
into the new "Files the tool touches" table rather than sitting in a row this PR
deletes.

Housekeeping and accuracy pass over `packages/react-native/scripts/spm/__docs__`.
Docs only — no code or test changes.

**Rename.** `scripts/spm/__doc__` was the only `__doc__` directory in the repo;
every other subsystem uses `__docs__` (27+ directories, and the convention
documented in `__docs__/GUIDELINES.md`). The misspelling also meant these files
were **published to npm**: `package.json` includes `scripts/spm` in `files` and
excludes `!**/__docs__/**`, which the misspelled directory dodged.

**Removed two superseded documents.** `rfc-spm-xcframework.md` (707 lines) was a
stale ancestor of RFC0994, still describing `spm init`, xcodeproj *generation*, the
`.xcodeproj.legacy` rename migration, a `spm clean` command that does not exist,
stub `Package.swift` files and VFS overlays. `spm-plugins-assessment.md` evaluated
SwiftPM plugins as a replacement for the injected build phases — conclusion valid,
detail stale (it listed a "Prepare VFS Overlay" phase). Both have had their durable
content folded into RFC0994.

**Corrected the remaining docs against the implementation.** The substantive one:
auto-sync is **two** hooks, not one — a scheme pre-action in the app's *shared*
scheme plus the build phase. The docs described only the phase.

Two claims here are corrections of my own earlier drafts, made after testing them
on `private/helloworld` rather than reading the code:

- Neither hook can bootstrap a clean checkout. With `build/` deleted,
  `xcodebuild -scheme … build` fails in nine lines of log, `Resolve Package Graph`
  first, the pre-action never running. The one-time setup run really is required,
  and the ordering table now shows the measured sequence.
- A sync failure is not unconditionally non-fatal. Exit 2 — a dependency with no
  `Package.swift` — maps to `exit 1` and fails the build deliberately; only other
  non-zero codes warn. As written, the doc also contradicted the exit-2 behaviour
  documented elsewhere in the same file.

Smaller fixes: `hermesvm` → `hermes-engine` (that name is in no code), plugin
`watchPaths` added to the staleness inputs, and the `#auto-sync-build-phase`
anchors left dangling by the heading rename.

**Added a `__docs__/README.md` index**, per `GUIDELINES.md`, plus a **"Files the
tool touches"** table — there was no single answer to what the tool creates or
modifies. Writing it surfaced that `add` creates or appends to `ios/.gitignore`
(documented nowhere), and that `deinit` does not revert that block, anything
`--deintegrate` changed, or a promoted array setting — so the "exact inverse of
`add`" claim needed qualifying in three places.

**Documented `spm.dependencies`**, the SwiftPM analog of a podspec's
`s.dependency`, which was implemented but absent from every doc, and corrected the
scaffold prerequisite: it is `add`/`update` that stops, with a distinct exit code 2,
not the build.

**Prettier-formatted the docs, in its own commit.** These were the only unformatted
markdown files in the repository, so `prettier --list-different "./**/*.md"` goes
from three failures to clean.

## Changelog:

[Internal] - SwiftPM docs: rename `__doc__` to `__docs__`, remove two superseded
documents, and correct the rest against the implementation

Pull Request resolved: #58006

Test Plan:
- `npx prettier --check "packages/react-native/scripts/spm/__docs__/*.md"` passes;
  `npx prettier --list-different "./**/*.md"` is empty for the whole repo.
- All 19 intra-doc anchor links verified against GitHub's slug rules; none dead.
  Every relative path out of the new README resolves, including the root-index hop.
- `npm pack --dry-run --ignore-scripts` lists no `scripts/spm/__docs__/` entries
  while every `scripts/spm/*.js` still ships.
- The Prettier commit is content-neutral: comparing `[A-Za-z0-9]+` token streams
  across it, all three files are identical word for word.
- Behavioural claims traced to source: `VALID_ACTIONS` in `setup-apple-spm.js`;
  `injectOrCreateScheme` / `addPreActionToScheme` and the `RC -eq 2` branch of
  `buildSyncAutolinkingScript` in `generate-spm-xcodeproj.js`; `BUILTIN_FRAMEWORKS`
  in `flavored-frameworks.js`; `generateXCFrameworksPackageSwift` in
  `generate-spm-package.js`; `ensureGitignoreSpmEntries` and its `action =--sanitized--

Reviewed By: fabriziocucci

Differential Revision: D116598871

Pulled By: cipolleschi

fbshipit-source-id: b1822dcc885b9c882ec3a2626addc42553244b51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants