@wipcomputer/wip-ldm-os 0.4.85-alpha.13 → 0.4.85-alpha.14
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/bin/ldm.js +25 -21
- package/package.json +1 -1
- package/scripts/test-installer-target-self-update.mjs +88 -3
package/bin/ldm.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, cpSync, chmodSync, unlinkSync, readlinkSync, renameSync, statSync, lstatSync, symlinkSync } from 'node:fs';
|
|
24
24
|
import { join, basename, resolve, dirname } from 'node:path';
|
|
25
|
-
import { execSync } from 'node:child_process';
|
|
25
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
26
26
|
import { fileURLToPath } from 'node:url';
|
|
27
27
|
|
|
28
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -272,9 +272,13 @@ function maybeSelfUpdateLdmCliBeforeInstall() {
|
|
|
272
272
|
execSync(`npm install -g @wipcomputer/wip-ldm-os@${latest}`, { stdio: 'inherit', timeout: 60000 });
|
|
273
273
|
console.log(` CLI updated to v${latest}. Re-running with new code...`);
|
|
274
274
|
console.log('');
|
|
275
|
-
const reArgs = process.argv.slice(2)
|
|
276
|
-
|
|
277
|
-
|
|
275
|
+
const reArgs = process.argv.slice(2);
|
|
276
|
+
const child = spawnSync('ldm', reArgs.length > 0 ? reArgs : ['install'], {
|
|
277
|
+
stdio: 'inherit',
|
|
278
|
+
env: { ...process.env, LDM_SELF_UPDATED: '1' },
|
|
279
|
+
});
|
|
280
|
+
if (child.error) throw child.error;
|
|
281
|
+
process.exit(child.status ?? 1);
|
|
278
282
|
} catch (e) {
|
|
279
283
|
console.log(` ! Self-update failed: ${e.message}. Continuing with v${PKG_VERSION}.`);
|
|
280
284
|
}
|
|
@@ -1492,23 +1496,6 @@ async function showCatalogPicker() {
|
|
|
1492
1496
|
// ── ldm install ──
|
|
1493
1497
|
|
|
1494
1498
|
async function cmdInstall() {
|
|
1495
|
-
if (!DRY_RUN && !acquireInstallLock()) return;
|
|
1496
|
-
|
|
1497
|
-
// Ensure LDM is initialized
|
|
1498
|
-
if (!existsSync(VERSION_PATH)) {
|
|
1499
|
-
console.log(' LDM OS not initialized. Running init first...');
|
|
1500
|
-
console.log('');
|
|
1501
|
-
cmdInit();
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
const { setFlags, installFromPath, installSingleTool, installToolbox, detectHarnesses } = await import('../lib/deploy.mjs');
|
|
1505
|
-
const { detectInterfacesJSON } = await import('../lib/detect.mjs');
|
|
1506
|
-
|
|
1507
|
-
// Refresh harness detection (catches newly installed harnesses)
|
|
1508
|
-
detectHarnesses();
|
|
1509
|
-
|
|
1510
|
-
setFlags({ dryRun: DRY_RUN, jsonOutput: JSON_OUTPUT, origin: 'manual' });
|
|
1511
|
-
|
|
1512
1499
|
// --help flag (#81)
|
|
1513
1500
|
if (args.includes('--help') || args.includes('-h')) {
|
|
1514
1501
|
console.log(`
|
|
@@ -1530,6 +1517,23 @@ async function cmdInstall() {
|
|
|
1530
1517
|
|
|
1531
1518
|
maybeSelfUpdateLdmCliBeforeInstall();
|
|
1532
1519
|
|
|
1520
|
+
if (!DRY_RUN && !acquireInstallLock()) return;
|
|
1521
|
+
|
|
1522
|
+
// Ensure LDM is initialized
|
|
1523
|
+
if (!existsSync(VERSION_PATH)) {
|
|
1524
|
+
console.log(' LDM OS not initialized. Running init first...');
|
|
1525
|
+
console.log('');
|
|
1526
|
+
cmdInit();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
const { setFlags, installFromPath, installSingleTool, installToolbox, detectHarnesses } = await import('../lib/deploy.mjs');
|
|
1530
|
+
const { detectInterfacesJSON } = await import('../lib/detect.mjs');
|
|
1531
|
+
|
|
1532
|
+
// Refresh harness detection (catches newly installed harnesses)
|
|
1533
|
+
detectHarnesses();
|
|
1534
|
+
|
|
1535
|
+
setFlags({ dryRun: DRY_RUN, jsonOutput: JSON_OUTPUT, origin: 'manual' });
|
|
1536
|
+
|
|
1533
1537
|
// Find the target (skip flags)
|
|
1534
1538
|
const target = args.slice(1).find(a => !a.startsWith('--'));
|
|
1535
1539
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
3
4
|
import { dirname, join } from 'node:path';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
4
6
|
import { fileURLToPath } from 'node:url';
|
|
5
7
|
|
|
6
8
|
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -21,17 +23,27 @@ if (!helperBlock.includes("execSync(`npm install -g @wipcomputer/wip-ldm-os@${la
|
|
|
21
23
|
throw new Error('Self-update helper must update LDM OS before real installs');
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
if (!helperBlock.includes('
|
|
25
|
-
throw new Error('Self-update helper must re-run the original install command');
|
|
26
|
+
if (!helperBlock.includes("spawnSync('ldm'")) {
|
|
27
|
+
throw new Error('Self-update helper must re-run the original install command without shell joining args');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (helperBlock.includes('process.argv.slice(2).join')) {
|
|
31
|
+
throw new Error('Self-update helper must preserve argv boundaries when re-running install');
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
const cmdInstallIdx = cli.indexOf('async function cmdInstall()');
|
|
35
|
+
const lockIdx = cli.indexOf('acquireInstallLock()', cmdInstallIdx);
|
|
36
|
+
const initIdx = cli.indexOf('LDM OS not initialized. Running init first', cmdInstallIdx);
|
|
29
37
|
const targetIdx = cli.indexOf('// Find the target (skip flags)', cmdInstallIdx);
|
|
30
38
|
const preflightCallIdx = cli.indexOf('maybeSelfUpdateLdmCliBeforeInstall();', cmdInstallIdx);
|
|
31
39
|
if (cmdInstallIdx === -1 || targetIdx === -1 || preflightCallIdx === -1) {
|
|
32
40
|
throw new Error('Could not find cmdInstall self-update placement');
|
|
33
41
|
}
|
|
34
42
|
|
|
43
|
+
if (preflightCallIdx > lockIdx || preflightCallIdx > initIdx) {
|
|
44
|
+
throw new Error('Self-update preflight must run before lock acquisition and init work');
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
if (preflightCallIdx > targetIdx) {
|
|
36
48
|
throw new Error('Self-update preflight must run before target resolution so app installs are covered');
|
|
37
49
|
}
|
|
@@ -43,4 +55,77 @@ if (oldCatalogBlock !== -1 && oldCatalogBlock < autoDetectIdx) {
|
|
|
43
55
|
throw new Error('Catalog install should not own the only self-update block');
|
|
44
56
|
}
|
|
45
57
|
|
|
58
|
+
const tempRoot = mkdtempSync(join(tmpdir(), 'ldm-target-self-update-'));
|
|
59
|
+
try {
|
|
60
|
+
const home = join(tempRoot, 'home');
|
|
61
|
+
const fakeBin = join(tempRoot, 'bin');
|
|
62
|
+
const target = join(tempRoot, 'target skill with spaces');
|
|
63
|
+
|
|
64
|
+
mkdirSync(join(home, '.ldm'), { recursive: true });
|
|
65
|
+
writeFileSync(join(home, '.ldm', 'version.json'), JSON.stringify({
|
|
66
|
+
version: '0.0.0',
|
|
67
|
+
installed: new Date().toISOString(),
|
|
68
|
+
updated: new Date().toISOString(),
|
|
69
|
+
}, null, 2) + '\n');
|
|
70
|
+
|
|
71
|
+
mkdirSync(fakeBin, { recursive: true });
|
|
72
|
+
const fakeNpm = join(fakeBin, 'npm');
|
|
73
|
+
writeFileSync(fakeNpm, `#!/usr/bin/env bash
|
|
74
|
+
if [ "$1" = "view" ] && [ "$2" = "@wipcomputer/wip-ldm-os" ] && [ "$3" = "dist-tags.alpha" ]; then
|
|
75
|
+
echo "99.0.0-alpha.1"
|
|
76
|
+
exit 0
|
|
77
|
+
fi
|
|
78
|
+
echo "unexpected npm command: $*" >&2
|
|
79
|
+
exit 64
|
|
80
|
+
`);
|
|
81
|
+
chmodSync(fakeNpm, 0o755);
|
|
82
|
+
|
|
83
|
+
mkdirSync(target, { recursive: true });
|
|
84
|
+
writeFileSync(join(target, 'SKILL.md'), `---
|
|
85
|
+
name: test-target-skill
|
|
86
|
+
description: Test target skill for installer self-update dry-run checks.
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
# Test Target Skill
|
|
90
|
+
`);
|
|
91
|
+
|
|
92
|
+
const result = spawnSync(process.execPath, [
|
|
93
|
+
join(root, 'bin', 'ldm.js'),
|
|
94
|
+
'install',
|
|
95
|
+
'--alpha',
|
|
96
|
+
'--dry-run',
|
|
97
|
+
target,
|
|
98
|
+
], {
|
|
99
|
+
cwd: root,
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
env: {
|
|
102
|
+
...process.env,
|
|
103
|
+
HOME: home,
|
|
104
|
+
PATH: `${fakeBin}:${process.env.PATH || ''}`,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (result.status !== 0) {
|
|
109
|
+
throw new Error(`Runtime dry-run exited ${result.status}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!result.stdout.includes('LDM OS CLI v')) {
|
|
113
|
+
throw new Error(`Runtime dry-run did not print the LDM OS skew warning\nstdout:\n${result.stdout}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!result.stdout.includes('-> v99.0.0-alpha.1 (alpha track) is available.')) {
|
|
117
|
+
throw new Error(`Runtime dry-run did not include the selected alpha track version\nstdout:\n${result.stdout}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!result.stdout.includes('Dry run only: continuing with v')) {
|
|
121
|
+
throw new Error(`Runtime dry-run did not say it would continue without updating\nstdout:\n${result.stdout}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!result.stdout.includes('Installing: target skill with spaces (dry run)')) {
|
|
125
|
+
throw new Error(`Runtime dry-run did not continue to the targeted install preview\nstdout:\n${result.stdout}`);
|
|
126
|
+
}
|
|
127
|
+
} finally {
|
|
128
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
129
|
+
}
|
|
130
|
+
|
|
46
131
|
console.log('targeted install self-update regression checks passed');
|