Fix self-hosted update path collision in cmdUpdate

This commit is contained in:
thePR0M3TH3AN committed 2026-02-26 08:41:36 -05:00
1 parent 811f45b3af
commit 32d0f905a6
2 files changed
+47 -4

No files matched your search

+20 -4
View File
@@ -85,9 +85,16 @@ function getPaths(root, installDirName) {
* @param {string} dest - Destination directory path. * @param {string} dest - Destination directory path.
*/ */
function copyDir(src, dest) { function copyDir(src, dest) {
if (fs.existsSync(src)) { if (!fs.existsSync(src)) return false;
fs.cpSync(src, dest, { recursive: true });
const normalizedSrc = path.resolve(src);
const normalizedDest = path.resolve(dest);
if (normalizedSrc === normalizedDest) {
return false;
} }
fs.cpSync(src, dest, { recursive: true });
return true;
} }
/** /**
@@ -149,8 +156,12 @@ function syncAppDirectories(torchDir, verb = 'Copied') {
const src = path.join(PKG_ROOT, dir); const src = path.join(PKG_ROOT, dir);
const dest = path.join(torchDir, dir); const dest = path.join(torchDir, dir);
if (fs.existsSync(src)) { if (fs.existsSync(src)) {
copyDir(src, dest); const copied = copyDir(src, dest);
console.log(` ${verb} ${dir}/`); if (copied) {
console.log(` ${verb} ${dir}/`);
} else {
console.log(` Skipped ${dir}/ (source and destination are identical)`);
}
} }
} }
} }
@@ -179,6 +190,11 @@ function syncAppFiles(torchDir, installDir, verb = 'Copied') {
} }
} }
if (path.resolve(src) === path.resolve(dest)) {
console.log(` Skipped ${file} (source and destination are identical)`);
continue;
}
fs.copyFileSync(src, dest); fs.copyFileSync(src, dest);
console.log(` ${verb} ${file}`); console.log(` ${verb} ${file}`);
} }
+27
View File
@@ -2,6 +2,7 @@ import { test, after } from 'node:test';
import assert from 'node:assert'; import assert from 'node:assert';
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { pathToFileURL } from 'node:url';
import { exec } from 'node:child_process'; import { exec } from 'node:child_process';
import { promisify } from 'node:util'; import { promisify } from 'node:util';
import { WebSocketServer } from 'ws'; import { WebSocketServer } from 'ws';
@@ -119,6 +120,32 @@ test('cmdUpdate creates backup', async () => {
assert.ok(backupContent.includes('roster.json'), 'Backup contains roster.json'); assert.ok(backupContent.includes('roster.json'), 'Backup contains roster.json');
}); });
test('cmdUpdate succeeds when running from package root install (src and dest identical)', async () => {
const fixtureRoot = path.join(tempBase, 'project_self_root');
fs.mkdirSync(fixtureRoot, { recursive: true });
const dirsToCopy = ['src', 'bin', 'dashboard', 'landing', 'assets', 'scripts'];
const filesToCopy = ['package.json', 'build.mjs', 'README.md', 'torch-config.example.json', 'TORCH.md', 'eslint.config.mjs'];
for (const dir of dirsToCopy) {
fs.cpSync(path.join(process.cwd(), dir), path.join(fixtureRoot, dir), { recursive: true });
}
for (const file of filesToCopy) {
fs.copyFileSync(path.join(process.cwd(), file), path.join(fixtureRoot, file));
}
const moduleUrl = pathToFileURL(path.join(fixtureRoot, 'src', 'ops.mjs'));
const { cmdUpdate: fixtureCmdUpdate } = await import(moduleUrl.href);
await assert.doesNotReject(async () => {
await fixtureCmdUpdate(true, fixtureRoot);
}, 'update should not fail when package root is install target');
const backupRoot = path.join(fixtureRoot, '_backups');
assert.ok(fs.existsSync(backupRoot), '_backups directory created for package-root update');
assert.ok(fs.readdirSync(backupRoot).length > 0, 'backup entry created for package-root update');
});
test('torch-lock check respects local roster', async () => { test('torch-lock check respects local roster', async () => {
const wss = new WebSocketServer({ port: 0 }); const wss = new WebSocketServer({ port: 0 });
const port = wss.address().port; const port = wss.address().port;