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
{{ message }}
Repository navigation
Promotion's frozen copy is refused on Node 24.22 (cpSync errorOnExist into its own temp folder) #2124
Section: ci
Rank: T2 · Sensitive: yes (deploy, promotion) · Builder: Opus high · Size: under 50 product lines, one PR · Depends on: the .nvmrc pin PR (lane OA-Fix-Node2422).
What is wrong
Under Node 24.22.0 every promotion is refused before anything is migrated or promoted:
{"kind":"refused","reason":"ops-astro-0123456789ab could not be copied: ERR_FS_CP_EEXIST; nothing was migrated or promoted"}
frozenCopy makes the copy's folder with mkdtempSync and then copies the artefact into that same, now existing, folder with errorOnExist: true, force: false (scripts/ops/served-copy.ts:73-75). Node 24.22.0 shipped nodejs/node#64124 ("fs: throw on existing dir in cpSync with errorOnExist", commit f3019b522a): onDir in lib/internal/fs/cp/cp-sync.js:136-145 now throws ERR_FS_CP_EEXIST when the destination folder exists, where 24.21.0 merged into it. The catch at served-copy.ts:80-84 turns that into the refusal above.
Red on 24.22.0, green on 24.21.0 (lane OA-Fix-Node2422, Mac arm64, checksummed nodejs.org binaries, main 49c7759): tests/ci/promotion-frozen-copy.test.ts (10), tests/ci/promotion-trust-chain.test.ts (2), tests/review/main-b1-p12-promotion-relative-link-proof.test.ts (1); 12 failed / 30 on 24.22.0, 30/30 on 24.21.0. CI moved to 24.22.0 on 18 Oct because .nvmrc said 24 (job 118178985475, runner image 20261018.339.1). The pin PR holds .nvmrc at 24.21.0 until this lands.
It is not only CI: node scripts/ops/promote.mjs runs on whatever Node the operator's host has, so a host on 24.22.0 or later refuses every promotion (fail-closed: nothing migrated, production's link unchanged).
What to build
frozenCopy copies into a name that does not exist yet inside its private temporary folder (for example join(copy, 'bytes')), keeping errorOnExist: true, force: false so an entry already there is still refused, and the rename, digest check and served modes act on that path. Or another shape that keeps the same guarantees; never drop errorOnExist.
Check servedModes still sets every mode under 24.22.0's other cpSync change, fs: copy directory trees for fs.cp() on the thread pool nodejs/node#65488 ("give directories created by cpSync the source directory's mode"): the umask-077 and closed-to-others cases in promotion-frozen-copy.test.ts cover it.
Put .nvmrc back to 24 in the same PR, so CI follows the Node 24 line again.
Done when
The three files above are green on 24.22.0 and 24.21.0, with no test edited to get there.
.nvmrc reads 24 and the merge group's shard 3 is green on the current Node 24.
Not in scope
scripts/ops/release.ts:64 and scripts/ops/web-deploy.ts:162 call cpSync without errorOnExist, so #64124 does not touch them.
Activity