stackbite 0.5.106 → 0.5.107
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/package.json
CHANGED
|
@@ -484,8 +484,30 @@ async function reservePreparation(environment, platform, target, source) {
|
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
+
// Durable runtime images share the standalone installer's versioned app root,
|
|
488
|
+
// with its live-reference retention and removal inventory. The disposable
|
|
489
|
+
// cache only holds images of the previous layout, which may be adopted.
|
|
490
|
+
// A different archive of the same version and commit (for example a signed
|
|
491
|
+
// rebuild) gets a sibling qualified by its pinned archive digest, so an image
|
|
492
|
+
// another installer or pin published is never replaced or run under this pin.
|
|
493
|
+
function runtimeName(spec, qualified = false) {
|
|
494
|
+
const commit = spec.commit.slice(0, 12), version = spec.version + (qualified ? `-pin${spec.sha256.slice(0, 10)}` : '');
|
|
495
|
+
return spec.platform === 'win32' ? `${version}-${commit}` : `stackbite-${version}-${spec.host}-${commit}`;
|
|
496
|
+
}
|
|
497
|
+
async function pinnedTarget(runtime, spec) {
|
|
498
|
+
const canonical = path.join(runtime, runtimeName(spec));
|
|
499
|
+
if (!await exists(canonical)) return canonical;
|
|
500
|
+
try { await pinnedDocuments(await payloadRoot(canonical), spec); return canonical; }
|
|
501
|
+
catch (error) { if (/manifest or metadata SHA-256 mismatch/.test(error.message)) return path.join(runtime, runtimeName(spec, true)); throw error; }
|
|
502
|
+
}
|
|
503
|
+
function previousCacheHome(home, cache, platform) {
|
|
504
|
+
const spelling = value => platform === 'win32' ? value.toLowerCase() : value;
|
|
505
|
+
return typeof home === 'string' && path.isAbsolute(home) && /^v1-[a-f0-9]{64}$/.test(path.basename(home))
|
|
506
|
+
&& !!cache && spelling(path.dirname(path.resolve(home))) === spelling(cache);
|
|
507
|
+
}
|
|
487
508
|
async function publishCache(payload, target, spec, options) {
|
|
488
509
|
await assertBaseline(options.environment, spec.platform);
|
|
510
|
+
let migrate = false;
|
|
489
511
|
if (options.existing) {
|
|
490
512
|
const root = installationRoot(options.environment, spec.platform);
|
|
491
513
|
for (const name of ['default-install.json', 'install.json']) {
|
|
@@ -493,46 +515,74 @@ async function publishCache(payload, target, spec, options) {
|
|
|
493
515
|
if (!await exists(locator)) continue;
|
|
494
516
|
const saved = JSON.parse((await readSmall(locator, 16384)).toString('utf8').replace(/^\uFEFF/, ''));
|
|
495
517
|
const descriptor = options.environment.STACKBITE_NPM_REGISTRATION;
|
|
496
|
-
|
|
518
|
+
migrate = previousCacheHome(saved.home, options.cache, spec.platform);
|
|
519
|
+
if (!migrate && typeof saved.home === 'string' && path.isAbsolute(saved.home)
|
|
497
520
|
&& typeof saved.state === 'string' && path.isAbsolute(saved.state) && /^[a-f0-9]{32}$/.test(saved.installation_id)
|
|
498
521
|
&& (!descriptor || JSON.stringify(saved.npm_registration) === JSON.stringify(JSON.parse(descriptor)))) return;
|
|
499
522
|
break;
|
|
500
523
|
}
|
|
501
524
|
}
|
|
502
525
|
const engine = path.join(payload, 'stackbite' + (spec.platform === 'win32' ? '.exe' : ''));
|
|
503
|
-
|
|
526
|
+
// A selection still in the previous cache layout moves to the durable image.
|
|
527
|
+
const argv = options.existing && !migrate ? ['--record-install', target]
|
|
504
528
|
: ['--record-install', target, '-', payload, 'cache'];
|
|
505
529
|
const result = await launchNative(engine, argv, { ...options.environment, STACKBITE_HOME: payload }, options);
|
|
506
530
|
if (result.code !== 0 || result.signal) fail('Runtime publication was refused');
|
|
507
531
|
}
|
|
508
532
|
|
|
509
|
-
|
|
533
|
+
function runtimeDirectory(environment, platform) {
|
|
534
|
+
return path.join(installationRoot(environment, platform), 'app');
|
|
535
|
+
}
|
|
536
|
+
// The previous layout's image of this exact pinned identity, when it still
|
|
537
|
+
// passes the full pinned validation. It is only read; a live process may use it.
|
|
538
|
+
async function previousImage(legacy, cache, spec) {
|
|
539
|
+
if (!await existingCacheDirectory(cache) || !await exists(legacy)) return null;
|
|
540
|
+
try { await validatePayload(legacy, spec); } catch { return null; }
|
|
541
|
+
return legacy;
|
|
542
|
+
}
|
|
543
|
+
async function ensureBinary(spec, { cache = cacheDirectory(), runtime = null, downloadImpl = download, extractImpl = extractArchive,
|
|
510
544
|
checkOnly = false, reportCached = false, includePhone = false, progress = () => {},
|
|
511
545
|
environment = process.env, publishImpl = publishCache, registrationSource = null, ...options } = {}) {
|
|
512
546
|
cache = await resolveCacheDirectory(cache);
|
|
513
547
|
environment = { ...environment };
|
|
548
|
+
runtime = path.resolve(runtime || runtimeDirectory(environment, spec.platform));
|
|
514
549
|
if (!checkOnly) {
|
|
515
550
|
await admitPublication(environment, spec.platform);
|
|
516
551
|
await existingCacheDirectory(cache); // Validate existing ancestors without creating a cache.
|
|
517
552
|
}
|
|
518
|
-
if (checkOnly) {
|
|
519
|
-
if (!await existingCacheDirectory(cache)) throw notCached();
|
|
520
|
-
}
|
|
521
553
|
// Full identity remains SHA-256 addressed while avoiding Windows MAX_PATH.
|
|
522
554
|
const cacheKey = createHash('sha256').update(JSON.stringify([spec.version, spec.commit, spec.key, spec.sha256])).digest('hex');
|
|
523
|
-
const
|
|
555
|
+
const legacy = path.join(cache, `v1-${cacheKey}`);
|
|
556
|
+
const target = await selectedDirectory(runtime, environment, spec.platform) ? await pinnedTarget(runtime, spec)
|
|
557
|
+
: path.join(runtime, runtimeName(spec));
|
|
524
558
|
const preparation = !checkOnly ? await reservePreparation(environment, spec.platform, target, registrationSource) : null;
|
|
525
559
|
try {
|
|
526
560
|
if (registrationSource) await registrationSource.validate();
|
|
527
561
|
if (!checkOnly) await assertBaseline(environment, spec.platform);
|
|
528
|
-
|
|
562
|
+
// Check-only callers (removal, setup --check) never publish; they may still
|
|
563
|
+
// use an image of the previous cache layout.
|
|
564
|
+
const home = await selectedDirectory(runtime, environment, spec.platform) && await exists(target) ? target
|
|
565
|
+
: checkOnly && await existingCacheDirectory(cache) && await exists(legacy) ? legacy : null;
|
|
566
|
+
if (home) {
|
|
529
567
|
if (checkOnly || reportCached) progress('validate');
|
|
530
|
-
const installed = { home
|
|
531
|
-
if (!checkOnly) await publishImpl(target, target, spec, { ...options, environment, existing: true });
|
|
568
|
+
const installed = { home, executable: await validatePayload(home, spec, { includePhone }) };
|
|
569
|
+
if (!checkOnly) await publishImpl(target, target, spec, { ...options, environment, cache, existing: true });
|
|
532
570
|
if (checkOnly || reportCached) progress('ready');
|
|
533
571
|
return installed;
|
|
534
572
|
}
|
|
535
573
|
if (checkOnly) throw notCached();
|
|
574
|
+
const previous = await previousImage(legacy, cache, spec);
|
|
575
|
+
if (previous) {
|
|
576
|
+
progress('publish');
|
|
577
|
+
await admitPublication(environment, spec.platform);
|
|
578
|
+
await assertBaseline(environment, spec.platform);
|
|
579
|
+
if (registrationSource) await registrationSource.validate();
|
|
580
|
+
await publishImpl(previous, target, spec, { ...options, environment, cache, existing: false });
|
|
581
|
+
progress('validate-published');
|
|
582
|
+
const installed = { home: target, executable: await validatePayload(target, spec) };
|
|
583
|
+
progress('ready');
|
|
584
|
+
return installed;
|
|
585
|
+
}
|
|
536
586
|
// An owned system-temp stage keeps downloads out of product caches. Only the
|
|
537
587
|
// verified engine below can create/publish a canonical cache installation.
|
|
538
588
|
await admitPublication(environment, spec.platform);
|
|
@@ -545,7 +595,7 @@ async function ensureBinary(spec, { cache = cacheDirectory(), downloadImpl = dow
|
|
|
545
595
|
await admitPublication(environment, spec.platform);
|
|
546
596
|
await assertBaseline(environment, spec.platform);
|
|
547
597
|
if (registrationSource) await registrationSource.validate();
|
|
548
|
-
await publishImpl(payload, target, spec, { ...options, environment, existing: false });
|
|
598
|
+
await publishImpl(payload, target, spec, { ...options, environment, cache, existing: false });
|
|
549
599
|
// Validate at the final path, including a concurrent publisher's payload.
|
|
550
600
|
progress('validate-published');
|
|
551
601
|
const installed = { home: target, executable: await validatePayload(target, spec) };
|
|
@@ -630,6 +680,21 @@ async function launchNative(executable, argv, environment, { spawnImpl = spawn,
|
|
|
630
680
|
child.once('exit', (code, signal) => { cleanup(); resolve({ code, signal }); });
|
|
631
681
|
});
|
|
632
682
|
}
|
|
683
|
+
// Before the first install, --help/--version answer from this package's pin
|
|
684
|
+
// instead of downloading a runtime; the launcher only ever runs that build.
|
|
685
|
+
async function coldAnswer(argv, spec, runtime, { stdout = process.stdout, stderr = process.stderr }) {
|
|
686
|
+
if (argv.length !== 1 || !['--version', '--help', '-h'].includes(argv[0])) return null;
|
|
687
|
+
try { if (await exists(await pinnedTarget(runtime, spec))) return null; } catch { return null; }
|
|
688
|
+
if (argv[0] === '--version') {
|
|
689
|
+
stdout.write(`Stackbite ${spec.version} (${spec.commit})\n`);
|
|
690
|
+
stderr.write('Stackbite: this is the pinned release; its runtime is not installed yet. Run stackbite setup to install it.\n');
|
|
691
|
+
} else {
|
|
692
|
+
stdout.write('usage: stackbite [--help | --version] | stackbite setup [--check] | stackbite [COMMAND ...]\n\n'
|
|
693
|
+
+ `The pinned Stackbite ${spec.version} runtime is not installed yet. Run stackbite setup to\n`
|
|
694
|
+
+ 'install it (or run any command); stackbite --help then shows the full reference.\n');
|
|
695
|
+
}
|
|
696
|
+
return { code: 0, signal: null };
|
|
697
|
+
}
|
|
633
698
|
async function main(argv = process.argv.slice(2), options = {}) {
|
|
634
699
|
const setup = parseSetup(argv);
|
|
635
700
|
const removing = ['uninstall', 'delete'].includes(argv[0]);
|
|
@@ -645,6 +710,8 @@ async function main(argv = process.argv.slice(2), options = {}) {
|
|
|
645
710
|
try {
|
|
646
711
|
if (npm) environment.STACKBITE_NPM_REGISTRATION = JSON.stringify(npm);
|
|
647
712
|
const spec = releaseSpec(manifest, platform, options.arch || process.arch, options);
|
|
713
|
+
const cold = await coldAnswer(argv, spec, options.runtime || runtimeDirectory(environment, platform), options);
|
|
714
|
+
if (cold) return cold;
|
|
648
715
|
// First install always verifies everything. Cached TUI/runtime starts defer only
|
|
649
716
|
// Phone; explicit phone/setup/removal commands retain the full pinned check.
|
|
650
717
|
let installed;
|
|
@@ -14,7 +14,7 @@ function stageReporter(stderr) {
|
|
|
14
14
|
archive: 'Verifying archive...',
|
|
15
15
|
extract: 'Extracting runtime...',
|
|
16
16
|
validate: 'Validating runtime files...',
|
|
17
|
-
publish: 'Publishing runtime
|
|
17
|
+
publish: 'Publishing runtime...',
|
|
18
18
|
'validate-published': 'Validating published runtime...',
|
|
19
19
|
ready: 'Pinned runtime is ready.'
|
|
20
20
|
};
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": 1,
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"commit": "
|
|
3
|
+
"version": "0.5.107",
|
|
4
|
+
"commit": "506288e3f01c20dd709905c95eaa46ad04471b17",
|
|
5
5
|
"repository": "Shinick-Han/stackbite",
|
|
6
6
|
"platforms": {
|
|
7
7
|
"win32-x64": {
|
|
8
|
-
"asset": "stackbite-0.5.
|
|
9
|
-
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.
|
|
10
|
-
"sha256": "
|
|
11
|
-
"size":
|
|
12
|
-
"root": "stackbite-0.5.
|
|
13
|
-
"manifestSha256": "
|
|
14
|
-
"metadataSha256": "
|
|
8
|
+
"asset": "stackbite-0.5.107-windows-x64-506288e3f01c.zip",
|
|
9
|
+
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.107/stackbite-0.5.107-windows-x64-506288e3f01c.zip",
|
|
10
|
+
"sha256": "6da273fbd3805fff41c3dc1346c39eb7290f4969b30e5f14124a9f9948659701",
|
|
11
|
+
"size": 96379204,
|
|
12
|
+
"root": "stackbite-0.5.107-windows-x64-506288e3f01c",
|
|
13
|
+
"manifestSha256": "fb81b39e291be6bd012b2d1e481f38aa506e38681ea463ddb55dd21bfeab309a",
|
|
14
|
+
"metadataSha256": "871eed20e507d284102373ce50e586919cf0ed3b5a791b6afa9d8cc7df762604"
|
|
15
15
|
},
|
|
16
16
|
"linux-x64": {
|
|
17
|
-
"asset": "stackbite-0.5.
|
|
18
|
-
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.
|
|
19
|
-
"sha256": "
|
|
20
|
-
"size":
|
|
21
|
-
"root": "stackbite-0.5.
|
|
22
|
-
"manifestSha256": "
|
|
23
|
-
"metadataSha256": "
|
|
17
|
+
"asset": "stackbite-0.5.107-linux-x86_64-506288e3f01c.tar.gz",
|
|
18
|
+
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.107/stackbite-0.5.107-linux-x86_64-506288e3f01c.tar.gz",
|
|
19
|
+
"sha256": "74e451669bd460b690dc45d4abe9f7bbe6af10d0ab56519c5c4a7c0ff55b2350",
|
|
20
|
+
"size": 77586426,
|
|
21
|
+
"root": "stackbite-0.5.107-linux-x86_64-506288e3f01c",
|
|
22
|
+
"manifestSha256": "a3030ce79fe2b84314064f7a5f5794e744fe8d16cfa223d467a8670cdc957e15",
|
|
23
|
+
"metadataSha256": "9df20d2f675d4e4457a060d07b1b821146d9dcfaa92a532960cb8c6b39239d35"
|
|
24
24
|
},
|
|
25
25
|
"darwin-arm64": {
|
|
26
|
-
"asset": "stackbite-0.5.
|
|
27
|
-
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.
|
|
28
|
-
"sha256": "
|
|
29
|
-
"size":
|
|
30
|
-
"root": "stackbite-0.5.
|
|
31
|
-
"manifestSha256": "
|
|
32
|
-
"metadataSha256": "
|
|
26
|
+
"asset": "stackbite-0.5.107-macos-arm64-506288e3f01c.tar.gz",
|
|
27
|
+
"url": "https://github.com/Shinick-Han/stackbite/releases/download/v0.5.107/stackbite-0.5.107-macos-arm64-506288e3f01c.tar.gz",
|
|
28
|
+
"sha256": "ddf4c956c7657df6bb4684aa3f7a9d677144cca2deb4e425e3a6442b88671262",
|
|
29
|
+
"size": 33557501,
|
|
30
|
+
"root": "stackbite-0.5.107-macos-arm64-506288e3f01c",
|
|
31
|
+
"manifestSha256": "4c2ab4d509a2fb1a48696c820b8c3f8cadea9441817e314a595ab8b08f34cef3",
|
|
32
|
+
"metadataSha256": "e07aedd09b464458bef021518136bbcfb3d9b521f72e1da991b6d1091cccaed3"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|