fix: sort object keys before JSON.stringify in transformProjectGraphForRust#35298
fix: sort object keys before JSON.stringify in transformProjectGraphForRust#35298omer-za wants to merge 2 commits intonrwl:masterfrom
Conversation
👷 Deploy request for nx-docs pending review.Visit the deploys page to approve it
|
👷 Deploy request for nx-dev pending review.Visit the deploys page to approve it
|
b2ef079 to
0a44149
Compare
|
View your CI Pipeline Execution ↗ for commit 0a44149
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Nx Cloud is proposing a fix for your failed CI:
We applied a prettier formatting fix to packages/nx/src/native/transform-objects.ts to resolve the format:check failure. The configurations line introduced by the PR exceeded prettier's line length limit and needed to be wrapped across multiple lines. This change is purely cosmetic and does not affect the runtime behavior of the sortObjectKeys fix.
Tip
✅ We verified this fix by re-running nx-cloud record -- nx format:check.
diff --git a/packages/nx/src/native/transform-objects.ts b/packages/nx/src/native/transform-objects.ts
index c4164ea1..3f4e03af 100644
--- a/packages/nx/src/native/transform-objects.ts
+++ b/packages/nx/src/native/transform-objects.ts
@@ -36,7 +36,9 @@ export function transformProjectGraphForRust(
inputs: targetConfig.inputs,
outputs: targetConfig.outputs,
options: JSON.stringify(sortObjectKeys(targetConfig.options)),
- configurations: JSON.stringify(sortObjectKeys(targetConfig.configurations)),
+ configurations: JSON.stringify(
+ sortObjectKeys(targetConfig.configurations)
+ ),
parallelism: targetConfig.parallelism,
};
}
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
Because this branch comes from a fork, it is not possible for us to apply fixes directly, but you can apply the changes locally using the available options below.
Apply changes locally with:
npx nx-cloud apply-locally ois5-3knp
Apply fix locally with your editor ↗ View interactive diff ↗
🎓 Learn more about Self-Healing CI on nx.dev
|
Update — extended the fix to cover We rolled out the original two-field fix ( Root cause: three more fields that flow into the same
These get reordered between CI runs whenever a plugin iterates a Latest commit:
Local verification: applying the patched JS to our 1500-project workspace and running |
…orRust JSON.stringify() preserves JavaScript object key insertion order. The Rust native hasher receives these serialized strings and hashes them as-is for ProjectConfiguration. If any plugin or the target-defaults merge produces options/configurations objects with keys in a different insertion order on different CI runners, the serialized string changes, causing the ProjectConfiguration hash to become non-deterministic. This leads to Nx Cloud cache invalidation and unnecessary task re-execution when rerunning failed CI pipelines. The fix adds a recursive sortObjectKeys helper that normalizes key order before serialization, making the hash independent of JS object construction order. Fixes nrwl#35297
The original change only sorted `target.options` and `target.configurations`
before `JSON.stringify`. Three other fields that flow into the hash via
the same `transformProjectGraphForRust` codepath were still ordered by JS
key insertion:
- `target.inputs` (entries can be objects: { env }, { runtime },
{ externalDependencies })
- `target.outputs` (rare, but objects are valid)
- `projectNode.data.namedInputs` (top-level keys + nested objects)
In production we still see ProjectConfiguration cache misses across CI
runs that build the same logical graph in different orders (Map vs.
plain-object iteration in inferred plugins, async resolution races, etc.).
Extending `sortObjectKeys` to these fields restores cache hits.
Also adds tests asserting determinism for each field individually plus
the full-graph case.
Co-authored-by: Cursor <cursoragent@cursor.com>
f7606bc to
e2bacb0
Compare
Summary
Fixes #35297
JSON.stringify()preserves JavaScript object key insertion order. IntransformProjectGraphForRust, targetoptionsandconfigurationsare serialized withJSON.stringify()and passed to the Rust native hasher, which hashes them as-is forProjectConfiguration.If any Nx plugin or the target-defaults merge step produces these objects with keys in a different insertion order on different CI runners, the serialized string changes, causing the
ProjectConfigurationhash to become non-deterministic. This leads to:The Fix
Adds a recursive
sortObjectKeyshelper that normalizes key order before serialization, making theProjectConfigurationhash independent of JS object construction order.Note: The Rust hasher already sorts target names before hashing (making the hash independent of target insertion order), but it does not normalize the content of the stringified
options/configurations. This fix closes that gap.Evidence
Tested locally using the native
HashPlanner+TaskHasherAPIs:Without fix
{"cwd":"...","env":{...},"command":"jest","passWithNoTests":true}10839486168096120338{"passWithNoTests":true,"command":"jest","env":{...},"cwd":"..."}14689511571626992510With fix
Reversed ALL target orders AND ALL options/configurations key orders for ALL projects across the entire project graph:
ProjectConfigurationentries, 0 mismatchesRelated
This is analogous to the fix in commit
db31f30which sorted keys before hashing forAllExternalDependenciesviahashObject.