paratix 0.10.0 → 0.12.2
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/README.md +7 -1
- package/dist/chunk-YOSHYUST.js +19058 -0
- package/dist/chunk-YOSHYUST.js.map +1 -0
- package/dist/cli.js +5091 -224
- package/dist/cli.js.map +1 -1
- package/dist/{user-CJDqZC8n.d.ts → index-udpAybq3.d.ts} +637 -36
- package/dist/index.d.ts +51 -7
- package/dist/index.js +965 -73
- package/dist/index.js.map +1 -1
- package/dist/modules/index.d.ts +1 -119
- package/dist/modules/index.js +1 -2
- package/llm-guide.md +176 -35
- package/package.json +10 -8
- package/dist/chunk-47PTUZZR.js +0 -495
- package/dist/chunk-47PTUZZR.js.map +0 -1
- package/dist/chunk-M7GETOJ5.js +0 -6237
- package/dist/chunk-M7GETOJ5.js.map +0 -1
- package/dist/chunk-NRDLYHJL.js +0 -1866
- package/dist/chunk-NRDLYHJL.js.map +0 -1
- package/dist/cli.d.ts +0 -62
- package/dist/types-Cl2Muw1x.d.ts +0 -254
package/dist/index.js
CHANGED
|
@@ -1,72 +1,75 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
printModuleResult,
|
|
4
|
-
printRecipeHeader,
|
|
5
|
-
printRecipeModuleResult,
|
|
6
|
-
runSignalModules,
|
|
7
|
-
startModuleSpinner,
|
|
8
|
-
withRecipeOutputScope
|
|
9
|
-
} from "./chunk-47PTUZZR.js";
|
|
10
|
-
import {
|
|
2
|
+
CommandError,
|
|
11
3
|
NEEDS_APPLY,
|
|
12
4
|
apt,
|
|
13
5
|
archive,
|
|
6
|
+
assertValidModuleMetaEntries,
|
|
7
|
+
assertValidModuleMetaEntry,
|
|
14
8
|
command,
|
|
15
9
|
compose,
|
|
10
|
+
createNullPrototypeEnvironment,
|
|
16
11
|
cron,
|
|
12
|
+
describeHostValidationFailure,
|
|
17
13
|
detectPackageManager,
|
|
14
|
+
diffEnvironmentToMetaEntries,
|
|
18
15
|
download,
|
|
16
|
+
environmentMeta,
|
|
17
|
+
environmentToMetaEntries,
|
|
19
18
|
failed,
|
|
20
19
|
failedCommand,
|
|
21
20
|
file,
|
|
21
|
+
getRunnerAbortSignal,
|
|
22
22
|
git,
|
|
23
23
|
group,
|
|
24
24
|
hostname,
|
|
25
|
+
inspectRedactedDiagnosticValue,
|
|
26
|
+
isBooleanEnvironmentMetaEntry,
|
|
27
|
+
isEnvironmentMetaEntry,
|
|
28
|
+
isLazyEnvironmentMetaEntry,
|
|
29
|
+
isNumberEnvironmentMetaEntry,
|
|
25
30
|
isPackageInstalled,
|
|
31
|
+
isSshdPortMetaEntry,
|
|
32
|
+
isStringEnvironmentMetaEntry,
|
|
33
|
+
isSystemHostMetaEntry,
|
|
34
|
+
isSystemRebootMetaEntry,
|
|
35
|
+
maskRegisteredSecrets,
|
|
36
|
+
mergeEnvironmentFromMeta,
|
|
37
|
+
meta,
|
|
26
38
|
mount,
|
|
27
39
|
net,
|
|
28
40
|
op,
|
|
29
41
|
pkg,
|
|
30
42
|
quadlet,
|
|
31
43
|
releaseUpgrade,
|
|
44
|
+
resolveEnvironment,
|
|
32
45
|
rsync,
|
|
46
|
+
sanitizeTerminalText,
|
|
33
47
|
script,
|
|
34
48
|
service,
|
|
49
|
+
shellQuote,
|
|
35
50
|
ssh,
|
|
36
51
|
sshd,
|
|
52
|
+
sshdPortMeta,
|
|
53
|
+
swap,
|
|
37
54
|
sysctl,
|
|
38
55
|
system,
|
|
39
|
-
systemd,
|
|
40
|
-
ufw,
|
|
41
|
-
user
|
|
42
|
-
} from "./chunk-M7GETOJ5.js";
|
|
43
|
-
import {
|
|
44
|
-
CommandError,
|
|
45
|
-
assertValidModuleMetaEntries,
|
|
46
|
-
assertValidModuleMetaEntry,
|
|
47
|
-
diffEnvironmentToMetaEntries,
|
|
48
|
-
environmentMeta,
|
|
49
|
-
environmentToMetaEntries,
|
|
50
|
-
isBooleanEnvironmentMetaEntry,
|
|
51
|
-
isEnvironmentMetaEntry,
|
|
52
|
-
isLazyEnvironmentMetaEntry,
|
|
53
|
-
isNumberEnvironmentMetaEntry,
|
|
54
|
-
isSshdPortMetaEntry,
|
|
55
|
-
isStringEnvironmentMetaEntry,
|
|
56
|
-
isSystemHostMetaEntry,
|
|
57
|
-
isSystemRebootMetaEntry,
|
|
58
|
-
mergeEnvironmentFromMeta,
|
|
59
|
-
meta,
|
|
60
|
-
shellQuote,
|
|
61
|
-
sshdPortMeta,
|
|
62
56
|
systemHostMeta,
|
|
63
57
|
systemRebootMeta,
|
|
58
|
+
systemd,
|
|
59
|
+
timer,
|
|
60
|
+
ufw,
|
|
61
|
+
user,
|
|
62
|
+
validateHostLabel,
|
|
64
63
|
validateSshConfig
|
|
65
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-YOSHYUST.js";
|
|
66
65
|
|
|
67
66
|
// src/conditionalModules.ts
|
|
68
67
|
function createConditionalApplyState(environment) {
|
|
69
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
environment: Object.assign(createNullPrototypeEnvironment(), environment),
|
|
70
|
+
meta: [],
|
|
71
|
+
status: "ok"
|
|
72
|
+
};
|
|
70
73
|
}
|
|
71
74
|
function markConditionalApplyChanged(state) {
|
|
72
75
|
return { ...state, status: "changed" };
|
|
@@ -74,7 +77,15 @@ function markConditionalApplyChanged(state) {
|
|
|
74
77
|
async function executeConditionalApply(parameters) {
|
|
75
78
|
const { dryRun, environment, module, ssh: ssh2 } = parameters;
|
|
76
79
|
if (dryRun && module._applyDryRun != null) {
|
|
77
|
-
return module._applyDryRun(ssh2, environment
|
|
80
|
+
return module._applyDryRun(ssh2, environment, {
|
|
81
|
+
shutdownSignal: parameters.shutdownSignal
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (module._supportsChildStepHook === true) {
|
|
85
|
+
return module.apply(ssh2, environment, {
|
|
86
|
+
onChildStep: parameters.onChildStep,
|
|
87
|
+
shutdownSignal: parameters.shutdownSignal
|
|
88
|
+
});
|
|
78
89
|
}
|
|
79
90
|
return module.apply(ssh2, environment);
|
|
80
91
|
}
|
|
@@ -82,35 +93,61 @@ function shouldExecuteConditionalApply(module, dryRun) {
|
|
|
82
93
|
if (!dryRun) return true;
|
|
83
94
|
return module._applyDryRun != null || module._dryRunBlocker === true || module._dryRunMetaProducer === true;
|
|
84
95
|
}
|
|
85
|
-
|
|
96
|
+
function getConditionalChildConnection(module, ssh2) {
|
|
97
|
+
return module.local === true ? null : ssh2;
|
|
98
|
+
}
|
|
99
|
+
async function mergeConditionalApplyState(preserveControlPlaneMeta, state, result) {
|
|
86
100
|
const environment = await mergeEnvironmentFromMeta(state.environment, result.meta);
|
|
101
|
+
const resultMeta = result.meta == null || preserveControlPlaneMeta ? result.meta : result.meta.filter(isEnvironmentMetaEntry);
|
|
87
102
|
return {
|
|
88
103
|
environment,
|
|
89
104
|
flushSignals: result._flushSignals === true ? true : state.flushSignals,
|
|
90
|
-
meta:
|
|
105
|
+
meta: resultMeta == null ? state.meta : [...state.meta, ...resultMeta],
|
|
91
106
|
status: result.status === "changed" ? "changed" : state.status,
|
|
92
107
|
stopRun: result._stopRun === true ? true : state.stopRun
|
|
93
108
|
};
|
|
94
109
|
}
|
|
110
|
+
async function notifyConditionalChildStep(parameters) {
|
|
111
|
+
if (parameters.onChildStep == null) return;
|
|
112
|
+
await parameters.onChildStep({
|
|
113
|
+
_flushSignals: parameters.result._flushSignals,
|
|
114
|
+
_stopRun: parameters.result._stopRun,
|
|
115
|
+
env: parameters.environment,
|
|
116
|
+
meta: parameters.result.meta,
|
|
117
|
+
status: parameters.result.status
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function processConditionalApplyResult(parameters) {
|
|
121
|
+
const state = await mergeConditionalApplyState(
|
|
122
|
+
parameters.preserveControlPlaneMeta,
|
|
123
|
+
parameters.state,
|
|
124
|
+
parameters.result
|
|
125
|
+
);
|
|
126
|
+
await notifyConditionalChildStep({
|
|
127
|
+
environment: state.environment,
|
|
128
|
+
onChildStep: parameters.onChildStep,
|
|
129
|
+
result: parameters.result
|
|
130
|
+
});
|
|
131
|
+
return state;
|
|
132
|
+
}
|
|
95
133
|
async function applyConditionalModules(parameters) {
|
|
96
134
|
const { dryRun = false, modules, ssh: ssh2 } = parameters;
|
|
135
|
+
const preserveControlPlaneMeta = parameters.onChildStep == null;
|
|
136
|
+
const shutdownSignal = parameters.shutdownSignal ?? (() => null);
|
|
97
137
|
let state = createConditionalApplyState(parameters.environment);
|
|
98
138
|
for (const currentModule of modules) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
if (!shouldExecuteConditionalApply(currentModule, dryRun)) {
|
|
102
|
-
state = markConditionalApplyChanged(state);
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
const result = await executeConditionalApply({
|
|
139
|
+
const step = await applyConditionalModuleStep({
|
|
140
|
+
currentModule,
|
|
106
141
|
dryRun,
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
142
|
+
onChildStep: parameters.onChildStep,
|
|
143
|
+
preserveControlPlaneMeta,
|
|
144
|
+
shutdownSignal,
|
|
145
|
+
ssh: ssh2,
|
|
146
|
+
state
|
|
110
147
|
});
|
|
111
|
-
if (
|
|
112
|
-
state =
|
|
113
|
-
if (
|
|
148
|
+
if (step.kind === "failed") return step.result;
|
|
149
|
+
state = step.state;
|
|
150
|
+
if (step.kind === "break") break;
|
|
114
151
|
}
|
|
115
152
|
return {
|
|
116
153
|
_flushSignals: state.flushSignals,
|
|
@@ -119,6 +156,34 @@ async function applyConditionalModules(parameters) {
|
|
|
119
156
|
status: state.status
|
|
120
157
|
};
|
|
121
158
|
}
|
|
159
|
+
async function applyConditionalModuleStep(parameters) {
|
|
160
|
+
if (parameters.shutdownSignal() != null) {
|
|
161
|
+
return { kind: "break", state: parameters.state };
|
|
162
|
+
}
|
|
163
|
+
const connection = getConditionalChildConnection(parameters.currentModule, parameters.ssh);
|
|
164
|
+
const checkResult = await parameters.currentModule.check(connection, parameters.state.environment);
|
|
165
|
+
if (checkResult === "ok") return { kind: "continue", state: parameters.state };
|
|
166
|
+
if (parameters.shutdownSignal() != null) return { kind: "break", state: parameters.state };
|
|
167
|
+
if (!shouldExecuteConditionalApply(parameters.currentModule, parameters.dryRun)) {
|
|
168
|
+
return { kind: "continue", state: markConditionalApplyChanged(parameters.state) };
|
|
169
|
+
}
|
|
170
|
+
const result = await executeConditionalApply({
|
|
171
|
+
dryRun: parameters.dryRun,
|
|
172
|
+
environment: parameters.state.environment,
|
|
173
|
+
module: parameters.currentModule,
|
|
174
|
+
onChildStep: parameters.onChildStep,
|
|
175
|
+
shutdownSignal: parameters.shutdownSignal,
|
|
176
|
+
ssh: connection
|
|
177
|
+
});
|
|
178
|
+
if (result.status === "failed") return { kind: "failed", result };
|
|
179
|
+
const state = await processConditionalApplyResult({
|
|
180
|
+
onChildStep: parameters.onChildStep,
|
|
181
|
+
preserveControlPlaneMeta: parameters.preserveControlPlaneMeta,
|
|
182
|
+
result,
|
|
183
|
+
state: parameters.state
|
|
184
|
+
});
|
|
185
|
+
return { kind: state.stopRun === true ? "break" : "continue", state };
|
|
186
|
+
}
|
|
122
187
|
function shouldExecuteConditionalDryRun(module) {
|
|
123
188
|
return module._applyDryRun != null || module._dryRunBlocker === true || module._dryRunMetaProducer === true;
|
|
124
189
|
}
|
|
@@ -126,9 +191,10 @@ function whenNeedsDryRunApply(modules) {
|
|
|
126
191
|
return modules.some((module) => shouldExecuteConditionalDryRun(module));
|
|
127
192
|
}
|
|
128
193
|
async function checkConditionalModules(modules, ssh2, environment) {
|
|
129
|
-
const currentEnvironment =
|
|
194
|
+
const currentEnvironment = Object.assign(createNullPrototypeEnvironment(), environment);
|
|
130
195
|
for (const currentModule of modules) {
|
|
131
|
-
const
|
|
196
|
+
const connection = getConditionalChildConnection(currentModule, ssh2);
|
|
197
|
+
const result = await currentModule.check(connection, currentEnvironment);
|
|
132
198
|
if (result === NEEDS_APPLY) {
|
|
133
199
|
return NEEDS_APPLY;
|
|
134
200
|
}
|
|
@@ -137,11 +203,17 @@ async function checkConditionalModules(modules, ssh2, environment) {
|
|
|
137
203
|
}
|
|
138
204
|
function createWhenDryRunApply(condition, modules, needsDryRunApply) {
|
|
139
205
|
if (!needsDryRunApply) return void 0;
|
|
140
|
-
return async (ssh2, environment) => {
|
|
206
|
+
return async (ssh2, environment, options) => {
|
|
141
207
|
if (!await condition(ssh2, environment)) {
|
|
142
208
|
return { status: "skipped" };
|
|
143
209
|
}
|
|
144
|
-
return applyConditionalModules({
|
|
210
|
+
return applyConditionalModules({
|
|
211
|
+
dryRun: true,
|
|
212
|
+
environment,
|
|
213
|
+
modules,
|
|
214
|
+
shutdownSignal: options?.shutdownSignal,
|
|
215
|
+
ssh: ssh2
|
|
216
|
+
});
|
|
145
217
|
};
|
|
146
218
|
}
|
|
147
219
|
function createConditionalModule(parameters) {
|
|
@@ -152,14 +224,21 @@ function createConditionalModule(parameters) {
|
|
|
152
224
|
needsDryRunApply
|
|
153
225
|
);
|
|
154
226
|
return {
|
|
227
|
+
_supportsChildStepHook: true,
|
|
155
228
|
...parameters.modules.some((module) => module._dryRunBlocker === true) ? { _dryRunBlocker: true } : {},
|
|
156
229
|
...parameters.modules.some((module) => module._dryRunMetaProducer === true) ? { _dryRunMetaProducer: true } : {},
|
|
157
230
|
...applyDryRun == null ? {} : { _applyDryRun: applyDryRun },
|
|
158
|
-
async apply(ssh2, environment) {
|
|
231
|
+
async apply(ssh2, environment, options) {
|
|
159
232
|
if (!await parameters.condition(ssh2, environment)) {
|
|
160
233
|
return { status: "skipped" };
|
|
161
234
|
}
|
|
162
|
-
return applyConditionalModules({
|
|
235
|
+
return applyConditionalModules({
|
|
236
|
+
environment,
|
|
237
|
+
modules: parameters.modules,
|
|
238
|
+
onChildStep: options?.onChildStep,
|
|
239
|
+
shutdownSignal: options?.shutdownSignal,
|
|
240
|
+
ssh: ssh2
|
|
241
|
+
});
|
|
163
242
|
},
|
|
164
243
|
async check(ssh2, environment) {
|
|
165
244
|
if (!await parameters.condition(ssh2, environment)) {
|
|
@@ -170,6 +249,8 @@ function createConditionalModule(parameters) {
|
|
|
170
249
|
name: parameters.name
|
|
171
250
|
};
|
|
172
251
|
}
|
|
252
|
+
|
|
253
|
+
// src/conditionalGuards.ts
|
|
173
254
|
function filesystemTypeName(testFlag) {
|
|
174
255
|
switch (testFlag) {
|
|
175
256
|
case "-d": {
|
|
@@ -189,7 +270,7 @@ function filesystemTypeName(testFlag) {
|
|
|
189
270
|
function createFilesystemGuard(parameters) {
|
|
190
271
|
const typeName = filesystemTypeName(parameters.testFlag);
|
|
191
272
|
return createConditionalModule({
|
|
192
|
-
|
|
273
|
+
async condition(ssh2) {
|
|
193
274
|
if (ssh2 == null) return false;
|
|
194
275
|
const exists = await ssh2.test(`test ${parameters.testFlag} ${shellQuote(parameters.path)}`);
|
|
195
276
|
return parameters.invert ? !exists : exists;
|
|
@@ -200,7 +281,7 @@ function createFilesystemGuard(parameters) {
|
|
|
200
281
|
}
|
|
201
282
|
function createCommandGuard(commandName, invert, modules) {
|
|
202
283
|
return createConditionalModule({
|
|
203
|
-
|
|
284
|
+
async condition(ssh2) {
|
|
204
285
|
if (ssh2 == null) return false;
|
|
205
286
|
const exists = await ssh2.test(`command -v ${shellQuote(commandName)} >/dev/null 2>&1`);
|
|
206
287
|
return invert ? !exists : exists;
|
|
@@ -211,7 +292,7 @@ function createCommandGuard(commandName, invert, modules) {
|
|
|
211
292
|
}
|
|
212
293
|
function createPackageGuard(packageName, invert, modules) {
|
|
213
294
|
return createConditionalModule({
|
|
214
|
-
|
|
295
|
+
async condition(ssh2) {
|
|
215
296
|
if (ssh2 == null) return false;
|
|
216
297
|
const pm = await detectPackageManager(ssh2);
|
|
217
298
|
if (pm == null) return false;
|
|
@@ -269,17 +350,70 @@ function fail(message) {
|
|
|
269
350
|
name: `fail: ${message}`
|
|
270
351
|
};
|
|
271
352
|
}
|
|
353
|
+
function normalizePauseAbortReason(reason) {
|
|
354
|
+
if (reason instanceof Error) return reason;
|
|
355
|
+
if (reason === void 0 || reason === null) return new Error("pause aborted");
|
|
356
|
+
if (typeof reason === "string") return new Error(reason);
|
|
357
|
+
return new Error("pause aborted");
|
|
358
|
+
}
|
|
359
|
+
async function waitForEnterOrAbort(abortSignal) {
|
|
360
|
+
return new Promise((resolve, reject) => {
|
|
361
|
+
let settled = false;
|
|
362
|
+
const onData = (chunk) => {
|
|
363
|
+
const input = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
364
|
+
if (!input.includes("\n") && !input.includes("\r")) return;
|
|
365
|
+
if (settled) return;
|
|
366
|
+
settled = true;
|
|
367
|
+
cleanup();
|
|
368
|
+
process.stdin.pause();
|
|
369
|
+
resolve();
|
|
370
|
+
};
|
|
371
|
+
const onAbort = () => {
|
|
372
|
+
if (settled) return;
|
|
373
|
+
settled = true;
|
|
374
|
+
cleanup();
|
|
375
|
+
process.stdin.pause();
|
|
376
|
+
reject(normalizePauseAbortReason(abortSignal?.reason));
|
|
377
|
+
};
|
|
378
|
+
const onClosed = () => {
|
|
379
|
+
if (settled) return;
|
|
380
|
+
settled = true;
|
|
381
|
+
cleanup();
|
|
382
|
+
process.stdin.pause();
|
|
383
|
+
reject(new Error("pause input closed before Enter"));
|
|
384
|
+
};
|
|
385
|
+
const onError = (error) => {
|
|
386
|
+
if (settled) return;
|
|
387
|
+
settled = true;
|
|
388
|
+
cleanup();
|
|
389
|
+
process.stdin.pause();
|
|
390
|
+
reject(error instanceof Error ? error : new Error("pause input error"));
|
|
391
|
+
};
|
|
392
|
+
function cleanup() {
|
|
393
|
+
process.stdin.removeListener("data", onData);
|
|
394
|
+
process.stdin.removeListener("end", onClosed);
|
|
395
|
+
process.stdin.removeListener("close", onClosed);
|
|
396
|
+
process.stdin.removeListener("error", onError);
|
|
397
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
398
|
+
}
|
|
399
|
+
if (abortSignal?.aborted === true) {
|
|
400
|
+
onAbort();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
process.stdin.on("end", onClosed);
|
|
404
|
+
process.stdin.on("close", onClosed);
|
|
405
|
+
process.stdin.on("error", onError);
|
|
406
|
+
process.stdin.on("data", onData);
|
|
407
|
+
process.stdin.resume();
|
|
408
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
409
|
+
});
|
|
410
|
+
}
|
|
272
411
|
function pause(message) {
|
|
273
412
|
return {
|
|
274
413
|
async apply() {
|
|
275
414
|
const promptText = message ?? "Press enter to continue...";
|
|
276
415
|
process.stdout.write(` [pause] ${promptText} `);
|
|
277
|
-
await
|
|
278
|
-
process.stdin.once("data", () => {
|
|
279
|
-
process.stdin.pause();
|
|
280
|
-
resolve();
|
|
281
|
-
});
|
|
282
|
-
});
|
|
416
|
+
await waitForEnterOrAbort(getRunnerAbortSignal());
|
|
283
417
|
return { status: "ok" };
|
|
284
418
|
},
|
|
285
419
|
// eslint-disable-next-line @typescript-eslint/require-await -- Interface requires async
|
|
@@ -377,6 +511,656 @@ var when = Object.assign(baseWhen, {
|
|
|
377
511
|
symlinkMissing: (path, ...modules) => createFilesystemGuard({ invert: true, modules, path, testFlag: "-L" })
|
|
378
512
|
});
|
|
379
513
|
|
|
514
|
+
// src/firstRunContext.ts
|
|
515
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
516
|
+
var firstRunContext = new AsyncLocalStorage();
|
|
517
|
+
function isFirstRun() {
|
|
518
|
+
return firstRunContext.getStore() === true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/dryRunDispatch.ts
|
|
522
|
+
function shouldExecuteApplyDuringDryRun(module, diffEnabled) {
|
|
523
|
+
if (module._dryRunBlocker === true || module._dryRunMetaProducer === true) return true;
|
|
524
|
+
if (diffEnabled && module._dryRunDiffProducer === true && module._applyDryRun != null) return true;
|
|
525
|
+
return module._applyDryRun != null && module._dryRunDiffProducer !== true;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/output.ts
|
|
529
|
+
import pc from "picocolors";
|
|
530
|
+
|
|
531
|
+
// src/outputFormatting.ts
|
|
532
|
+
import { stripVTControlCharacters } from "util";
|
|
533
|
+
var PACKAGE_MODULE_SUFFIX_LENGTH = 2;
|
|
534
|
+
var PACKAGE_COLUMN_GAP_WIDTH = 2;
|
|
535
|
+
var DEFAULT_TERMINAL_COLUMNS = 100;
|
|
536
|
+
var MIN_PACKAGE_COLUMNS_WIDTH = 24;
|
|
537
|
+
var MIN_PACKAGE_COLUMN_WIDTH = 18;
|
|
538
|
+
var MIN_ANIMATED_LINE_COLUMNS = 8;
|
|
539
|
+
function splitPackageModuleName(name) {
|
|
540
|
+
for (const prefix of ["package.installed: ", "package.absent: "]) {
|
|
541
|
+
if (!name.startsWith(prefix)) continue;
|
|
542
|
+
const packages = name.slice(prefix.length).split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
543
|
+
if (packages.length === 0) return null;
|
|
544
|
+
return {
|
|
545
|
+
packages,
|
|
546
|
+
summaryName: prefix.slice(0, -PACKAGE_MODULE_SUFFIX_LENGTH)
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
function getPackageColumns(parameters) {
|
|
552
|
+
if (parameters.packages.length <= 1) return parameters.packages;
|
|
553
|
+
const availableWidth = Math.max(
|
|
554
|
+
(parameters.terminalColumns ?? DEFAULT_TERMINAL_COLUMNS) - parameters.continuationIndentWidth,
|
|
555
|
+
MIN_PACKAGE_COLUMNS_WIDTH
|
|
556
|
+
);
|
|
557
|
+
const widestPackage = Math.max(...parameters.packages.map((entry) => entry.length));
|
|
558
|
+
const columnWidth = Math.max(widestPackage + PACKAGE_COLUMN_GAP_WIDTH, MIN_PACKAGE_COLUMN_WIDTH);
|
|
559
|
+
const columnCount = Math.max(Math.floor(availableWidth / columnWidth), 1);
|
|
560
|
+
const rowCount = Math.ceil(parameters.packages.length / columnCount);
|
|
561
|
+
return Array.from(
|
|
562
|
+
{ length: rowCount },
|
|
563
|
+
(_row, rowIndex) => Array.from({ length: columnCount }, (_column, columnIndex) => {
|
|
564
|
+
const packageIndex = rowIndex + columnIndex * rowCount;
|
|
565
|
+
const packageName = parameters.packages.at(packageIndex);
|
|
566
|
+
if (packageName == null) return "";
|
|
567
|
+
const isLastVisibleColumn = columnIndex === columnCount - 1 || packageIndex + rowCount >= parameters.packages.length;
|
|
568
|
+
return isLastVisibleColumn ? packageName : packageName.padEnd(columnWidth);
|
|
569
|
+
}).filter(Boolean).join("")
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
function getPackageSummaryDetail(packageCount, detail) {
|
|
573
|
+
const packageLabel = packageCount === 1 ? "1 package" : `${packageCount} packages`;
|
|
574
|
+
return detail == null ? packageLabel : `${packageLabel} \xB7 ${detail}`;
|
|
575
|
+
}
|
|
576
|
+
function fitAnimatedModuleLine(line, columns) {
|
|
577
|
+
if (columns === void 0 || columns < MIN_ANIMATED_LINE_COLUMNS) return line;
|
|
578
|
+
const plainLine = stripVTControlCharacters(line);
|
|
579
|
+
if (plainLine.length < columns) return line;
|
|
580
|
+
return `${plainLine.slice(0, columns - 1)}\u2026`;
|
|
581
|
+
}
|
|
582
|
+
function formatDisplayModule(parameters) {
|
|
583
|
+
const packageModule = splitPackageModuleName(parameters.name);
|
|
584
|
+
if (packageModule == null) {
|
|
585
|
+
return {
|
|
586
|
+
detail: parameters.detail,
|
|
587
|
+
detailLines: [],
|
|
588
|
+
name: parameters.name
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
return {
|
|
592
|
+
detail: getPackageSummaryDetail(packageModule.packages.length, parameters.detail),
|
|
593
|
+
detailLines: parameters.status === "waiting" ? [] : getPackageColumns({
|
|
594
|
+
continuationIndentWidth: parameters.continuationIndentWidth,
|
|
595
|
+
packages: packageModule.packages,
|
|
596
|
+
terminalColumns: parameters.terminalColumns
|
|
597
|
+
}),
|
|
598
|
+
name: packageModule.summaryName
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/output.ts
|
|
603
|
+
var CAUSE_INSPECT_DEPTH = 2;
|
|
604
|
+
var CAUSE_INSPECT_MAX_STRING_LENGTH = 1024;
|
|
605
|
+
var CAUSE_REDACT_BINARY_MAX_DEPTH = CAUSE_INSPECT_DEPTH + 1;
|
|
606
|
+
var MODULE_NAME_WIDTH = 56;
|
|
607
|
+
var MIN_MODULE_NAME_WIDTH = 12;
|
|
608
|
+
var OUTPUT_INDENT_UNIT = " ";
|
|
609
|
+
var SPINNER_FRAME_INTERVAL_MS = 80;
|
|
610
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
611
|
+
var STATUS_ICONS = {
|
|
612
|
+
changed: pc.yellow("\u21BA"),
|
|
613
|
+
failed: pc.red("\u2717"),
|
|
614
|
+
ok: pc.green("\u2713"),
|
|
615
|
+
skipped: pc.dim("\u2298"),
|
|
616
|
+
waiting: pc.cyan("\u23F8")
|
|
617
|
+
};
|
|
618
|
+
var activeSpinner = null;
|
|
619
|
+
var activeRecipeGuideDepths = [];
|
|
620
|
+
var pendingRecipeClosureGuideDepths = [];
|
|
621
|
+
var recipeOutputDepth = -1;
|
|
622
|
+
function supportsAnimatedModuleOutput() {
|
|
623
|
+
return process.stdout.isTTY && typeof process.stdout.clearLine === "function" && typeof process.stdout.cursorTo === "function";
|
|
624
|
+
}
|
|
625
|
+
function getModuleIcon(status, waitingFrame) {
|
|
626
|
+
return status === "waiting" ? pc.cyan(waitingFrame ?? "|") : STATUS_ICONS[status];
|
|
627
|
+
}
|
|
628
|
+
function getModuleStatusText(status) {
|
|
629
|
+
switch (status) {
|
|
630
|
+
case "changed": {
|
|
631
|
+
return pc.yellow(status);
|
|
632
|
+
}
|
|
633
|
+
case "failed": {
|
|
634
|
+
return pc.red(status);
|
|
635
|
+
}
|
|
636
|
+
case "ok": {
|
|
637
|
+
return pc.green(status);
|
|
638
|
+
}
|
|
639
|
+
case "skipped": {
|
|
640
|
+
return pc.dim(status);
|
|
641
|
+
}
|
|
642
|
+
case "waiting": {
|
|
643
|
+
return pc.cyan("running");
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function getCurrentOutputDepth() {
|
|
648
|
+
return Math.max(recipeOutputDepth, 0);
|
|
649
|
+
}
|
|
650
|
+
function getGuideDot(depth) {
|
|
651
|
+
return depth % 2 === 0 ? pc.gray("\xB7") : pc.cyan("\xB7");
|
|
652
|
+
}
|
|
653
|
+
function buildGuideIndent(baseIndent, options) {
|
|
654
|
+
const indentCharacters = Array.from(baseIndent);
|
|
655
|
+
const guideDepths = [
|
|
656
|
+
...options?.activeGuideDepths ?? activeRecipeGuideDepths,
|
|
657
|
+
...options?.extraGuideDepths ?? []
|
|
658
|
+
];
|
|
659
|
+
for (const guideDepth of guideDepths) {
|
|
660
|
+
const guideCharacterIndex = OUTPUT_INDENT_UNIT.length * (guideDepth + 1);
|
|
661
|
+
if (guideCharacterIndex >= indentCharacters.length) continue;
|
|
662
|
+
indentCharacters[guideCharacterIndex] = options?.colorize === false ? "\xB7" : getGuideDot(guideDepth);
|
|
663
|
+
}
|
|
664
|
+
return indentCharacters.join("");
|
|
665
|
+
}
|
|
666
|
+
function clearPendingRecipeClosureGuides() {
|
|
667
|
+
pendingRecipeClosureGuideDepths = [];
|
|
668
|
+
}
|
|
669
|
+
function getModuleIndent() {
|
|
670
|
+
if (recipeOutputDepth < 0) {
|
|
671
|
+
return OUTPUT_INDENT_UNIT;
|
|
672
|
+
}
|
|
673
|
+
return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 2);
|
|
674
|
+
}
|
|
675
|
+
function getRecipeHeaderIndent() {
|
|
676
|
+
if (recipeOutputDepth < 0) return "";
|
|
677
|
+
return OUTPUT_INDENT_UNIT.repeat(getCurrentOutputDepth() + 1);
|
|
678
|
+
}
|
|
679
|
+
function getErrorIndent() {
|
|
680
|
+
return `${getModuleIndent()}\u2502 `;
|
|
681
|
+
}
|
|
682
|
+
function getContinuationIndent() {
|
|
683
|
+
return `${getModuleIndent()} `;
|
|
684
|
+
}
|
|
685
|
+
async function withRecipeOutputScope(scopedOperation) {
|
|
686
|
+
recipeOutputDepth += 1;
|
|
687
|
+
try {
|
|
688
|
+
return await scopedOperation();
|
|
689
|
+
} finally {
|
|
690
|
+
activeRecipeGuideDepths = activeRecipeGuideDepths.filter((depth) => depth !== recipeOutputDepth);
|
|
691
|
+
pendingRecipeClosureGuideDepths = [recipeOutputDepth];
|
|
692
|
+
recipeOutputDepth -= 1;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function renderModuleLine(parameters) {
|
|
696
|
+
const { detail, extraGuideDepths = [], name, status, waitingFrame } = parameters;
|
|
697
|
+
const baseIndent = getModuleIndent();
|
|
698
|
+
const indent = buildGuideIndent(baseIndent, { extraGuideDepths });
|
|
699
|
+
const icon = getModuleIcon(status, waitingFrame);
|
|
700
|
+
const statusText = getModuleStatusText(status);
|
|
701
|
+
const detailSuffix = detail == null ? "" : ` ${pc.dim(detail)}`;
|
|
702
|
+
const alignedNameWidth = Math.max(MODULE_NAME_WIDTH - baseIndent.length, MIN_MODULE_NAME_WIDTH);
|
|
703
|
+
return `${indent}${icon} ${name.padEnd(alignedNameWidth)} ${statusText}${detailSuffix}`;
|
|
704
|
+
}
|
|
705
|
+
function writeAnimatedModuleLine(line) {
|
|
706
|
+
process.stdout.clearLine(0);
|
|
707
|
+
process.stdout.cursorTo(0);
|
|
708
|
+
process.stdout.write(fitAnimatedModuleLine(line, process.stdout.columns));
|
|
709
|
+
}
|
|
710
|
+
function stopAnimatedModuleLine(clearCurrentLine = false) {
|
|
711
|
+
if (activeSpinner == null) return;
|
|
712
|
+
clearInterval(activeSpinner.interval);
|
|
713
|
+
activeSpinner = null;
|
|
714
|
+
if (clearCurrentLine && supportsAnimatedModuleOutput()) {
|
|
715
|
+
process.stdout.clearLine(0);
|
|
716
|
+
process.stdout.cursorTo(0);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
function startModuleSpinner(name, detail) {
|
|
720
|
+
if (!supportsAnimatedModuleOutput()) return;
|
|
721
|
+
clearPendingRecipeClosureGuides();
|
|
722
|
+
stopAnimatedModuleLine();
|
|
723
|
+
const maskedName = maskRegisteredSecrets(name);
|
|
724
|
+
const maskedDetail = detail == null ? void 0 : maskRegisteredSecrets(detail);
|
|
725
|
+
const displayModule = formatDisplayModule({
|
|
726
|
+
continuationIndentWidth: getContinuationIndent().length,
|
|
727
|
+
detail: maskedDetail,
|
|
728
|
+
name: maskedName,
|
|
729
|
+
status: "waiting",
|
|
730
|
+
terminalColumns: process.stdout.columns
|
|
731
|
+
});
|
|
732
|
+
const spinner = {
|
|
733
|
+
detail: displayModule.detail,
|
|
734
|
+
frameIndex: 0,
|
|
735
|
+
interval: setInterval(() => {
|
|
736
|
+
spinner.frameIndex = (spinner.frameIndex + 1) % SPINNER_FRAMES.length;
|
|
737
|
+
writeAnimatedModuleLine(
|
|
738
|
+
renderModuleLine({
|
|
739
|
+
detail: spinner.detail,
|
|
740
|
+
name: displayModule.name,
|
|
741
|
+
status: "waiting",
|
|
742
|
+
waitingFrame: SPINNER_FRAMES[spinner.frameIndex]
|
|
743
|
+
})
|
|
744
|
+
);
|
|
745
|
+
}, SPINNER_FRAME_INTERVAL_MS)
|
|
746
|
+
};
|
|
747
|
+
if (typeof spinner.interval.unref === "function") spinner.interval.unref();
|
|
748
|
+
activeSpinner = spinner;
|
|
749
|
+
writeAnimatedModuleLine(
|
|
750
|
+
renderModuleLine({
|
|
751
|
+
detail: displayModule.detail,
|
|
752
|
+
name: displayModule.name,
|
|
753
|
+
status: "waiting",
|
|
754
|
+
waitingFrame: SPINNER_FRAMES[0]
|
|
755
|
+
})
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
function printRecipeHeader(name) {
|
|
759
|
+
stopAnimatedModuleLine(true);
|
|
760
|
+
clearPendingRecipeClosureGuides();
|
|
761
|
+
const header = pc.bold(pc.blue(`[${name}]`));
|
|
762
|
+
console.log(`${buildGuideIndent(getRecipeHeaderIndent())}${header}`);
|
|
763
|
+
if (recipeOutputDepth >= 0) {
|
|
764
|
+
activeRecipeGuideDepths = [...activeRecipeGuideDepths, recipeOutputDepth];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
function colorizeDiffLine(line) {
|
|
768
|
+
if (line.startsWith("---") || line.startsWith("+++") || line.startsWith("@@")) {
|
|
769
|
+
return pc.dim(line);
|
|
770
|
+
}
|
|
771
|
+
if (line.startsWith("-")) return pc.red(line);
|
|
772
|
+
if (line.startsWith("+")) return pc.green(line);
|
|
773
|
+
return pc.dim(line);
|
|
774
|
+
}
|
|
775
|
+
function renderDiffLines(diff) {
|
|
776
|
+
if (diff.trim() === "") return [];
|
|
777
|
+
const sanitized = sanitizeTerminalText(maskRegisteredSecrets(diff));
|
|
778
|
+
return sanitized.split("\n").map((line) => `\u2502 ${colorizeDiffLine(line)}`);
|
|
779
|
+
}
|
|
780
|
+
function writeContinuationLine(line, extraGuideDepths, via) {
|
|
781
|
+
const composed = `${buildGuideIndent(getContinuationIndent(), { extraGuideDepths })}${line}`;
|
|
782
|
+
if (via === "stdout") {
|
|
783
|
+
process.stdout.write(`${composed}
|
|
784
|
+
`);
|
|
785
|
+
} else {
|
|
786
|
+
console.log(composed);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function writeContinuationBlock(parameters) {
|
|
790
|
+
for (const detailLine of parameters.detailLines) {
|
|
791
|
+
writeContinuationLine(pc.dim(detailLine), parameters.extraGuideDepths, parameters.via);
|
|
792
|
+
}
|
|
793
|
+
for (const diffLine of parameters.diffLines) {
|
|
794
|
+
writeContinuationLine(diffLine, parameters.extraGuideDepths, parameters.via);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
function printRenderedModuleResult(parameters) {
|
|
798
|
+
const extraGuideDepths = parameters.extraGuideDepths ?? [];
|
|
799
|
+
const maskedName = maskRegisteredSecrets(parameters.name);
|
|
800
|
+
const maskedDetail = parameters.detail == null ? void 0 : maskRegisteredSecrets(parameters.detail);
|
|
801
|
+
const displayModule = formatDisplayModule({
|
|
802
|
+
continuationIndentWidth: `${buildGuideIndent(
|
|
803
|
+
OUTPUT_INDENT_UNIT.repeat(Math.max(getCurrentOutputDepth() + 2, 1)),
|
|
804
|
+
{ extraGuideDepths }
|
|
805
|
+
)} `.length,
|
|
806
|
+
detail: maskedDetail,
|
|
807
|
+
name: maskedName,
|
|
808
|
+
status: parameters.status,
|
|
809
|
+
terminalColumns: process.stdout.columns
|
|
810
|
+
});
|
|
811
|
+
const line = renderModuleLine({
|
|
812
|
+
detail: displayModule.detail,
|
|
813
|
+
extraGuideDepths,
|
|
814
|
+
name: displayModule.name,
|
|
815
|
+
status: parameters.status
|
|
816
|
+
});
|
|
817
|
+
const diffLines = parameters.diff == null ? [] : renderDiffLines(parameters.diff);
|
|
818
|
+
const usesSpinner = supportsAnimatedModuleOutput() && activeSpinner != null;
|
|
819
|
+
if (usesSpinner) {
|
|
820
|
+
stopAnimatedModuleLine();
|
|
821
|
+
writeAnimatedModuleLine(line);
|
|
822
|
+
process.stdout.write("\n");
|
|
823
|
+
} else {
|
|
824
|
+
console.log(line);
|
|
825
|
+
}
|
|
826
|
+
writeContinuationBlock({
|
|
827
|
+
detailLines: displayModule.detailLines,
|
|
828
|
+
diffLines,
|
|
829
|
+
extraGuideDepths,
|
|
830
|
+
via: usesSpinner ? "stdout" : "console"
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
function printModuleResult(name, status, detail, diff) {
|
|
834
|
+
clearPendingRecipeClosureGuides();
|
|
835
|
+
printRenderedModuleResult({ detail, diff, name, status });
|
|
836
|
+
}
|
|
837
|
+
function printRecipeModuleResult(name, status, detail, diff) {
|
|
838
|
+
printRenderedModuleResult({
|
|
839
|
+
detail,
|
|
840
|
+
diff,
|
|
841
|
+
extraGuideDepths: pendingRecipeClosureGuideDepths,
|
|
842
|
+
name,
|
|
843
|
+
status
|
|
844
|
+
});
|
|
845
|
+
clearPendingRecipeClosureGuides();
|
|
846
|
+
}
|
|
847
|
+
function printCommandError(stdout, stderr) {
|
|
848
|
+
const maskedStdout = sanitizeTerminalText(maskRegisteredSecrets(stdout));
|
|
849
|
+
const maskedStderr = sanitizeTerminalText(maskRegisteredSecrets(stderr));
|
|
850
|
+
const lines = [];
|
|
851
|
+
if (maskedStderr.trim()) {
|
|
852
|
+
lines.push(...maskedStderr.trim().split("\n"));
|
|
853
|
+
}
|
|
854
|
+
if (maskedStdout.trim()) {
|
|
855
|
+
lines.push(...maskedStdout.trim().split("\n"));
|
|
856
|
+
}
|
|
857
|
+
if (lines.length > 0) {
|
|
858
|
+
console.error(pc.red(`${getErrorIndent()}Error output:`));
|
|
859
|
+
for (const line of lines) {
|
|
860
|
+
console.error(pc.red(`${getErrorIndent()}${line}`));
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
function printVerboseCommandError(stdout, stderr) {
|
|
865
|
+
const maskedStdout = sanitizeTerminalText(maskRegisteredSecrets(stdout));
|
|
866
|
+
const maskedStderr = sanitizeTerminalText(maskRegisteredSecrets(stderr));
|
|
867
|
+
if (maskedStderr.trim()) {
|
|
868
|
+
console.error(pc.red(`${getErrorIndent()}Full stderr:`));
|
|
869
|
+
for (const line of maskedStderr.trim().split("\n")) {
|
|
870
|
+
console.error(pc.red(`${getErrorIndent()}${line}`));
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (maskedStdout.trim()) {
|
|
874
|
+
console.error(pc.red(`${getErrorIndent()}Full stdout:`));
|
|
875
|
+
for (const line of maskedStdout.trim().split("\n")) {
|
|
876
|
+
console.error(pc.red(`${getErrorIndent()}${line}`));
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
function printVerboseErrorBlock(label, content) {
|
|
881
|
+
if (!content.trim()) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
console.error(pc.red(`${getErrorIndent()}${label}`));
|
|
885
|
+
for (const line of content.trim().split("\n")) {
|
|
886
|
+
console.error(pc.red(`${getErrorIndent()}${line}`));
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function getErrorCause(error) {
|
|
890
|
+
return error.cause;
|
|
891
|
+
}
|
|
892
|
+
function printVerboseErrorCause(cause, depth, visitedCauses) {
|
|
893
|
+
const label = `Cause ${depth}:`;
|
|
894
|
+
if (cause instanceof Error) {
|
|
895
|
+
if (visitedCauses.has(cause)) {
|
|
896
|
+
printVerboseErrorBlock(label, "<cycle detected>");
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
visitedCauses.add(cause);
|
|
900
|
+
const stack = cause.stack?.trim() ?? "";
|
|
901
|
+
const stackOrMessage = stack.length > 0 ? stack : String(cause);
|
|
902
|
+
printVerboseErrorBlock(label, maskRegisteredSecrets(stackOrMessage));
|
|
903
|
+
const nestedCause = getErrorCause(cause);
|
|
904
|
+
if (nestedCause !== void 0) {
|
|
905
|
+
printVerboseErrorCause(nestedCause, depth + 1, visitedCauses);
|
|
906
|
+
}
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
printVerboseErrorBlock(label, maskRegisteredSecrets(formatCauseValue(cause)));
|
|
910
|
+
}
|
|
911
|
+
function printVerboseGenericError(error) {
|
|
912
|
+
const stack = error.stack?.trim() ?? "";
|
|
913
|
+
const stackOrMessage = stack.length > 0 ? stack : String(error);
|
|
914
|
+
printVerboseErrorBlock("Full stack:", maskRegisteredSecrets(stackOrMessage));
|
|
915
|
+
const cause = getErrorCause(error);
|
|
916
|
+
if (cause !== void 0) {
|
|
917
|
+
const visitedCauses = /* @__PURE__ */ new WeakSet();
|
|
918
|
+
visitedCauses.add(error);
|
|
919
|
+
printVerboseErrorCause(cause, 1, visitedCauses);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
function formatCauseValue(cause) {
|
|
923
|
+
if (cause instanceof Error) return cause.message;
|
|
924
|
+
return inspectRedactedDiagnosticValue(cause, {
|
|
925
|
+
depth: CAUSE_INSPECT_DEPTH,
|
|
926
|
+
maxStringLength: CAUSE_INSPECT_MAX_STRING_LENGTH,
|
|
927
|
+
redactMaxDepth: CAUSE_REDACT_BINARY_MAX_DEPTH
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
function printCauseChain(error) {
|
|
931
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
932
|
+
visited.add(error);
|
|
933
|
+
let cause = getErrorCause(error);
|
|
934
|
+
while (cause !== void 0) {
|
|
935
|
+
if (cause instanceof Error) {
|
|
936
|
+
if (visited.has(cause)) return;
|
|
937
|
+
visited.add(cause);
|
|
938
|
+
}
|
|
939
|
+
console.error(pc.red(`${getErrorIndent()}Cause: ${maskRegisteredSecrets(formatCauseValue(cause))}`));
|
|
940
|
+
if (cause instanceof CommandError) {
|
|
941
|
+
printVerboseCommandError(
|
|
942
|
+
maskRegisteredSecrets(cause.fullStdout),
|
|
943
|
+
maskRegisteredSecrets(cause.fullStderr)
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
cause = cause instanceof Error ? getErrorCause(cause) : void 0;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
function printCommandFailure(error, verbose) {
|
|
950
|
+
if (verbose && error instanceof CommandError) {
|
|
951
|
+
const summaryLine = error.message.split("\n")[0];
|
|
952
|
+
printCommandError("", maskRegisteredSecrets(summaryLine));
|
|
953
|
+
printVerboseCommandError(
|
|
954
|
+
maskRegisteredSecrets(error.fullStdout),
|
|
955
|
+
maskRegisteredSecrets(error.fullStderr)
|
|
956
|
+
);
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
printCommandError("", maskRegisteredSecrets(String(error)));
|
|
960
|
+
if (error instanceof Error) {
|
|
961
|
+
if (!(error instanceof CommandError)) {
|
|
962
|
+
printCauseChain(error);
|
|
963
|
+
}
|
|
964
|
+
if (verbose) {
|
|
965
|
+
printVerboseGenericError(error);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// src/dryRunRecipe.ts
|
|
971
|
+
function interruptedDryRunResult(parameters) {
|
|
972
|
+
return {
|
|
973
|
+
env: parameters.currentEnvironment,
|
|
974
|
+
meta: parameters.aggregatedMeta.length === 0 ? void 0 : parameters.aggregatedMeta,
|
|
975
|
+
shouldBreak: true,
|
|
976
|
+
status: parameters.aggregatedStatus === "changed" ? "changed" : void 0
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
async function executeDryRunBlockingModule(parameters) {
|
|
980
|
+
const { childModule, connection, diff, environment, verbose } = parameters;
|
|
981
|
+
if (parameters.shutdownSignal() != null) return { env: environment, shouldBreak: true };
|
|
982
|
+
startModuleSpinner(childModule.name);
|
|
983
|
+
const result = childModule._applyDryRun == null ? await childModule.apply(connection, environment) : await childModule._applyDryRun(connection, environment, {
|
|
984
|
+
shutdownSignal: parameters.shutdownSignal
|
|
985
|
+
});
|
|
986
|
+
const nextEnvironment = result.meta == null ? environment : await mergeEnvironmentFromMeta(environment, result.meta);
|
|
987
|
+
const diffOutput = diff ? result.diff : void 0;
|
|
988
|
+
printModuleResult(
|
|
989
|
+
childModule.name,
|
|
990
|
+
result.status,
|
|
991
|
+
result._dryRunDetail ?? "(dry-run)",
|
|
992
|
+
diffOutput
|
|
993
|
+
);
|
|
994
|
+
if (result.status === "failed" && result.error != null) {
|
|
995
|
+
printCommandFailure(result.error, verbose);
|
|
996
|
+
}
|
|
997
|
+
return {
|
|
998
|
+
env: nextEnvironment,
|
|
999
|
+
meta: result.meta,
|
|
1000
|
+
shouldBreak: result.status === "failed" || result._stopRun === true,
|
|
1001
|
+
status: result.status,
|
|
1002
|
+
stopRun: result._stopRun
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
async function executeDryRunChildModule(parameters) {
|
|
1006
|
+
const { childModule, diff, environment, ssh: ssh2, verbose } = parameters;
|
|
1007
|
+
if (parameters.shutdownSignal() != null) return { env: environment, shouldBreak: true };
|
|
1008
|
+
const connection = childModule.local === true ? null : ssh2;
|
|
1009
|
+
startModuleSpinner(childModule.name);
|
|
1010
|
+
const checkResult = await childModule.check(connection, environment);
|
|
1011
|
+
if (parameters.shutdownSignal() != null) return { env: environment, shouldBreak: true };
|
|
1012
|
+
if (checkResult !== "ok" && shouldExecuteApplyDuringDryRun(childModule, diff)) {
|
|
1013
|
+
return executeDryRunBlockingModule({
|
|
1014
|
+
childModule,
|
|
1015
|
+
connection,
|
|
1016
|
+
diff,
|
|
1017
|
+
environment,
|
|
1018
|
+
shutdownSignal: parameters.shutdownSignal,
|
|
1019
|
+
verbose
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
const status = checkResult === "ok" ? "ok" : "changed";
|
|
1023
|
+
const suffix = checkResult === "ok" ? void 0 : "(dry-run)";
|
|
1024
|
+
printModuleResult(childModule.name, status, suffix);
|
|
1025
|
+
return { env: environment, shouldBreak: false, status };
|
|
1026
|
+
}
|
|
1027
|
+
function applyDryRunChildResult(accumulator, result) {
|
|
1028
|
+
return {
|
|
1029
|
+
aggregatedMeta: result.meta == null ? accumulator.aggregatedMeta : [...accumulator.aggregatedMeta, ...result.meta],
|
|
1030
|
+
aggregatedStatus: result.status === "changed" ? "changed" : accumulator.aggregatedStatus,
|
|
1031
|
+
currentEnvironment: result.env
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
async function runDryRunChildLoop(parameters) {
|
|
1035
|
+
let accumulator = parameters.accumulator;
|
|
1036
|
+
for (const childModule of parameters.recipeModule._modules) {
|
|
1037
|
+
if (parameters.shutdownSignal() != null) {
|
|
1038
|
+
return interruptedDryRunResult({
|
|
1039
|
+
aggregatedMeta: accumulator.aggregatedMeta,
|
|
1040
|
+
aggregatedStatus: accumulator.aggregatedStatus,
|
|
1041
|
+
currentEnvironment: accumulator.currentEnvironment
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
const result = await executeDryRunChildModule({
|
|
1045
|
+
childModule,
|
|
1046
|
+
diff: parameters.diff,
|
|
1047
|
+
environment: accumulator.currentEnvironment,
|
|
1048
|
+
shutdownSignal: parameters.shutdownSignal,
|
|
1049
|
+
ssh: parameters.ssh,
|
|
1050
|
+
verbose: parameters.verbose
|
|
1051
|
+
});
|
|
1052
|
+
if (result.shouldBreak) return result;
|
|
1053
|
+
accumulator = applyDryRunChildResult(accumulator, result);
|
|
1054
|
+
}
|
|
1055
|
+
return accumulator;
|
|
1056
|
+
}
|
|
1057
|
+
async function dryRunRecipeModule(parameters) {
|
|
1058
|
+
return withRecipeOutputScope(async () => {
|
|
1059
|
+
const { environment, recipeModule, ssh: ssh2 } = parameters;
|
|
1060
|
+
printRecipeHeader(recipeModule.name);
|
|
1061
|
+
const shutdownSignal = parameters.shutdownSignal ?? (() => null);
|
|
1062
|
+
const loopResult = await runDryRunChildLoop({
|
|
1063
|
+
accumulator: {
|
|
1064
|
+
aggregatedMeta: [],
|
|
1065
|
+
aggregatedStatus: "ok",
|
|
1066
|
+
currentEnvironment: environment
|
|
1067
|
+
},
|
|
1068
|
+
diff: parameters.options?.diff ?? false,
|
|
1069
|
+
recipeModule,
|
|
1070
|
+
shutdownSignal,
|
|
1071
|
+
ssh: ssh2,
|
|
1072
|
+
verbose: parameters.options?.verbose ?? false
|
|
1073
|
+
});
|
|
1074
|
+
if ("shouldBreak" in loopResult) return loopResult;
|
|
1075
|
+
return {
|
|
1076
|
+
env: loopResult.currentEnvironment,
|
|
1077
|
+
meta: loopResult.aggregatedMeta.length === 0 ? void 0 : loopResult.aggregatedMeta,
|
|
1078
|
+
shouldBreak: false,
|
|
1079
|
+
status: loopResult.aggregatedStatus
|
|
1080
|
+
};
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// src/signalOrchestration.ts
|
|
1085
|
+
function handleSignalResult(parameters) {
|
|
1086
|
+
const { hooks, result, signalName, verbose } = parameters;
|
|
1087
|
+
printModuleResult(`signal: ${signalName}`, result.status);
|
|
1088
|
+
if (result.status === "failed" && result.error != null) {
|
|
1089
|
+
printCommandFailure(result.error, verbose);
|
|
1090
|
+
}
|
|
1091
|
+
hooks?.onSignalFinished?.(result.status);
|
|
1092
|
+
return result.status === "failed" ? "failed" : "changed";
|
|
1093
|
+
}
|
|
1094
|
+
function handleSignalFailure(parameters) {
|
|
1095
|
+
const { error, hooks, signalName, verbose } = parameters;
|
|
1096
|
+
printModuleResult(`signal: ${signalName}`, "failed");
|
|
1097
|
+
printCommandFailure(error, verbose);
|
|
1098
|
+
hooks?.onSignalFinished?.("failed");
|
|
1099
|
+
return "failed";
|
|
1100
|
+
}
|
|
1101
|
+
async function applySignalMeta(parameters) {
|
|
1102
|
+
assertValidModuleMetaEntries(parameters.result.meta);
|
|
1103
|
+
const nextEnvironment = parameters.result.status === "failed" ? parameters.currentEnvironment : await mergeEnvironmentFromMeta(parameters.currentEnvironment, parameters.result.meta);
|
|
1104
|
+
await parameters.onSignalStep?.({
|
|
1105
|
+
env: nextEnvironment,
|
|
1106
|
+
meta: parameters.result.meta,
|
|
1107
|
+
status: parameters.result.status
|
|
1108
|
+
});
|
|
1109
|
+
return nextEnvironment;
|
|
1110
|
+
}
|
|
1111
|
+
async function runOneSignal(parameters) {
|
|
1112
|
+
const connection = parameters.signal.local === true ? null : parameters.ssh;
|
|
1113
|
+
startModuleSpinner(`signal: ${parameters.signal.name}`);
|
|
1114
|
+
const result = parameters.shutdownSignal == null ? await parameters.signal.apply(connection, parameters.currentEnvironment) : await parameters.signal.apply(connection, parameters.currentEnvironment, {
|
|
1115
|
+
shutdownSignal: parameters.shutdownSignal
|
|
1116
|
+
});
|
|
1117
|
+
const nextEnvironment = await applySignalMeta({
|
|
1118
|
+
currentEnvironment: parameters.currentEnvironment,
|
|
1119
|
+
onSignalStep: parameters.onSignalStep,
|
|
1120
|
+
result
|
|
1121
|
+
});
|
|
1122
|
+
return {
|
|
1123
|
+
nextEnvironment,
|
|
1124
|
+
status: handleSignalResult({
|
|
1125
|
+
hooks: parameters.hooks,
|
|
1126
|
+
result,
|
|
1127
|
+
signalName: parameters.signal.name,
|
|
1128
|
+
verbose: parameters.verbose
|
|
1129
|
+
})
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
async function runSignalModules(parameters) {
|
|
1133
|
+
const getShutdownSignal = parameters.shutdownSignal ?? (() => null);
|
|
1134
|
+
const verbose = parameters.verbose ?? false;
|
|
1135
|
+
let currentEnvironment = parameters.environment;
|
|
1136
|
+
let status = "changed";
|
|
1137
|
+
for (const signal of parameters.signals) {
|
|
1138
|
+
if (getShutdownSignal() != null) break;
|
|
1139
|
+
parameters.hooks?.onSignalStarted?.();
|
|
1140
|
+
try {
|
|
1141
|
+
const signalStep = await runOneSignal({
|
|
1142
|
+
currentEnvironment,
|
|
1143
|
+
hooks: parameters.hooks,
|
|
1144
|
+
onSignalStep: parameters.onSignalStep,
|
|
1145
|
+
shutdownSignal: parameters.shutdownSignal,
|
|
1146
|
+
signal,
|
|
1147
|
+
ssh: parameters.ssh,
|
|
1148
|
+
verbose
|
|
1149
|
+
});
|
|
1150
|
+
currentEnvironment = signalStep.nextEnvironment;
|
|
1151
|
+
if (signalStep.status === "failed") status = "failed";
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
status = handleSignalFailure({
|
|
1154
|
+
error,
|
|
1155
|
+
hooks: parameters.hooks,
|
|
1156
|
+
signalName: signal.name,
|
|
1157
|
+
verbose
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return status;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
380
1164
|
// src/recipe.ts
|
|
381
1165
|
var INTERRUPTED_BEFORE_APPLY = /* @__PURE__ */ Symbol("recipe-interrupted-before-apply");
|
|
382
1166
|
function isRecipeModuleLike(module) {
|
|
@@ -422,12 +1206,21 @@ async function executeOneModule(parameters) {
|
|
|
422
1206
|
if ((parameters.shutdownSignal?.() ?? null) != null) {
|
|
423
1207
|
return INTERRUPTED_BEFORE_APPLY;
|
|
424
1208
|
}
|
|
425
|
-
const result = await
|
|
1209
|
+
const result = await applyRecipeChild({
|
|
1210
|
+
connection,
|
|
1211
|
+
currentEnvironment,
|
|
1212
|
+
onChildStep: parameters.onChildStep,
|
|
1213
|
+
onSignalStep: parameters.onSignalStep,
|
|
1214
|
+
shutdownSignal: parameters.shutdownSignal,
|
|
1215
|
+
signalHooks: parameters.signalHooks,
|
|
1216
|
+
targetModule,
|
|
1217
|
+
verbose
|
|
1218
|
+
});
|
|
426
1219
|
printRecipeChildResult(targetModule, result);
|
|
427
1220
|
if (result.status === "failed" && result.error != null) {
|
|
428
1221
|
printCommandFailure(result.error, verbose);
|
|
429
1222
|
}
|
|
430
|
-
const environment = await mergeEnvironmentFromMeta(currentEnvironment, result.meta);
|
|
1223
|
+
const environment = result.status === "failed" ? currentEnvironment : await mergeEnvironmentFromMeta(currentEnvironment, result.meta);
|
|
431
1224
|
return {
|
|
432
1225
|
_flushSignals: result._flushSignals,
|
|
433
1226
|
_stopRun: result._stopRun,
|
|
@@ -440,6 +1233,24 @@ async function checkRecipeChild(targetModule, connection, currentEnvironment) {
|
|
|
440
1233
|
startModuleSpinner(targetModule.name);
|
|
441
1234
|
return targetModule.check(connection, currentEnvironment);
|
|
442
1235
|
}
|
|
1236
|
+
async function applyRecipeChild(parameters) {
|
|
1237
|
+
if (isRecipeModuleLike(parameters.targetModule)) {
|
|
1238
|
+
return parameters.targetModule.apply(parameters.connection, parameters.currentEnvironment, {
|
|
1239
|
+
onChildStep: parameters.onChildStep,
|
|
1240
|
+
onSignalStep: parameters.onSignalStep,
|
|
1241
|
+
shutdownSignal: parameters.shutdownSignal,
|
|
1242
|
+
signalHooks: parameters.signalHooks,
|
|
1243
|
+
verbose: parameters.verbose
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
if (parameters.targetModule._supportsChildStepHook === true && (parameters.onChildStep != null || parameters.shutdownSignal != null)) {
|
|
1247
|
+
return parameters.targetModule.apply(parameters.connection, parameters.currentEnvironment, {
|
|
1248
|
+
onChildStep: parameters.onChildStep,
|
|
1249
|
+
shutdownSignal: parameters.shutdownSignal
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
return parameters.targetModule.apply(parameters.connection, parameters.currentEnvironment);
|
|
1253
|
+
}
|
|
443
1254
|
async function applyExecutedRecipeStep(parameters) {
|
|
444
1255
|
if (parameters.step == null) return parameters.state;
|
|
445
1256
|
if (parameters.step === INTERRUPTED_BEFORE_APPLY) return null;
|
|
@@ -519,7 +1330,13 @@ async function executeModules(modules, ssh2, parameters) {
|
|
|
519
1330
|
const shutdownSignal = parameters.shutdownSignal ?? (() => null);
|
|
520
1331
|
const verbose = parameters.verbose ?? false;
|
|
521
1332
|
let state = {
|
|
522
|
-
|
|
1333
|
+
// R-0000079: preserve the null-prototype guarantee that
|
|
1334
|
+
// R-0000069/R-0000070/R-0000074 establish for the runner-level
|
|
1335
|
+
// environment. Plain object spread (`{ ...environment }`) would create a
|
|
1336
|
+
// map with `Object.prototype`, exposing the first child module of every
|
|
1337
|
+
// recipe to a polluted-fähige environment. Mirror the meta.ts:214
|
|
1338
|
+
// approach.
|
|
1339
|
+
env: Object.assign(createNullPrototypeEnvironment(), parameters.environment),
|
|
523
1340
|
meta: void 0,
|
|
524
1341
|
signalsPending: false,
|
|
525
1342
|
status: "ok"
|
|
@@ -528,7 +1345,10 @@ async function executeModules(modules, ssh2, parameters) {
|
|
|
528
1345
|
if (shutdownSignal() != null) break;
|
|
529
1346
|
const step = await executeRecipeChildStep({
|
|
530
1347
|
currentEnvironment: state.env,
|
|
1348
|
+
onChildStep,
|
|
1349
|
+
onSignalStep: parameters.onSignalStep,
|
|
531
1350
|
shutdownSignal,
|
|
1351
|
+
signalHooks: parameters.signalHooks,
|
|
532
1352
|
ssh: ssh2,
|
|
533
1353
|
targetModule: currentModule,
|
|
534
1354
|
verbose
|
|
@@ -609,11 +1429,51 @@ async function applyRecipe(parameters) {
|
|
|
609
1429
|
function shouldRunRecipeSignalsAtEnd(state, signals2) {
|
|
610
1430
|
return state.signalsPending && state.status === "changed" && signals2 != null;
|
|
611
1431
|
}
|
|
1432
|
+
function shouldExecuteRecipeDryRun(module) {
|
|
1433
|
+
return module._applyDryRun != null || module._dryRunBlocker === true || module._dryRunMetaProducer === true;
|
|
1434
|
+
}
|
|
1435
|
+
function recipeNeedsDryRunApply(modules) {
|
|
1436
|
+
return modules.some((module) => shouldExecuteRecipeDryRun(module));
|
|
1437
|
+
}
|
|
1438
|
+
function createRecipeDryRunApply(name, modules, needsDryRunApply) {
|
|
1439
|
+
if (!needsDryRunApply) return void 0;
|
|
1440
|
+
return async (ssh2, environment, parameters) => {
|
|
1441
|
+
const result = await dryRunRecipeModule({
|
|
1442
|
+
environment,
|
|
1443
|
+
recipeModule: {
|
|
1444
|
+
_isRecipe: true,
|
|
1445
|
+
_modules: modules,
|
|
1446
|
+
async apply() {
|
|
1447
|
+
await Promise.resolve();
|
|
1448
|
+
return { status: "ok" };
|
|
1449
|
+
},
|
|
1450
|
+
async check() {
|
|
1451
|
+
await Promise.resolve();
|
|
1452
|
+
return "ok";
|
|
1453
|
+
},
|
|
1454
|
+
name
|
|
1455
|
+
},
|
|
1456
|
+
shutdownSignal: parameters?.shutdownSignal,
|
|
1457
|
+
ssh: ssh2
|
|
1458
|
+
});
|
|
1459
|
+
return {
|
|
1460
|
+
_stopRun: result.stopRun,
|
|
1461
|
+
meta: result.meta,
|
|
1462
|
+
status: result.status ?? "ok"
|
|
1463
|
+
};
|
|
1464
|
+
};
|
|
1465
|
+
}
|
|
612
1466
|
function recipe(name, modules, options) {
|
|
1467
|
+
const needsDryRunApply = recipeNeedsDryRunApply(modules);
|
|
1468
|
+
const applyDryRun = createRecipeDryRunApply(name, modules, needsDryRunApply);
|
|
613
1469
|
return {
|
|
1470
|
+
...modules.some((module) => module._dryRunBlocker === true) ? { _dryRunBlocker: true } : {},
|
|
1471
|
+
...modules.some((module) => module._dryRunMetaProducer === true) ? { _dryRunMetaProducer: true } : {},
|
|
1472
|
+
...applyDryRun == null ? {} : { _applyDryRun: applyDryRun },
|
|
614
1473
|
_isRecipe: true,
|
|
615
1474
|
_modules: modules,
|
|
616
1475
|
_signals: options?.signals,
|
|
1476
|
+
_supportsChildStepHook: true,
|
|
617
1477
|
async apply(ssh2, environment, parameters) {
|
|
618
1478
|
return applyRecipe({
|
|
619
1479
|
environment,
|
|
@@ -626,6 +1486,7 @@ function recipe(name, modules, options) {
|
|
|
626
1486
|
},
|
|
627
1487
|
async check(ssh2, environment) {
|
|
628
1488
|
for (const childModule of modules) {
|
|
1489
|
+
if (getRunnerAbortSignal()?.aborted === true) return NEEDS_APPLY;
|
|
629
1490
|
const connection = childModule.local === true ? null : ssh2;
|
|
630
1491
|
let result;
|
|
631
1492
|
try {
|
|
@@ -642,17 +1503,44 @@ function recipe(name, modules, options) {
|
|
|
642
1503
|
}
|
|
643
1504
|
|
|
644
1505
|
// src/server.ts
|
|
645
|
-
function
|
|
646
|
-
if (
|
|
1506
|
+
function isModuleLike(value) {
|
|
1507
|
+
if (value === null || typeof value !== "object") return false;
|
|
1508
|
+
return "apply" in value && "check" in value && "name" in value && typeof value.name === "string" && value.name.length > 0 && typeof value.check === "function" && typeof value.apply === "function";
|
|
1509
|
+
}
|
|
1510
|
+
function validateModuleList(modules, property, options) {
|
|
1511
|
+
if (!Array.isArray(modules)) {
|
|
1512
|
+
throw new TypeError(`ServerDefinition: ${property} must be an array of modules`);
|
|
1513
|
+
}
|
|
1514
|
+
if (options?.requireNonEmpty === true && modules.length === 0) {
|
|
1515
|
+
throw new Error("ServerDefinition: run must contain at least one module");
|
|
1516
|
+
}
|
|
1517
|
+
for (const [index, module] of modules.entries()) {
|
|
1518
|
+
if (!isModuleLike(module)) {
|
|
1519
|
+
throw new Error(
|
|
1520
|
+
`ServerDefinition: ${property}[${index}] must be a module with name, check, and apply`
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function validateServerDefinition(config, options) {
|
|
1526
|
+
const hostValidationFailure = validateHostLabel(config.host);
|
|
1527
|
+
if (hostValidationFailure === "empty") {
|
|
647
1528
|
throw new Error("ServerDefinition: host is required");
|
|
648
1529
|
}
|
|
1530
|
+
if (hostValidationFailure != null) {
|
|
1531
|
+
throw new Error(
|
|
1532
|
+
`ServerDefinition: host ${describeHostValidationFailure(hostValidationFailure)}`
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
649
1535
|
if (config.name.length === 0) {
|
|
650
1536
|
throw new Error("ServerDefinition: name is required");
|
|
651
1537
|
}
|
|
652
1538
|
validateSshConfig(config.ssh);
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
1539
|
+
validateModuleList(config.run, "run", { requireNonEmpty: options?.allowEmptyRun !== true });
|
|
1540
|
+
if (config.signals !== void 0) validateModuleList(config.signals, "signals");
|
|
1541
|
+
}
|
|
1542
|
+
function server(config) {
|
|
1543
|
+
validateServerDefinition(config);
|
|
656
1544
|
return config;
|
|
657
1545
|
}
|
|
658
1546
|
export {
|
|
@@ -680,6 +1568,7 @@ export {
|
|
|
680
1568
|
hostname,
|
|
681
1569
|
isBooleanEnvironmentMetaEntry,
|
|
682
1570
|
isEnvironmentMetaEntry,
|
|
1571
|
+
isFirstRun,
|
|
683
1572
|
isLazyEnvironmentMetaEntry,
|
|
684
1573
|
isNumberEnvironmentMetaEntry,
|
|
685
1574
|
isSshdPortMetaEntry,
|
|
@@ -696,6 +1585,7 @@ export {
|
|
|
696
1585
|
quadlet,
|
|
697
1586
|
recipe,
|
|
698
1587
|
releaseUpgrade,
|
|
1588
|
+
resolveEnvironment,
|
|
699
1589
|
rsync,
|
|
700
1590
|
script,
|
|
701
1591
|
server,
|
|
@@ -705,11 +1595,13 @@ export {
|
|
|
705
1595
|
ssh,
|
|
706
1596
|
sshd,
|
|
707
1597
|
sshdPortMeta,
|
|
1598
|
+
swap,
|
|
708
1599
|
sysctl,
|
|
709
1600
|
system,
|
|
710
1601
|
systemHostMeta,
|
|
711
1602
|
systemRebootMeta,
|
|
712
1603
|
systemd,
|
|
1604
|
+
timer,
|
|
713
1605
|
ufw,
|
|
714
1606
|
user,
|
|
715
1607
|
when
|