diff --git a/.changeset/30467-ledger-adoption-staging-dir.md b/.changeset/30467-ledger-adoption-staging-dir.md new file mode 100644 index 00000000000..c0ab0f30047 --- /dev/null +++ b/.changeset/30467-ledger-adoption-staging-dir.md @@ -0,0 +1,8 @@ +--- +'@objectstack/cloud-connection': patch +--- + +Clause-②: no +Metadata-protocol: none + +The one-time copy of the working-directory install ledger into its `OS_HOME` location (#30217) now creates its staging directory with an exclusive `mkdirSync` of a randomly named sibling instead of `mkdtempSync`. Behaviour is unchanged: the copy still lands in a unique sibling of the ledger directory, is renamed into place, and is removed if the copy fails. This repairs the red `main` build (#30467), where the repository's in-tree scratch scan could not read the `mkdtempSync` base. diff --git a/packages/cloud-connection/src/local-manifest-source.ts b/packages/cloud-connection/src/local-manifest-source.ts index 6cd1bd09566..bf4f8b466a5 100644 --- a/packages/cloud-connection/src/local-manifest-source.ts +++ b/packages/cloud-connection/src/local-manifest-source.ts @@ -30,9 +30,9 @@ * the same ledger without going through HTTP. */ +import { randomBytes } from 'node:crypto'; import { - copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, - writeFileSync, + copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync, } from 'node:fs'; import { homedir } from 'node:os'; import { basename, dirname, join, resolve } from 'node:path'; @@ -330,7 +330,14 @@ export class LocalManifestSource { const files = readdirSync(from).filter((name) => name.endsWith('.json')); if (files.length === 0) return null; mkdirSync(dirname(this.dir), { recursive: true }); - const staging = mkdtempSync(join(dirname(this.dir), `.${basename(this.dir)}.adopting-`)); + // A unique sibling, created exclusively: a non-recursive mkdirSync + // throws EEXIST rather than reuse a directory that is already there, + // which is the guarantee mkdtempSync gave. mkdtempSync is not used + // because the repo's in-tree scratch scan holds every mkdtempSync to a + // base it can read from source, and this base is the ledger location + // resolved at run time. + const staging = join(dirname(this.dir), `.${basename(this.dir)}.adopting-${randomBytes(6).toString('hex')}`); + mkdirSync(staging); try { for (const name of files) copyFileSync(join(from, name), join(staging, name)); renameSync(staging, this.dir);