Locate stages output in the system temp directory and fails when --out is on another filesystem
Scope and verification boundary
Identified in the unmerged feature work in PR #367, at head cc1b33a7a02351347842dad4ebe9c13278afc166, using an isolated snapshot. This report does not claim a regression in the released version or main branch.
The same-filesystem control below was executed and passed. Cross-filesystem execution has not been performed. The cross-filesystem failure is identified from the staging and rename implementation; no actual EXDEV log is claimed here.
Cause and impact
At this head:
archify/locate/cli.mjs:503 creates the staging directory under os.tmpdir().
- Line 280 backs up existing output files into staging with
fs.renameSync().
- Line 285 commits candidates to
--out with fs.renameSync().
Renaming across filesystem boundaries is unsupported. If --out is on a filesystem different from the system temp directory, both first-time generation and replacement of existing output encounter a cross-filesystem rename. The output path is otherwise a valid writable directory.
Environment
macOS, Node.js v22.23.2. The scripts below were verified with this runtime against the PR #367 snapshot identified above.
Reproduction procedure
From the repository root at the head above, save the script below as locate-exdev-repro.mjs. It creates an isolated Git repository and a fresh output directory, then removes both. The script can live outside the repository, but run it with the repository root as the current working directory.
- Run
node locate-exdev-repro.mjs with LOCATE_OUT_PARENT unset for the same-filesystem control.
- Choose an already available, writable directory on another filesystem. No disk creation or mounting is needed.
- Set
LOCATE_OUT_PARENT to that directory and run the same script, for example:
LOCATE_OUT_PARENT='/Volumes/ExistingExternalDisk/writable-directory' node locate-exdev-repro.mjs
The script must print same filesystem: false for step 3 to exercise the reported path. Different directory names on the same filesystem do not count as a cross-filesystem test.
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { execFileSync, spawnSync } from 'node:child_process';
// Run from the repository root. Set LOCATE_OUT_PARENT to an existing writable
// directory on another filesystem to exercise the cross-filesystem path.
const repo = fs.mkdtempSync(path.join(os.tmpdir(), 'archify-locate-exdev-repo-'));
const out = fs.mkdtempSync(path.join(
process.env.LOCATE_OUT_PARENT || os.tmpdir(), 'archify-locate-exdev-out-',
));
try {
const map = JSON.parse(fs.readFileSync(
'archify/test/fixtures/bundle-checkout/ledger-flow.json', 'utf8',
));
fs.writeFileSync(path.join(repo, 'map.json'), JSON.stringify(map));
fs.writeFileSync(path.join(repo, 'map.ownership.json'), JSON.stringify({
schema_version: 1, kind: 'ownership', map: 'map.json', excluded: [],
components: map.nodes.map(node => ({ id: node.id, globs: [] })),
}));
const git = (...args) => execFileSync('git', ['-C', repo, ...args], { stdio: 'pipe' });
git('init');
git('add', '.');
git('-c', 'user.name=Archify Repro', '-c', 'user.email=repro@example.test',
'-c', 'commit.gpgsign=false', 'commit', '-m', 'fixture');
console.log('same filesystem:', fs.statSync(os.tmpdir()).dev === fs.statSync(out).dev);
const result = spawnSync(process.execPath, [
'archify/bin/archify.mjs', 'locate', '--lint', 'HEAD',
'--map', path.join(repo, 'map.json'), '--repo-root', repo,
'--out', out, '--json',
], { encoding: 'utf8' });
console.log('exit:', result.status);
const receipt = JSON.parse(result.stdout);
console.log(JSON.stringify({ ok: receipt.ok, error: receipt.error, diagnostics: receipt.diagnostics }, null, 2));
console.log('output files:', JSON.stringify(fs.readdirSync(out)));
} finally {
fs.rmSync(repo, { recursive: true, force: true });
fs.rmSync(out, { recursive: true, force: true });
}
Actual same-filesystem result
Executed against the snapshot above, with LOCATE_OUT_PARENT unset:
same filesystem: true
exit: 0
{
"ok": true
}
output files: ["locate.html","locate.receipt.json"]
Cross-filesystem expectation — not an observed log
With same filesystem: false, the rename path is expected to exit nonzero with a diagnostic containing EXDEV and rename, without successfully committing the output files. This remains to be confirmed on an actual second filesystem; the text here is a prediction from the implementation, not an execution transcript.
Expected behavior
Locate should support a writable output directory on another filesystem. Create staging beneath the resolved output directory, as the existing deliver and preview commands already do, so backups and candidate commits remain on the target filesystem.
Add coverage that verifies staging is target-adjacent, and include a real cross-filesystem run when a suitable environment is available.
Locate stages output in the system temp directory and fails when --out is on another filesystem
Scope and verification boundary
Identified in the unmerged feature work in PR #367, at head
cc1b33a7a02351347842dad4ebe9c13278afc166, using an isolated snapshot. This report does not claim a regression in the released version or main branch.The same-filesystem control below was executed and passed. Cross-filesystem execution has not been performed. The cross-filesystem failure is identified from the staging and rename implementation; no actual EXDEV log is claimed here.
Cause and impact
At this head:
archify/locate/cli.mjs:503creates the staging directory underos.tmpdir().fs.renameSync().--outwithfs.renameSync().Renaming across filesystem boundaries is unsupported. If
--outis on a filesystem different from the system temp directory, both first-time generation and replacement of existing output encounter a cross-filesystem rename. The output path is otherwise a valid writable directory.Environment
macOS, Node.js v22.23.2. The scripts below were verified with this runtime against the PR #367 snapshot identified above.
Reproduction procedure
From the repository root at the head above, save the script below as
locate-exdev-repro.mjs. It creates an isolated Git repository and a fresh output directory, then removes both. The script can live outside the repository, but run it with the repository root as the current working directory.node locate-exdev-repro.mjswithLOCATE_OUT_PARENTunset for the same-filesystem control.LOCATE_OUT_PARENTto that directory and run the same script, for example:LOCATE_OUT_PARENT='/Volumes/ExistingExternalDisk/writable-directory' node locate-exdev-repro.mjsThe script must print
same filesystem: falsefor step 3 to exercise the reported path. Different directory names on the same filesystem do not count as a cross-filesystem test.Actual same-filesystem result
Executed against the snapshot above, with
LOCATE_OUT_PARENTunset:Cross-filesystem expectation — not an observed log
With
same filesystem: false, the rename path is expected to exit nonzero with a diagnostic containingEXDEVandrename, without successfully committing the output files. This remains to be confirmed on an actual second filesystem; the text here is a prediction from the implementation, not an execution transcript.Expected behavior
Locate should support a writable output directory on another filesystem. Create staging beneath the resolved output directory, as the existing deliver and preview commands already do, so backups and candidate commits remain on the target filesystem.
Add coverage that verifies staging is target-adjacent, and include a real cross-filesystem run when a suitable environment is available.