@edspencer/paddock 0.82.0 → 0.83.0
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 +1 -1
- package/packages/server/dist/cli/args.d.ts +29 -4
- package/packages/server/dist/cli/args.js +145 -7
- package/packages/server/dist/cli/busy-command.js +1 -1
- package/packages/server/dist/cli/busy.js +10 -12
- package/packages/server/dist/cli/doctor.d.ts +60 -0
- package/packages/server/dist/cli/doctor.js +580 -0
- package/packages/server/dist/cli/exit-codes.d.ts +21 -45
- package/packages/server/dist/cli/exit-codes.js +42 -45
- package/packages/server/dist/cli/output.js +5 -6
- package/packages/server/dist/cli/paddock.js +37 -12
- package/packages/server/dist/cli/service/index.d.ts +30 -2
- package/packages/server/dist/cli/service/index.js +163 -31
- package/packages/server/dist/cli/service/launchd.d.ts +31 -84
- package/packages/server/dist/cli/service/launchd.js +114 -304
- package/packages/server/dist/cli/service/logs.d.ts +9 -28
- package/packages/server/dist/cli/service/logs.js +33 -75
- package/packages/server/dist/cli/service/manager.d.ts +93 -0
- package/packages/server/dist/cli/service/manager.js +21 -0
- package/packages/server/dist/cli/service/path-env.d.ts +7 -18
- package/packages/server/dist/cli/service/path-env.js +30 -81
- package/packages/server/dist/cli/service/ready.d.ts +16 -23
- package/packages/server/dist/cli/service/ready.js +42 -0
- package/packages/server/dist/cli/service/spec.d.ts +4 -31
- package/packages/server/dist/cli/service/spec.js +9 -0
- package/packages/server/dist/cli/service/systemd.d.ts +27 -101
- package/packages/server/dist/cli/service/systemd.js +118 -261
- package/packages/server/dist/cli/update/health.d.ts +48 -0
- package/packages/server/dist/cli/update/health.js +64 -0
- package/packages/server/dist/cli/update/install-method.d.ts +29 -76
- package/packages/server/dist/cli/update/install-method.js +103 -178
- package/packages/server/dist/cli/update/lock.d.ts +7 -12
- package/packages/server/dist/cli/update/lock.js +28 -62
- package/packages/server/dist/cli/update/package-manager.d.ts +39 -74
- package/packages/server/dist/cli/update/package-manager.js +95 -160
- package/packages/server/dist/cli/update/registry.d.ts +17 -54
- package/packages/server/dist/cli/update/registry.js +46 -89
- package/packages/server/dist/cli/update/semver.d.ts +18 -41
- package/packages/server/dist/cli/update/semver.js +74 -124
- package/packages/server/dist/cli/update/update-check.d.ts +15 -26
- package/packages/server/dist/cli/update/update-check.js +30 -60
- package/packages/server/dist/cli/update/update.d.ts +57 -91
- package/packages/server/dist/cli/update/update.js +230 -431
- package/packages/server/dist/cli/update-command.js +3 -5
- package/packages/server/dist/runtime-file.d.ts +1 -1
- package/packages/server/dist/runtime-file.js +9 -13
- package/packages/server/package.json +1 -1
- package/packages/web/dist/assets/{DeleteProjectDialog-CphdGB3R.js → DeleteProjectDialog-B07MKgv4.js} +1 -1
- package/packages/web/dist/assets/DiscoverPage-D5C2fkeQ.js +2 -0
- package/packages/web/dist/assets/{DiscoverView-B_kcF4sm.js → DiscoverView-DpAAyBEf.js} +1 -1
- package/packages/web/dist/assets/{InstanceConfigPage-CvUK5DKW.js → InstanceConfigPage-YX0I_jVA.js} +1 -1
- package/packages/web/dist/assets/{MarkdownRenderer-DvZ-j_Ao.js → MarkdownRenderer-C-CUuFhT.js} +1 -1
- package/packages/web/dist/assets/{ProjectRedirect-DayHvUH2.js → ProjectRedirect-qyXbTK56.js} +1 -1
- package/packages/web/dist/assets/{ProjectView-CCw83gUi.js → ProjectView-CguNOv8k.js} +3 -3
- package/packages/web/dist/assets/{ProjectsGrid-B3Klg2H4.js → ProjectsGrid-CH2Lz2Hf.js} +1 -1
- package/packages/web/dist/assets/{RootHome-BxcDvpgI.js → RootHome-CUGUc8za.js} +1 -1
- package/packages/web/dist/assets/{Surfaces-7UlhFBSG.js → Surfaces-11or1q_3.js} +1 -1
- package/packages/web/dist/assets/{arc-DNkh5o5Q.js → arc-CQHeCzrk.js} +1 -1
- package/packages/web/dist/assets/{architectureDiagram-3BPJPVTR-BrgN3oLF.js → architectureDiagram-3BPJPVTR-dGcuFxAa.js} +1 -1
- package/packages/web/dist/assets/{blockDiagram-GPEHLZMM-D05XOJSk.js → blockDiagram-GPEHLZMM-CGc9h97S.js} +1 -1
- package/packages/web/dist/assets/{c4Diagram-AAUBKEIU-CKaMBhMP.js → c4Diagram-AAUBKEIU-BpX0sqhH.js} +1 -1
- package/packages/web/dist/assets/channel-BAeygx-e.js +2 -0
- package/packages/web/dist/assets/{chunk-2J33WTMH-8hgadCQc.js → chunk-2J33WTMH-uORGdO9D.js} +1 -1
- package/packages/web/dist/assets/{chunk-4BX2VUAB-CBfEC-pu.js → chunk-4BX2VUAB-C8zI2PFy.js} +1 -1
- package/packages/web/dist/assets/{chunk-55IACEB6-DV1eMgHk.js → chunk-55IACEB6-D22Iv6j6.js} +1 -1
- package/packages/web/dist/assets/{chunk-727SXJPM-Cfgl5fxY.js → chunk-727SXJPM-DdbHXAtS.js} +1 -1
- package/packages/web/dist/assets/{chunk-AQP2D5EJ-BXhqD295.js → chunk-AQP2D5EJ-BeKbob3a.js} +1 -1
- package/packages/web/dist/assets/{chunk-FMBD7UC4-TFGOPtek.js → chunk-FMBD7UC4-DtLvFU_y.js} +1 -1
- package/packages/web/dist/assets/{chunk-ND2GUHAM-CHFM665R.js → chunk-ND2GUHAM-BWJFo6B1.js} +1 -1
- package/packages/web/dist/assets/{chunk-QZHKN3VN-CqEf6MMT.js → chunk-QZHKN3VN-DeFkNg3S.js} +1 -1
- package/packages/web/dist/assets/classDiagram-4FO5ZUOK-Cq2-sBMa.js +2 -0
- package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-Cq2-sBMa.js +2 -0
- package/packages/web/dist/assets/{core-DmEaKxBb.js → core-CTNNIHx5.js} +1 -1
- package/packages/web/dist/assets/{cose-bilkent-S5V4N54A-BnGI6jkr.js → cose-bilkent-S5V4N54A-BmtlyA-v.js} +1 -1
- package/packages/web/dist/assets/{dagre-BM42HDAG-CKoPx61E.js → dagre-BM42HDAG-oS3rC9CI.js} +1 -1
- package/packages/web/dist/assets/{diagram-2AECGRRQ-mMmtlBl1.js → diagram-2AECGRRQ-BzIBqz6A.js} +1 -1
- package/packages/web/dist/assets/{diagram-5GNKFQAL-Cdz9Mtnt.js → diagram-5GNKFQAL-BgIT2g8R.js} +1 -1
- package/packages/web/dist/assets/{diagram-KO2AKTUF-DQxM3wYj.js → diagram-KO2AKTUF-DACT41Re.js} +1 -1
- package/packages/web/dist/assets/{diagram-LMA3HP47-BWrinPsq.js → diagram-LMA3HP47-BstA3UkI.js} +1 -1
- package/packages/web/dist/assets/{diagram-OG6HWLK6-U6MYUve2.js → diagram-OG6HWLK6-Dp3Cn5n_.js} +1 -1
- package/packages/web/dist/assets/{documentTitle-DGjRXuTP.js → documentTitle-DRSQmA8q.js} +1 -1
- package/packages/web/dist/assets/{erDiagram-TEJ5UH35-xqMCsXxY.js → erDiagram-TEJ5UH35-wf8PyGx_.js} +1 -1
- package/packages/web/dist/assets/{flowDiagram-I6XJVG4X-PMfy_czF.js → flowDiagram-I6XJVG4X-CJ4xx6EQ.js} +1 -1
- package/packages/web/dist/assets/{ganttDiagram-6RSMTGT7-whhYYE4N.js → ganttDiagram-6RSMTGT7-z9obPrPM.js} +1 -1
- package/packages/web/dist/assets/{gitGraphDiagram-PVQCEYII-C51JW4tf.js → gitGraphDiagram-PVQCEYII-Cf4Cu7Tk.js} +1 -1
- package/packages/web/dist/assets/{index-CHOtvlnO.js → index-Bsv-raz2.js} +2 -2
- package/packages/web/dist/assets/{infoDiagram-5YYISTIA-SH8cr905.js → infoDiagram-5YYISTIA-BIfuiYxX.js} +1 -1
- package/packages/web/dist/assets/{ishikawaDiagram-YF4QCWOH-BjqaoU3U.js → ishikawaDiagram-YF4QCWOH-1pACxMTX.js} +1 -1
- package/packages/web/dist/assets/{journeyDiagram-JHISSGLW-CBmdEVrd.js → journeyDiagram-JHISSGLW-XGbzfw-v.js} +1 -1
- package/packages/web/dist/assets/{kanban-definition-UN3LZRKU-U8njjMLZ.js → kanban-definition-UN3LZRKU-Bvl-nMak.js} +1 -1
- package/packages/web/dist/assets/{linear-DF66GPs1.js → linear-uNToO4bv.js} +1 -1
- package/packages/web/dist/assets/{mermaid.core-CouJz7Ph.js → mermaid.core-CrChvyj6.js} +4 -4
- package/packages/web/dist/assets/{mindmap-definition-RKZ34NQL-BYIf0oFt.js → mindmap-definition-RKZ34NQL-C6DaQ4Fm.js} +1 -1
- package/packages/web/dist/assets/{pieDiagram-4H26LBE5-Cm8rgg09.js → pieDiagram-4H26LBE5-CEh8lKyF.js} +1 -1
- package/packages/web/dist/assets/{quadrantDiagram-W4KKPZXB-C3Il5kOH.js → quadrantDiagram-W4KKPZXB-z4zvICSM.js} +1 -1
- package/packages/web/dist/assets/{requirementDiagram-4Y6WPE33-D3PE2nTe.js → requirementDiagram-4Y6WPE33-BBltkjsj.js} +1 -1
- package/packages/web/dist/assets/{sankeyDiagram-5OEKKPKP-DdmvBM18.js → sankeyDiagram-5OEKKPKP-D0s95wQ4.js} +1 -1
- package/packages/web/dist/assets/{sequenceDiagram-3UESZ5HK-B8M_GAIq.js → sequenceDiagram-3UESZ5HK-kQa65Nbt.js} +1 -1
- package/packages/web/dist/assets/{stateDiagram-AJRCARHV-DZkXnK1_.js → stateDiagram-AJRCARHV-BqVIoyED.js} +1 -1
- package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-B6OXxEra.js +2 -0
- package/packages/web/dist/assets/{timeline-definition-PNZ67QCA-IHXq7rqi.js → timeline-definition-PNZ67QCA-Bo5L62WI.js} +1 -1
- package/packages/web/dist/assets/{vennDiagram-CIIHVFJN-Dga7tLtT.js → vennDiagram-CIIHVFJN-CN9rH4lT.js} +1 -1
- package/packages/web/dist/assets/{wardley-L42UT6IY-CmcwnEJ-.js → wardley-L42UT6IY-BKhnD_W_.js} +1 -1
- package/packages/web/dist/assets/{wardleyDiagram-YWT4CUSO-zHuSLrD4.js → wardleyDiagram-YWT4CUSO-XHTbZzXj.js} +1 -1
- package/packages/web/dist/assets/{xychartDiagram-2RQKCTM6-D93MU_ic.js → xychartDiagram-2RQKCTM6--t9XJLbR.js} +1 -1
- package/packages/web/dist/index.html +1 -1
- package/packages/web/dist/sw.js +1 -1
- package/packages/server/dist/cli/service/backend.d.ts +0 -115
- package/packages/server/dist/cli/service/backend.js +0 -19
- package/packages/web/dist/assets/DiscoverPage-C4OPLNO8.js +0 -2
- package/packages/web/dist/assets/channel-JrEedxB8.js +0 -2
- package/packages/web/dist/assets/classDiagram-4FO5ZUOK-CHJiQp-W.js +0 -2
- package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-CHJiQp-W.js +0 -2
- package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-CjCdT9Ls.js +0 -2
|
@@ -3,16 +3,17 @@ export declare function lockPath(home: string): string;
|
|
|
3
3
|
export interface LockBody {
|
|
4
4
|
pid: number;
|
|
5
5
|
startedAt: string;
|
|
6
|
-
/** What
|
|
6
|
+
/** What holds it, for the message: `paddock update --to 0.81.0`. */
|
|
7
7
|
command?: string;
|
|
8
8
|
}
|
|
9
9
|
export interface Lock {
|
|
10
10
|
path: string;
|
|
11
|
-
/**
|
|
11
|
+
/** Removes the file if it's still ours. Safe to call twice. */
|
|
12
12
|
release(): void;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
14
|
+
/** Another live process holds the lock. */
|
|
15
15
|
export declare class LockHeldError extends Error {
|
|
16
|
+
name: string;
|
|
16
17
|
readonly holder: LockBody;
|
|
17
18
|
readonly path: string;
|
|
18
19
|
constructor(file: string, holder: LockBody);
|
|
@@ -21,20 +22,14 @@ export interface AcquireOptions {
|
|
|
21
22
|
home: string;
|
|
22
23
|
pid?: number;
|
|
23
24
|
command?: string;
|
|
24
|
-
/** Injected so a stale lock can be tested without spawning a process to kill. */
|
|
25
25
|
isAlive?: (pid: number) => boolean;
|
|
26
26
|
now?: () => Date;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* The retry is deliberately ONCE. After removing a stale lock the create is
|
|
32
|
-
* attempted again, and a second `EEXIST` means another process won the same
|
|
33
|
-
* race in that microsecond — which is a genuine holder, not a stale file, so it
|
|
34
|
-
* refuses. Looping instead would be a way to spin forever against a lock being
|
|
35
|
-
* recreated as fast as it is deleted.
|
|
29
|
+
* Takes the lock, or throws `LockHeldError`. After removing a stale lock it tries once more, and only once: a second
|
|
30
|
+
* `EEXIST` means another process won the same race just now, which is a real holder.
|
|
36
31
|
*/
|
|
37
32
|
export declare function acquireLock(o: AcquireOptions): Lock;
|
|
38
33
|
export declare function readLock(file: string): LockBody | null;
|
|
39
|
-
/**
|
|
34
|
+
/** Removes the lock only if it names `pid`, or names nothing: never somebody else's. */
|
|
40
35
|
export declare function releaseLock(file: string, pid: number): void;
|
|
@@ -1,108 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* is removed, because the alternative is an install that can never update
|
|
13
|
-
* itself again without someone finding out about a file they have never heard
|
|
14
|
-
* of.
|
|
15
|
-
*
|
|
16
|
-
* Generic: wave C4 lifts this into `reference/lock.ts`. Node built-ins only.
|
|
17
|
-
*/
|
|
18
|
-
import fs from "node:fs";
|
|
19
|
-
import path from "node:path";
|
|
20
|
-
export const LOCK_FILE = "update.lock";
|
|
1
|
+
// From edspencer/cli-contract reference/lock.ts @ 0.2; local changes: `.ts` import specifiers → `.js`, and `exit-codes`, `output` and `runtime-file` imported from where Paddock keeps them (`../`, `../../`).
|
|
2
|
+
// `<home>/update.lock`: one update at a time (SPEC §5 step 1). Two at once is easy to cause (an Ansible run and a
|
|
3
|
+
// person at a terminal) and bad: the second stops the service the first just started, or installs an older target
|
|
4
|
+
// over a newer one, and the loser's rollback "restores" a version nobody asked for.
|
|
5
|
+
//
|
|
6
|
+
// So the lock is an `O_EXCL` create holding the pid. A live holder refuses with exit 1; a dead one (a crashed or
|
|
7
|
+
// killed update) is stale and is removed, or the install could never update itself again. Node built-ins only.
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { isPidAlive } from '../../runtime-file.js';
|
|
11
|
+
export const LOCK_FILE = 'update.lock';
|
|
21
12
|
export function lockPath(home) {
|
|
22
13
|
return path.join(home, LOCK_FILE);
|
|
23
14
|
}
|
|
24
|
-
/**
|
|
15
|
+
/** Another live process holds the lock. */
|
|
25
16
|
export class LockHeldError extends Error {
|
|
17
|
+
name = 'LockHeldError';
|
|
26
18
|
holder;
|
|
27
19
|
path;
|
|
28
20
|
constructor(file, holder) {
|
|
29
|
-
super(`another update is already running (pid ${holder.pid}, started ${holder.startedAt})
|
|
30
|
-
` If that is wrong, remove ${file} and try again.`);
|
|
21
|
+
super(`another update is already running (pid ${holder.pid}, started ${holder.startedAt}). If that's wrong, remove ${file} and try again.`);
|
|
31
22
|
this.holder = holder;
|
|
32
23
|
this.path = file;
|
|
33
24
|
}
|
|
34
25
|
}
|
|
35
26
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* The retry is deliberately ONCE. After removing a stale lock the create is
|
|
39
|
-
* attempted again, and a second `EEXIST` means another process won the same
|
|
40
|
-
* race in that microsecond — which is a genuine holder, not a stale file, so it
|
|
41
|
-
* refuses. Looping instead would be a way to spin forever against a lock being
|
|
42
|
-
* recreated as fast as it is deleted.
|
|
27
|
+
* Takes the lock, or throws `LockHeldError`. After removing a stale lock it tries once more, and only once: a second
|
|
28
|
+
* `EEXIST` means another process won the same race just now, which is a real holder.
|
|
43
29
|
*/
|
|
44
30
|
export function acquireLock(o) {
|
|
45
31
|
const file = lockPath(o.home);
|
|
46
32
|
const pid = o.pid ?? process.pid;
|
|
47
|
-
const isAlive = o.isAlive ??
|
|
33
|
+
const isAlive = o.isAlive ?? isPidAlive;
|
|
48
34
|
const body = {
|
|
49
35
|
pid,
|
|
50
36
|
startedAt: (o.now ?? (() => new Date()))().toISOString(),
|
|
51
37
|
...(o.command !== undefined ? { command: o.command } : {}),
|
|
52
38
|
};
|
|
53
39
|
fs.mkdirSync(o.home, { recursive: true });
|
|
54
|
-
for (let attempt = 0
|
|
40
|
+
for (let attempt = 0;; attempt++) {
|
|
55
41
|
try {
|
|
56
|
-
// 'wx' is O_CREAT|O_EXCL
|
|
57
|
-
|
|
58
|
-
fs.writeFileSync(file, `${JSON.stringify(body, null, 2)}\n`, { flag: "wx", mode: 0o600 });
|
|
42
|
+
// 'wx' is O_CREAT | O_EXCL: the create is the lock, in one call.
|
|
43
|
+
fs.writeFileSync(file, `${JSON.stringify(body, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
|
|
59
44
|
return { path: file, release: () => releaseLock(file, pid) };
|
|
60
45
|
}
|
|
61
46
|
catch (err) {
|
|
62
|
-
if (err.code !==
|
|
47
|
+
if (err.code !== 'EEXIST')
|
|
63
48
|
throw err;
|
|
64
49
|
const holder = readLock(file);
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (holder !== null && isAlive(holder.pid) && holder.pid !== pid) {
|
|
69
|
-
throw new LockHeldError(file, holder);
|
|
70
|
-
}
|
|
71
|
-
if (attempt === 1) {
|
|
72
|
-
throw new LockHeldError(file, holder ?? { pid: 0, startedAt: "unknown" });
|
|
50
|
+
// A lock with no readable pid counts as stale: nobody can show a process holds it.
|
|
51
|
+
if ((holder !== null && holder.pid !== pid && isAlive(holder.pid)) || attempt > 0) {
|
|
52
|
+
throw new LockHeldError(file, holder ?? { pid: 0, startedAt: 'unknown' });
|
|
73
53
|
}
|
|
74
54
|
fs.rmSync(file, { force: true });
|
|
75
55
|
}
|
|
76
56
|
}
|
|
77
|
-
// Unreachable: both branches of the loop above either return or throw.
|
|
78
|
-
throw new LockHeldError(file, { pid: 0, startedAt: "unknown" });
|
|
79
57
|
}
|
|
80
58
|
export function readLock(file) {
|
|
81
59
|
try {
|
|
82
|
-
const raw = JSON.parse(fs.readFileSync(file,
|
|
83
|
-
if (Number.isInteger(raw.pid) && typeof raw.startedAt ===
|
|
60
|
+
const raw = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
61
|
+
if (Number.isInteger(raw.pid) && typeof raw.startedAt === 'string')
|
|
84
62
|
return raw;
|
|
85
63
|
}
|
|
86
64
|
catch {
|
|
87
|
-
|
|
65
|
+
// missing, half-written, or not a lock
|
|
88
66
|
}
|
|
89
67
|
return null;
|
|
90
68
|
}
|
|
91
|
-
/**
|
|
69
|
+
/** Removes the lock only if it names `pid`, or names nothing: never somebody else's. */
|
|
92
70
|
export function releaseLock(file, pid) {
|
|
93
71
|
const holder = readLock(file);
|
|
94
72
|
if (holder === null || holder.pid === pid)
|
|
95
73
|
fs.rmSync(file, { force: true });
|
|
96
74
|
}
|
|
97
|
-
function defaultIsAlive(pid) {
|
|
98
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
99
|
-
return false;
|
|
100
|
-
try {
|
|
101
|
-
process.kill(pid, 0);
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
catch (err) {
|
|
105
|
-
// EPERM means the process exists and is not ours, which still counts.
|
|
106
|
-
return err.code === "EPERM";
|
|
107
|
-
}
|
|
108
|
-
}
|
|
@@ -1,109 +1,74 @@
|
|
|
1
|
-
import type { PackageManager } from
|
|
1
|
+
import type { PackageManager } from './install-method.js';
|
|
2
2
|
export interface RunResult {
|
|
3
3
|
status: number | null;
|
|
4
4
|
stdout: string;
|
|
5
5
|
stderr: string;
|
|
6
6
|
}
|
|
7
|
-
/**
|
|
7
|
+
/** Runs a command to completion; never throws for a non-zero exit. Injected everywhere, for tests. */
|
|
8
8
|
export type Spawn = (cmd: string, args: string[], opts?: {
|
|
9
9
|
env?: NodeJS.ProcessEnv;
|
|
10
10
|
shell?: boolean;
|
|
11
11
|
timeoutMs?: number;
|
|
12
12
|
}) => RunResult;
|
|
13
13
|
export declare const spawnSyncRunner: Spawn;
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
* `<dirname(execPath)>/npm` on POSIX; on Windows the sibling is `npm.cmd`.
|
|
18
|
-
* Falls back to the bare name — if the sibling is not there (a distro that
|
|
19
|
-
* splits node and npm into separate packages), PATH is the only answer left and
|
|
20
|
-
* is better than failing.
|
|
21
|
-
*/
|
|
22
|
-
export declare function npmBeside(execPath?: string, platform?: NodeJS.Platform): string;
|
|
14
|
+
/** How long an install may take. */
|
|
15
|
+
export declare const INSTALL_TIMEOUT_MS: number;
|
|
23
16
|
export interface NpmContext {
|
|
24
17
|
spawn?: Spawn;
|
|
25
18
|
env?: NodeJS.ProcessEnv;
|
|
26
19
|
execPath?: string;
|
|
27
20
|
platform?: NodeJS.Platform;
|
|
21
|
+
/** Injected for tests: does this file exist? */
|
|
22
|
+
exists?: (p: string) => boolean;
|
|
23
|
+
realpath?: (p: string) => string;
|
|
28
24
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
/**
|
|
26
|
+
* How to run the npm that belongs to this Node: `[execPath, npm-cli.js]`, found beside it (`<dir>/npm` is a symlink
|
|
27
|
+
* to it on POSIX; on Windows it's `<dir>/node_modules/npm/bin/npm-cli.js`). Falls back to `npm` on the PATH, for a
|
|
28
|
+
* distribution that packages npm separately.
|
|
29
|
+
*/
|
|
30
|
+
export declare function npmCommand(ctx?: NpmContext): {
|
|
31
|
+
cmd: string;
|
|
32
|
+
pre: string[];
|
|
33
|
+
shell: boolean;
|
|
34
|
+
};
|
|
35
|
+
/** Runs this Node's npm. */
|
|
36
|
+
export declare function runNpm(args: string[], ctx?: NpmContext, timeoutMs?: number): RunResult;
|
|
37
|
+
/** `npm root -g`, or undefined when npm can't answer. */
|
|
32
38
|
export declare function npmGlobalRoot(ctx?: NpmContext): string | undefined;
|
|
33
|
-
/** `npm config get <key>`, or undefined.
|
|
39
|
+
/** `npm config get <key>`, or undefined. */
|
|
34
40
|
export declare function npmConfigGet(key: string, ctx?: NpmContext): string | undefined;
|
|
35
41
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* this check. It consults the mode bits, which say yes for root and say
|
|
40
|
-
* nothing about a read-only mount, an immutable flag, or an ACL — the usual
|
|
41
|
-
* shapes of "npm install -g needs sudo". So the directory is probed by
|
|
42
|
-
* CREATING something in it and removing it again, which is the same syscall
|
|
43
|
-
* npm is about to make.
|
|
44
|
-
*
|
|
45
|
-
* A missing directory walks up to its nearest existing parent: npm would create
|
|
46
|
-
* it, and whether it can is the parent's question.
|
|
42
|
+
* Whether `dir` is really writable (SPEC §5 step 5): a folder is created in it and removed again, the same call npm
|
|
43
|
+
* is about to make. `access(W_OK)` alone says yes to root on a read-only mount, and nothing about ACLs or immutable
|
|
44
|
+
* flags. A folder that doesn't exist yet asks its nearest existing parent, since npm would create it.
|
|
47
45
|
*/
|
|
48
46
|
export declare function isWritable(dir: string): boolean;
|
|
49
47
|
/**
|
|
50
|
-
* Where a
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* Only npm can be asked properly (`npm root -g`). For the others the install
|
|
54
|
-
* command's own target is a good enough proxy — and if it is wrong, the install
|
|
55
|
-
* fails with that manager's own message and the rollback runs, which is a worse
|
|
56
|
-
* experience than the sudo hint but not a worse outcome.
|
|
48
|
+
* Where a manager would install, for `isWritable` to ask about. Only npm can be asked (`npm root -g`). For the others
|
|
49
|
+
* there's no answer, and an install that can't write fails with that manager's own message, and rolls back.
|
|
57
50
|
*/
|
|
58
51
|
export declare function globalRootFor(manager: PackageManager, ctx?: NpmContext): string | undefined;
|
|
59
|
-
|
|
60
|
-
manager: PackageManager;
|
|
61
|
-
packageName: string;
|
|
62
|
-
version: string;
|
|
63
|
-
spawn?: Spawn;
|
|
64
|
-
env?: NodeJS.ProcessEnv;
|
|
65
|
-
execPath?: string;
|
|
66
|
-
platform?: NodeJS.Platform;
|
|
67
|
-
}
|
|
68
|
-
/** The argv for a global install, per SPEC §6's last column. */
|
|
52
|
+
/** The global install command, per SPEC §6's last column. */
|
|
69
53
|
export declare function installArgs(manager: PackageManager, spec: string): {
|
|
70
54
|
cmd: string;
|
|
71
55
|
args: string[];
|
|
72
56
|
};
|
|
57
|
+
/** Installs `<name>@<version>` with the manager this install came from. pnpm, bun and volta come from the PATH. */
|
|
58
|
+
export declare function installPackage(o: NpmContext & {
|
|
59
|
+
manager: PackageManager;
|
|
60
|
+
packageName: string;
|
|
61
|
+
version: string;
|
|
62
|
+
}): RunResult;
|
|
73
63
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* three are looked up on PATH, which is the only place they are: pnpm, bun and
|
|
78
|
-
* volta are not installed beside node.
|
|
79
|
-
*/
|
|
80
|
-
export declare function installPackage(o: InstallInput): RunResult;
|
|
81
|
-
/**
|
|
82
|
-
* `npm cache add <name>@<version>` — fetch before stopping anything
|
|
83
|
-
* (SPEC §5 step 8).
|
|
84
|
-
*
|
|
85
|
-
* Both the target and the CURRENT version are cached, and caching the current
|
|
86
|
-
* one is the half that is easy to leave out and expensive to miss: it is what
|
|
87
|
-
* gives the rollback a package to reinstall when the reason the update failed
|
|
88
|
-
* was the network. Done while the old version is still serving, so a failure
|
|
89
|
-
* here costs nothing but a message.
|
|
90
|
-
*
|
|
91
|
-
* `npm cache add` is used whatever the manager is, because every one of these
|
|
92
|
-
* layouts has an npm available (it ships with Node) and the point is only to
|
|
93
|
-
* have the tarball on the machine. A failure is reported and not fatal — the
|
|
94
|
-
* install below may still succeed from the network.
|
|
64
|
+
* `npm cache add <name>@<version>` (SPEC §5 step 8), for the target and for the current version, while the old one
|
|
65
|
+
* still serves. Caching the current one is what lets a rollback reinstall it when the network is what failed. npm
|
|
66
|
+
* is used whatever the manager: it ships with Node, and the point is only to have the tarball on the machine.
|
|
95
67
|
*/
|
|
96
68
|
export declare function cacheAdd(packageName: string, version: string, ctx?: NpmContext): RunResult;
|
|
97
69
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* before it hands over to the new CLI. The alternative — reading the
|
|
102
|
-
* `package.json` next to it — answers a different question: npm writes the
|
|
103
|
-
* manifest and the files in one operation, but a partially-updated global
|
|
104
|
-
* folder, a stale symlink, or a pnpm layout whose versioned realpath moved all
|
|
105
|
-
* show up here and not there.
|
|
70
|
+
* A CLI's version, by running it: `node <cli> --version`. This is how `update` shows the install replaced the files
|
|
71
|
+
* before it hands over. A `package.json` beside it answers a different question: a stale symlink, or a pnpm layout
|
|
72
|
+
* whose versioned path moved, shows up here and not there.
|
|
106
73
|
*/
|
|
107
74
|
export declare function cliVersion(cliPath: string, ctx?: NpmContext): string | undefined;
|
|
108
|
-
/** A throwaway directory, for callers that need one (the writability probe's sibling). */
|
|
109
|
-
export declare function tmpDir(prefix: string): string;
|