castle-web-cli 0.4.99 → 0.4.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/imports.js +26 -15
- package/package.json +1 -1
package/dist/imports.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
-
import * as os from 'os';
|
|
3
2
|
import * as path from 'path';
|
|
4
3
|
import { nanoid } from 'nanoid';
|
|
5
4
|
import * as api from './api.js';
|
|
@@ -248,20 +247,32 @@ export async function restoreMissingImports(deckDir) {
|
|
|
248
247
|
}
|
|
249
248
|
return restored;
|
|
250
249
|
}
|
|
251
|
-
// Download an archive and unpack it at `destDir`,
|
|
252
|
-
//
|
|
250
|
+
// Download an archive and unpack it at `destDir`, replacing whatever is there.
|
|
251
|
+
//
|
|
252
|
+
// Staged NEXT TO destDir rather than in the OS temp dir. In a sandbox `/tmp` and
|
|
253
|
+
// the workspace are separate filesystems, and renaming across them fails with
|
|
254
|
+
// EXDEV -- which, when the old copy had already been cleared to make room, left
|
|
255
|
+
// the deck with no kit at all. Staging inside `imports/` keeps the move on one
|
|
256
|
+
// filesystem, and nothing existing is touched until the new copy is unpacked and
|
|
257
|
+
// whole: a failed download or a bad archive now leaves the deck exactly as it
|
|
258
|
+
// was.
|
|
253
259
|
async function fetchDeckSourceInto(archiveUrl, destDir) {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const
|
|
258
|
-
const tmpFile = path.join(os.tmpdir(), `castle-import-${nanoid(8)}.tar.gz`);
|
|
259
|
-
const tmpDir = path.join(os.tmpdir(), `castle-import-${nanoid(8)}`);
|
|
260
|
-
fs.writeFileSync(tmpFile, buf);
|
|
260
|
+
const importsDir = path.dirname(destDir);
|
|
261
|
+
fs.mkdirSync(importsDir, { recursive: true });
|
|
262
|
+
const tmpFile = path.join(importsDir, `.castle-import-${nanoid(8)}.tar.gz`);
|
|
263
|
+
const tmpDir = path.join(importsDir, `.castle-import-${nanoid(8)}`);
|
|
261
264
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
262
265
|
try {
|
|
266
|
+
const res = await fetch(archiveUrl, { signal: AbortSignal.timeout(60000) });
|
|
267
|
+
if (!res.ok)
|
|
268
|
+
throw new Error(`Archive fetch failed: HTTP ${res.status}`);
|
|
269
|
+
fs.writeFileSync(tmpFile, Buffer.from(await res.arrayBuffer()));
|
|
263
270
|
await runTar(['-xzf', tmpFile, '-C', tmpDir]);
|
|
264
|
-
|
|
271
|
+
// The new copy is whole; only now is it safe to drop the old one.
|
|
272
|
+
if (fs.existsSync(destDir)) {
|
|
273
|
+
unlockImportTree(destDir);
|
|
274
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
275
|
+
}
|
|
265
276
|
fs.renameSync(tmpDir, destDir);
|
|
266
277
|
}
|
|
267
278
|
finally {
|
|
@@ -347,11 +358,11 @@ function backupImport(deckDir, alias) {
|
|
|
347
358
|
async function placeImport(deckDir, alias, deckId, source, via) {
|
|
348
359
|
const destDir = path.join(deckDir, IMPORTS_DIR, alias);
|
|
349
360
|
const replaced = fs.existsSync(destDir);
|
|
350
|
-
|
|
361
|
+
// Back up first, but leave the old copy in place: fetchDeckSourceInto only
|
|
362
|
+
// clears it once the replacement is unpacked, so a failed update is a no-op
|
|
363
|
+
// rather than a deck with its kit missing.
|
|
364
|
+
if (replaced)
|
|
351
365
|
backupImport(deckDir, alias);
|
|
352
|
-
unlockImportTree(destDir);
|
|
353
|
-
fs.rmSync(destDir, { recursive: true, force: true });
|
|
354
|
-
}
|
|
355
366
|
await fetchDeckSourceInto(source.archiveUrl, destDir);
|
|
356
367
|
lockImportTree(destDir);
|
|
357
368
|
clearStatusCache(deckDir);
|