@outerlayer/cli 0.7.0 → 0.8.1
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/CHANGELOG.md +30 -0
- package/README.md +5 -5
- package/dist/{agent-setup-TXLDY5RV.js → agent-setup-K6RD2Z4P.js} +4 -4
- package/dist/{build-JF5UOWKJ.js → build-MS34XEGN.js} +2 -1
- package/dist/build-info.json +1 -1
- package/dist/builder-IG6FNA54.js +13 -0
- package/dist/{check-ASZL5BEO.js → check-GX5ND6RO.js} +26 -22
- package/dist/chunk-2BRSMBBB.js +264 -0
- package/dist/{chunk-PUN7LMNS.js → chunk-4ACZCY4I.js} +2 -2
- package/dist/{chunk-2VO25HHO.js → chunk-4FPWKV7L.js} +3 -3
- package/dist/{chunk-AII7CKTH.js → chunk-5ODHQP6O.js} +68 -145
- package/dist/chunk-7RQ73DLO.js +23 -0
- package/dist/{chunk-7FTNIAHS.js → chunk-AX23QKHS.js} +7 -3
- package/dist/{chunk-6ZZHWY2Z.js → chunk-BF4KKEFY.js} +4 -4
- package/dist/{chunk-5FWNEQNZ.js → chunk-BQFNZOMQ.js} +1 -1
- package/dist/chunk-CX22S5BA.js +45 -0
- package/dist/{chunk-D6FXM5OW.js → chunk-GAYWY4GI.js} +8 -8
- package/dist/{chunk-MQPRB5K7.js → chunk-HSN5QNS5.js} +8 -8
- package/dist/chunk-I2UOAV7X.js +133 -0
- package/dist/chunk-I7CJIRMM.js +93 -0
- package/dist/{chunk-SSU747O6.js → chunk-ILEA3YHM.js} +1 -1
- package/dist/{chunk-OUUINEYB.js → chunk-JKIWMRFH.js} +1 -1
- package/dist/{chunk-JPR2WHHF.js → chunk-KSLNQOOS.js} +1 -1
- package/dist/{chunk-DGAIVMMN.js → chunk-KV47VQJZ.js} +5 -5
- package/dist/{chunk-E4PIFIC3.js → chunk-M4ERCYDB.js} +133 -159
- package/dist/chunk-PTW6W5NR.js +127 -0
- package/dist/{chunk-DTXOJDEH.js → chunk-PWBILI5P.js} +3 -2
- package/dist/{chunk-X2FSFU4C.js → chunk-PXFEYBDE.js} +3 -3
- package/dist/{chunk-6IYLDODX.js → chunk-Q4432IZT.js} +5 -5
- package/dist/{chunk-AJKEHABI.js → chunk-QB5JQH5V.js} +255 -54
- package/dist/{chunk-AQ54MKSQ.js → chunk-QNRSFEGY.js} +2 -2
- package/dist/{chunk-774HEQL6.js → chunk-TEKZRTM3.js} +91 -8
- package/dist/{chunk-CEBIA5FD.js → chunk-TTNFGMHO.js} +42 -50
- package/dist/{chunk-F6GFZXZ3.js → chunk-UXYLWVTQ.js} +1 -18
- package/dist/{chunk-M5POMKKW.js → chunk-V7NIVOFD.js} +5 -5
- package/dist/{chunk-BCLJSQCV.js → chunk-YO4PSLVB.js} +1 -1
- package/dist/{chunk-6QWV3YDJ.js → chunk-Z2THOHPP.js} +2 -2
- package/dist/{cli-LEVCGAQV.js → cli-X5ONGYNY.js} +128 -78
- package/dist/{command-FLSYCGIB.js → command-7NFU3IPO.js} +38 -20
- package/dist/{config-I3TVFYLH.js → config-Z3USDBLU.js} +3 -2
- package/dist/{connect-cmd-MJMUONDY.js → connect-cmd-PNZ6IJLJ.js} +2 -2
- package/dist/{context-adopt-CE4KDXS3.js → context-adopt-D2Z7F7PB.js} +3 -3
- package/dist/{context-materialize-QL33HDJN.js → context-materialize-4PMTDS6K.js} +3 -3
- package/dist/{dist-PXBSA7QX.js → dist-3ZTZF2TP.js} +1 -1
- package/dist/docker-PRHRSJM5.js +15 -0
- package/dist/{doctor-FVDT3KJZ.js → doctor-KALLPCRB.js} +39 -16
- package/dist/{emit-artifact-cmd-7RPMRSVP.js → emit-artifact-cmd-D5GMVWNC.js} +7 -7
- package/dist/{emit-criteria-cmd-MKI7WETO.js → emit-criteria-cmd-IE7VIM64.js} +5 -5
- package/dist/{emit-finding-cmd-RMLGZASB.js → emit-finding-cmd-46PTPN3A.js} +5 -5
- package/dist/{emit-result-cmd-MTXAZ2GV.js → emit-result-cmd-KXHHP6HX.js} +7 -7
- package/dist/{hook-fast-XBOZ3N6L.js → hook-fast-5H5QBJKT.js} +1 -1
- package/dist/{host-key-KDYZ5ECC.js → host-key-YMLXY6QB.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/{init-VLW7H6RW.js → init-B4CECVOX.js} +25 -18
- package/dist/{init-BACXESVV.js → init-S4DH6PWW.js} +16 -13
- package/dist/{init-cmd-VJFB5L7X.js → init-cmd-P4MVCBCZ.js} +8 -8
- package/dist/{install-NMAPLLBQ.js → install-FHTVJZS6.js} +5 -4
- package/dist/{install-cmd-B5SEU3G7.js → install-cmd-BEC7XTNG.js} +3 -1
- package/dist/{loop-D3243JKT.js → loop-ODMRE3CW.js} +26 -22
- package/dist/{machine-EB3GEVI4.js → machine-J4SAB3Z2.js} +15 -12
- package/dist/{mcp-serve-cmd-N6UD2KBX.js → mcp-serve-cmd-CT47P4YQ.js} +2 -2
- package/dist/{session-sender-LMACEJJS.js → session-sender-V46VPWQ5.js} +6 -6
- package/dist/{status-F4THTJJK.js → status-QBKSH3QE.js} +19 -16
- package/dist/{sync-cmd-M6JA3OEE.js → sync-cmd-RJR77V4O.js} +5 -5
- package/dist/{watch-NB5TGPEZ.js → watch-YNZTFLEY.js} +5 -5
- package/dist/{work-claim-cmd-UHMWOS3G.js → work-claim-cmd-KAD3ZAH7.js} +10 -9
- package/dist/{work-cmd-6UMCFTAW.js → work-cmd-CNWPTH7W.js} +6 -6
- package/dist/{work-comment-cmd-E4LEUIXZ.js → work-comment-cmd-HH45S6UE.js} +6 -6
- package/dist/{work-open-pr-cmd-OPZWGSEQ.js → work-open-pr-cmd-5KR3KFCN.js} +7 -7
- package/dist/{work-pr-cmd-IDIAFFRU.js → work-pr-cmd-TTEGCP76.js} +6 -6
- package/package.json +2 -2
- package/skill-pack/maintained/amend/SKILL.md +22 -6
- package/dist/chunk-Q5G2ODUZ.js +0 -54
- package/dist/docker-NEGDLU6D.js +0 -7
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { currentLink } from './chunk-TI7B7UEO.js';
|
|
4
|
+
import { readFileSync, statSync, chmodSync, mkdirSync, writeFileSync, renameSync, rmSync, existsSync, lstatSync, accessSync, constants, realpathSync } from 'fs';
|
|
5
|
+
import { randomBytes } from 'crypto';
|
|
6
|
+
import { join, dirname, delimiter, resolve } from 'path';
|
|
7
|
+
|
|
8
|
+
createRequire(import.meta.url);
|
|
9
|
+
var LAUNCHER_MARKER = "# outerlayer-runner-launcher";
|
|
10
|
+
var LauncherWriteError = class extends Error {
|
|
11
|
+
};
|
|
12
|
+
var PATH_LINE = 'export PATH="$HOME/.local/bin:$PATH"';
|
|
13
|
+
var ADD_TO_PATH = `${PATH_LINE} (in ~/.profile, ~/.bashrc or your shell's startup file; the runner never edits those)`;
|
|
14
|
+
function launcherPath(home) {
|
|
15
|
+
return join(home, ".local", "bin", "outerlayer");
|
|
16
|
+
}
|
|
17
|
+
function quoted(text) {
|
|
18
|
+
return `'${text.replace(/'/g, `'\\''`)}'`;
|
|
19
|
+
}
|
|
20
|
+
function launcherText(configPath, nodePath) {
|
|
21
|
+
const entry = join(currentLink(configPath), "node_modules", "@outerlayer", "cli", "dist", "index.js");
|
|
22
|
+
return [
|
|
23
|
+
"#!/bin/sh",
|
|
24
|
+
LAUNCHER_MARKER,
|
|
25
|
+
"# Written by `outerlayer runner install`. Runs the CLI the runner's `current` link names, so a self-update changes",
|
|
26
|
+
"# what this runs without changing this file.",
|
|
27
|
+
`exec ${quoted(nodePath)} ${quoted(entry)} "$@"`,
|
|
28
|
+
""
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
31
|
+
function ownerOf(path) {
|
|
32
|
+
try {
|
|
33
|
+
lstatSync(path);
|
|
34
|
+
} catch {
|
|
35
|
+
return "absent";
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return readFileSync(path, "utf8").includes(LAUNCHER_MARKER) ? "ours" : "foreign";
|
|
39
|
+
} catch {
|
|
40
|
+
return "foreign";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function writeLauncher(input) {
|
|
44
|
+
const path = launcherPath(input.home);
|
|
45
|
+
const text = launcherText(input.configPath, input.nodePath);
|
|
46
|
+
const owner = ownerOf(path);
|
|
47
|
+
if (owner === "foreign" && input.replace !== true) return { path, state: "foreign" };
|
|
48
|
+
if (owner === "ours" && readFileSync(path, "utf8") === text) {
|
|
49
|
+
if ((statSync(path).mode & 493) !== 493) chmodSync(path, 493);
|
|
50
|
+
return { path, state: "unchanged" };
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
54
|
+
const temporary = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.new`;
|
|
55
|
+
try {
|
|
56
|
+
writeFileSync(temporary, text, { mode: 493, flag: "wx" });
|
|
57
|
+
chmodSync(temporary, 493);
|
|
58
|
+
renameSync(temporary, path);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
rmSync(temporary, { force: true });
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new LauncherWriteError(`cannot write the outerlayer command at ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
}
|
|
66
|
+
return { path, state: "written" };
|
|
67
|
+
}
|
|
68
|
+
function firstOnPath(PATH) {
|
|
69
|
+
for (const dir of (PATH ?? "").split(delimiter)) {
|
|
70
|
+
if (dir === "") continue;
|
|
71
|
+
const candidate = join(dir, "outerlayer");
|
|
72
|
+
try {
|
|
73
|
+
if (!statSync(candidate).isFile()) continue;
|
|
74
|
+
accessSync(candidate, constants.X_OK);
|
|
75
|
+
return candidate;
|
|
76
|
+
} catch {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
function sameFile(a, b) {
|
|
83
|
+
try {
|
|
84
|
+
return realpathSync(a) === realpathSync(b);
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function onPath(PATH, dir) {
|
|
90
|
+
return (PATH ?? "").split(delimiter).some((entry) => entry !== "" && resolve(entry) === resolve(dir));
|
|
91
|
+
}
|
|
92
|
+
function pathNotes(input) {
|
|
93
|
+
const notes = [];
|
|
94
|
+
if (input.state === "foreign") {
|
|
95
|
+
notes.push(`${input.launcher} exists and the runner did not write it, so it was left alone. Run \`outerlayer runner install --replace\` to replace it`);
|
|
96
|
+
return notes;
|
|
97
|
+
}
|
|
98
|
+
if (!onPath(input.PATH, dirname(input.launcher))) {
|
|
99
|
+
notes.push(`${dirname(input.launcher)} is not on PATH, so a shell will not find the launcher. Add it with: ${ADD_TO_PATH}`);
|
|
100
|
+
return notes;
|
|
101
|
+
}
|
|
102
|
+
const first = firstOnPath(input.PATH);
|
|
103
|
+
if (first !== void 0 && !sameFile(first, input.launcher)) {
|
|
104
|
+
notes.push(`${first} comes before ${input.launcher} on PATH, so a shell runs that one instead. Put the launcher's directory first with: ${ADD_TO_PATH}`);
|
|
105
|
+
}
|
|
106
|
+
return notes;
|
|
107
|
+
}
|
|
108
|
+
function launcherCheck(input) {
|
|
109
|
+
const name = "outerlayer on PATH";
|
|
110
|
+
const launcher = launcherPath(input.home);
|
|
111
|
+
if (!existsSync(launcher) || ownerOf(launcher) !== "ours") return void 0;
|
|
112
|
+
const first = firstOnPath(input.PATH);
|
|
113
|
+
if (first === void 0) {
|
|
114
|
+
return { name, status: "warn", detail: `no outerlayer is on PATH; the launcher is at ${launcher}`, fix: `add its directory to PATH: ${ADD_TO_PATH}` };
|
|
115
|
+
}
|
|
116
|
+
if (sameFile(first, launcher)) {
|
|
117
|
+
return { name, status: "pass", detail: `${launcher} is the first outerlayer on PATH, so it runs the version the runner runs` };
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
name,
|
|
121
|
+
status: "warn",
|
|
122
|
+
detail: `${first} comes before ${launcher} on PATH, so a shell runs that one, which may not be the version the runner runs`,
|
|
123
|
+
fix: `remove ${first}, or put the launcher's directory first: ${ADD_TO_PATH}`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { LauncherWriteError, launcherCheck, launcherPath, pathNotes, writeLauncher };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import {
|
|
3
|
+
import { MAX_ALLOW_HOSTS, allowHostProblem } from './chunk-UXYLWVTQ.js';
|
|
4
|
+
import { parseSize } from './chunk-7RQ73DLO.js';
|
|
4
5
|
import { isRefusedInConfig, resolveDestinations, isReservedVariable } from './chunk-OD2VTIHN.js';
|
|
5
6
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
6
7
|
import { existsSync, statSync, accessSync, constants, readFileSync } from 'fs';
|
|
@@ -358,4 +359,4 @@ function resolveBuild(configPath, block) {
|
|
|
358
359
|
};
|
|
359
360
|
}
|
|
360
361
|
|
|
361
|
-
export { DEFAULT_CHECK_TIME_LIMIT_MINUTES, RunnerConfigError, builtinKind, checkHooks, defaultRunnerConfigPath, hasRunnerBlock, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, loadRunnerConfig, resolveBuild, runnerVmOf };
|
|
362
|
+
export { DEFAULT_CHECK_TIME_LIMIT_MINUTES, RunnerConfigError, builtinKind, checkHooks, defaultRunnerConfigPath, hasRunnerBlock, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, loadRunnerConfig, resolveBuild, resolveHousekeeping, runnerVmOf };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { autoConnect } from './chunk-
|
|
3
|
+
import { autoConnect } from './chunk-4ACZCY4I.js';
|
|
4
4
|
import { detectActiveSession } from './chunk-BFESLKPP.js';
|
|
5
|
-
import { resolveRepoIdentity } from './chunk-
|
|
5
|
+
import { resolveRepoIdentity } from './chunk-GAYWY4GI.js';
|
|
6
6
|
import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
|
|
7
|
-
import { defaultFetch } from './chunk-
|
|
7
|
+
import { defaultFetch } from './chunk-YO4PSLVB.js';
|
|
8
8
|
import { unauthorizedMessage, resolveGatewayCredentials } from './chunk-BOLTI6LR.js';
|
|
9
9
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
10
10
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { isPlaceholderCommand } from './chunk-
|
|
4
|
-
import { describeCallFailure } from './chunk-
|
|
5
|
-
import { WorkCommandError, getJson } from './chunk-
|
|
3
|
+
import { isPlaceholderCommand } from './chunk-Z2THOHPP.js';
|
|
4
|
+
import { describeCallFailure } from './chunk-M4ERCYDB.js';
|
|
5
|
+
import { WorkCommandError, getJson } from './chunk-KV47VQJZ.js';
|
|
6
6
|
import { sanitizeTerminal } from './chunk-ZYHKJKSU.js';
|
|
7
|
-
import { builtinKind } from './chunk-
|
|
7
|
+
import { builtinKind } from './chunk-PWBILI5P.js';
|
|
8
8
|
import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-OD2VTIHN.js';
|
|
9
|
-
import { signerFor } from './chunk-
|
|
9
|
+
import { signerFor } from './chunk-V7NIVOFD.js';
|
|
10
10
|
|
|
11
11
|
createRequire(import.meta.url);
|
|
12
12
|
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { writeJobFile, readJobFile } from './chunk-T6OIJQRT.js';
|
|
4
|
-
import { senderPidfile } from './chunk-
|
|
4
|
+
import { senderPidfile } from './chunk-4FPWKV7L.js';
|
|
5
5
|
import { DOCKERD_LOG } from './chunk-WVGSB3Z2.js';
|
|
6
|
-
import { installScrubContext } from './chunk-
|
|
7
|
-
import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason,
|
|
8
|
-
import {
|
|
6
|
+
import { installScrubContext } from './chunk-BF4KKEFY.js';
|
|
7
|
+
import { AssetError, describeCallFailure, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createBuildDisk, machineConfig, refusalCode, resolveAgentUser, AgentUserError, runBuildSteps, removeStagedContext, stageGovernedContext, shellQuote, createRunnerLog, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, formatDuration, retrySend, formatClock, containerSenderCommand, syncInContainer, GH_CONFIG_YML, ghHostsYml, ADDED_USER, isLiteralAddress, signalInContainer, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, senderSignalScript } from './chunk-M4ERCYDB.js';
|
|
8
|
+
import { readBranchHead, fetchCommit } from './chunk-TTNFGMHO.js';
|
|
9
9
|
import { isSameProcess, startTimeOf } from './chunk-N5FOE5PS.js';
|
|
10
|
-
import { childEnvironment } from './chunk-
|
|
10
|
+
import { childEnvironment } from './chunk-TEKZRTM3.js';
|
|
11
|
+
import { CLAUDE_CODE_VERSION } from './chunk-CX22S5BA.js';
|
|
11
12
|
import { waitForAgent, vmExecArgs } from './chunk-ZOAEPWGL.js';
|
|
12
13
|
import { socketPathProblem, vsockLinks, linkVsockSockets } from './chunk-R7STSBLC.js';
|
|
13
|
-
import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-
|
|
14
|
+
import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-KV47VQJZ.js';
|
|
14
15
|
import { planArtifactUpload } from './chunk-ZYHKJKSU.js';
|
|
15
|
-
import { scrubText } from './chunk-
|
|
16
|
-
import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-
|
|
17
|
-
import { parseSize } from './chunk-
|
|
18
|
-
import { claudeCredential, buildEnvironment, JOB_MOUNT, CHECKOUT_DIR, parseIsolation, proxyEnvironment, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, withTunnelRecord, RELAY_PORT } from './chunk-OD2VTIHN.js';
|
|
16
|
+
import { scrubText } from './chunk-GAYWY4GI.js';
|
|
17
|
+
import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-PWBILI5P.js';
|
|
18
|
+
import { parseSize } from './chunk-7RQ73DLO.js';
|
|
19
|
+
import { claudeCredential, buildEnvironment, JOB_MOUNT, CHECKOUT_DIR, parseIsolation, proxyEnvironment, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, withTunnelRecord, RELAY_PORT, checkEnvironment } from './chunk-OD2VTIHN.js';
|
|
19
20
|
import { withHooksPath, authorEnvironment, commitIdentityRecord } from './chunk-HKMPVPO7.js';
|
|
20
21
|
import { parsePolicy } from './chunk-7ZR5UPMB.js';
|
|
21
22
|
import { readCloudConfig } from './chunk-EABW6AJQ.js';
|
|
22
23
|
import { resolveVersion } from './chunk-RA3O54FC.js';
|
|
23
|
-
import { signerFor } from './chunk-
|
|
24
|
+
import { signerFor } from './chunk-V7NIVOFD.js';
|
|
24
25
|
import { runnerDir, buildDir, vsockPath, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
25
26
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
26
27
|
import { spawn, spawnSync } from 'child_process';
|
|
@@ -42,6 +43,9 @@ var DOCKER_LOG_LINES = 20;
|
|
|
42
43
|
var GUEST_GH_DIR = `${JOB_MOUNT}/gh`;
|
|
43
44
|
var GUEST_HOOKS_DIR = `${JOB_MOUNT}/git-hooks`;
|
|
44
45
|
var GUEST_CONTEXT_DIR = `${JOB_MOUNT}/context`;
|
|
46
|
+
function buildMemoryBytes(config) {
|
|
47
|
+
return parseSize(config.build?.memory ?? DEFAULT_MEMORY) ?? parseSize(DEFAULT_MEMORY);
|
|
48
|
+
}
|
|
45
49
|
function firstLine(text2) {
|
|
46
50
|
return text2.trim().split("\n")[0] ?? "";
|
|
47
51
|
}
|
|
@@ -121,7 +125,9 @@ async function provisionMicroVm(input) {
|
|
|
121
125
|
repository,
|
|
122
126
|
imageId: report.imageId,
|
|
123
127
|
containerEnv: report.apply.containerEnv,
|
|
124
|
-
builtIns: true
|
|
128
|
+
builtIns: true,
|
|
129
|
+
lifecycle: report.apply.lifecycle,
|
|
130
|
+
...report.docker === true ? { docker: true } : {}
|
|
125
131
|
};
|
|
126
132
|
const env = buildEnvironment({
|
|
127
133
|
config,
|
|
@@ -163,7 +169,7 @@ async function provisionMicroVm(input) {
|
|
|
163
169
|
if (input.aborted()) return bail("", true);
|
|
164
170
|
let imageConfig;
|
|
165
171
|
try {
|
|
166
|
-
state.rootfs = await ensureRootfs({ host: vm.rootfs, runnerDir: runnerDirPath, imageId: report.imageId });
|
|
172
|
+
state.rootfs = await ensureRootfs({ host: vm.rootfs, runnerDir: runnerDirPath, imageId: report.imageId, memoryBytes: buildMemoryBytes(config) });
|
|
167
173
|
imageConfig = await vm.rootfs.imageConfig(report.imageId);
|
|
168
174
|
} catch (err) {
|
|
169
175
|
return bail(`the image could not be converted to a root disk: ${describeCallFailure(err)}`);
|
|
@@ -183,14 +189,14 @@ async function provisionMicroVm(input) {
|
|
|
183
189
|
}
|
|
184
190
|
const limits = {
|
|
185
191
|
cpus: config.build?.cpus ?? DEFAULT_CPUS,
|
|
186
|
-
memoryBytes:
|
|
192
|
+
memoryBytes: buildMemoryBytes(config),
|
|
187
193
|
diskBytes: parseSize(config.build?.disk ?? DEFAULT_DISK) ?? parseSize(DEFAULT_DISK)
|
|
188
194
|
};
|
|
189
195
|
const links = vsockLinks({ destinations: config.build?.destinations?.length ?? 0, builtIns: true });
|
|
190
196
|
const configPath = join(buildDir2, "vm.json");
|
|
191
197
|
try {
|
|
192
198
|
linkVsockSockets({ buildDir: buildDir2, vsockPath: state.vsockPath, links });
|
|
193
|
-
await createBuildDisk({ host: vm.rootfs, runnerDir: runnerDirPath, bytes: limits.diskBytes, dest: state.buildDisk });
|
|
199
|
+
await createBuildDisk({ host: vm.rootfs, runnerDir: runnerDirPath, bytes: limits.diskBytes, dest: state.buildDisk, memoryBytes: limits.memoryBytes });
|
|
194
200
|
writeFileSync(
|
|
195
201
|
configPath,
|
|
196
202
|
JSON.stringify(
|
|
@@ -403,6 +409,147 @@ async function copyHookErrors(runtime, state, jobDir) {
|
|
|
403
409
|
} catch {
|
|
404
410
|
}
|
|
405
411
|
}
|
|
412
|
+
async function startCheckMicroVm(input) {
|
|
413
|
+
const { config, runtime, job, jobDir, tokens, log } = input;
|
|
414
|
+
const { vm } = runtime;
|
|
415
|
+
const tag = `#${job.item}`;
|
|
416
|
+
const built = job.microvm;
|
|
417
|
+
const repository = built?.repository ?? job.repository;
|
|
418
|
+
if (built === void 0 || built.imageId === void 0 || repository === void 0 || job.branch === void 0) {
|
|
419
|
+
return { ok: false, reason: "the build's image, repository or branch is not recorded" };
|
|
420
|
+
}
|
|
421
|
+
const branch = job.branch;
|
|
422
|
+
const runnerDirPath = runnerDir(config.configPath);
|
|
423
|
+
const tooDeep = socketPathProblem(buildDir(config.configPath, "0".repeat(12)));
|
|
424
|
+
if (tooDeep !== void 0) return { ok: false, reason: tooDeep };
|
|
425
|
+
const buildId = randomBytes(6).toString("hex");
|
|
426
|
+
const buildDir2 = buildDir(config.configPath, buildId);
|
|
427
|
+
const agentUser = built.agentUser ?? ADDED_USER;
|
|
428
|
+
const state = {
|
|
429
|
+
buildId,
|
|
430
|
+
buildDir: buildDir2,
|
|
431
|
+
repository,
|
|
432
|
+
imageId: built.imageId,
|
|
433
|
+
agentUser,
|
|
434
|
+
containerEnv: built.containerEnv,
|
|
435
|
+
imageEnv: built.imageEnv,
|
|
436
|
+
lifecycle: built.lifecycle,
|
|
437
|
+
...built.docker === true ? { docker: true } : {}
|
|
438
|
+
};
|
|
439
|
+
const checksDir = join(jobDir, "checks");
|
|
440
|
+
mkdirSync(checksDir, { recursive: true });
|
|
441
|
+
const consolePath = join(checksDir, "vm-console.log");
|
|
442
|
+
input.save(state);
|
|
443
|
+
const fail = async (reason) => {
|
|
444
|
+
try {
|
|
445
|
+
await cleanupMicroVm(runtime, state);
|
|
446
|
+
} catch (err) {
|
|
447
|
+
log.error(`${tag} cleanup of the check environment failed: ${describeCallFailure(err)}`);
|
|
448
|
+
}
|
|
449
|
+
return { ok: false, reason };
|
|
450
|
+
};
|
|
451
|
+
const abandoned = () => input.aborted?.() === true;
|
|
452
|
+
const ABANDONED = "the check environment was abandoned before it was ready";
|
|
453
|
+
let assets;
|
|
454
|
+
try {
|
|
455
|
+
assets = await vm.assets(runnerDirPath);
|
|
456
|
+
} catch (err) {
|
|
457
|
+
return fail(err instanceof AssetError ? err.message : `the microVM's assets could not be had: ${describeCallFailure(err)}`);
|
|
458
|
+
}
|
|
459
|
+
if (abandoned()) return fail(ABANDONED);
|
|
460
|
+
try {
|
|
461
|
+
state.rootfs = await ensureRootfs({ host: vm.rootfs, runnerDir: runnerDirPath, imageId: built.imageId, memoryBytes: buildMemoryBytes(config) });
|
|
462
|
+
} catch (err) {
|
|
463
|
+
return fail(`the image could not be converted to a root disk: ${describeCallFailure(err)}`);
|
|
464
|
+
}
|
|
465
|
+
if (abandoned()) return fail(ABANDONED);
|
|
466
|
+
state.buildDisk = join(buildDir2, "build.ext4");
|
|
467
|
+
state.vsockPath = vsockPath(buildDir2);
|
|
468
|
+
input.save(state);
|
|
469
|
+
mkdirSync(buildDir2, { recursive: true, mode: 448 });
|
|
470
|
+
try {
|
|
471
|
+
const gitRemote = gitRemoteFor(runtime, { repository, branch, tokens, buildDir: buildDir2, log: (line) => log.line(`${tag} ${line}`), readOnly: true });
|
|
472
|
+
runtime.tunnels.set(buildId, await startServicesFor(runtime, config, buildDir2, checksDir, void 0, false, gitRemote));
|
|
473
|
+
} catch (err) {
|
|
474
|
+
return fail(`the tunnel could not start: ${describeCallFailure(err)}`);
|
|
475
|
+
}
|
|
476
|
+
if (abandoned()) return fail(ABANDONED);
|
|
477
|
+
const limits = {
|
|
478
|
+
cpus: config.build?.cpus ?? DEFAULT_CPUS,
|
|
479
|
+
memoryBytes: buildMemoryBytes(config),
|
|
480
|
+
diskBytes: parseSize(config.build?.disk ?? DEFAULT_DISK) ?? parseSize(DEFAULT_DISK)
|
|
481
|
+
};
|
|
482
|
+
const links = vsockLinks({ destinations: config.build?.destinations?.length ?? 0, builtIns: false });
|
|
483
|
+
const configPath = join(buildDir2, "vm.json");
|
|
484
|
+
try {
|
|
485
|
+
linkVsockSockets({ buildDir: buildDir2, vsockPath: state.vsockPath, links });
|
|
486
|
+
await createBuildDisk({ host: vm.rootfs, runnerDir: runnerDirPath, bytes: limits.diskBytes, dest: state.buildDisk, memoryBytes: limits.memoryBytes });
|
|
487
|
+
writeFileSync(
|
|
488
|
+
configPath,
|
|
489
|
+
JSON.stringify(
|
|
490
|
+
machineConfig(limits, { kernel: assets.kernel, rootfs: state.rootfs, buildDisk: state.buildDisk, vsock: state.vsockPath, relayPorts: links.map((link) => link.port), docker: built.docker }),
|
|
491
|
+
null,
|
|
492
|
+
2
|
|
493
|
+
),
|
|
494
|
+
{ mode: 384 }
|
|
495
|
+
);
|
|
496
|
+
} catch (err) {
|
|
497
|
+
return fail(`the check environment's disk and machine could not be made: ${describeCallFailure(err)}`);
|
|
498
|
+
}
|
|
499
|
+
if (abandoned()) return fail(ABANDONED);
|
|
500
|
+
try {
|
|
501
|
+
state.cgroup = vm.cgroup.create(buildId, limits.memoryBytes);
|
|
502
|
+
} catch (err) {
|
|
503
|
+
return fail(`the check environment's memory limit could not be set: ${describeCallFailure(err)}`);
|
|
504
|
+
}
|
|
505
|
+
if (state.cgroup !== void 0) input.save(state);
|
|
506
|
+
const boot = async () => {
|
|
507
|
+
try {
|
|
508
|
+
state.firecracker = await vm.firecracker.spawn({ binary: assets.firecracker, configPath, consolePath, cgroup: state.cgroup });
|
|
509
|
+
} catch (err) {
|
|
510
|
+
return `Firecracker did not start: ${describeCallFailure(err)}`;
|
|
511
|
+
}
|
|
512
|
+
input.save(state);
|
|
513
|
+
if (abandoned()) return ABANDONED;
|
|
514
|
+
const ready = await waitForAgent(state.vsockPath, { attempts: BOOT_ATTEMPTS, delayMs: BOOT_POLL_MS, sleep: runtime.sleep });
|
|
515
|
+
if (!ready) {
|
|
516
|
+
const why = vm.firecracker.alive(state.firecracker) ? "its exec agent did not answer" : "it exited before its exec agent answered";
|
|
517
|
+
return `the microVM did not start: ${why}; its console is in ${consolePath}`;
|
|
518
|
+
}
|
|
519
|
+
if (built.docker === true) return waitForDocker(runtime, state, agentUser, () => /* @__PURE__ */ new Date());
|
|
520
|
+
return void 0;
|
|
521
|
+
};
|
|
522
|
+
const failed = await boot();
|
|
523
|
+
if (failed !== void 0) return fail(failed);
|
|
524
|
+
if (abandoned()) return fail(ABANDONED);
|
|
525
|
+
const env = checkEnvironment({ config, item: job.item, repository, hostEnv: runtime.hostEnv, containerEnv: built.containerEnv ?? {} });
|
|
526
|
+
return {
|
|
527
|
+
ok: true,
|
|
528
|
+
environment: {
|
|
529
|
+
state,
|
|
530
|
+
exec: async (argv, workdir, opts) => {
|
|
531
|
+
const ran = await vm.exec(state.vsockPath, { argv, user: agentUser, cwd: workdir, env: execEnvironment(state.imageEnv, env) });
|
|
532
|
+
if (opts?.logPath !== void 0) appendFileSync(opts.logPath, ran.stdout + ran.stderr);
|
|
533
|
+
return ran;
|
|
534
|
+
},
|
|
535
|
+
restart: async () => {
|
|
536
|
+
try {
|
|
537
|
+
await vm.firecracker.stop(state.firecracker);
|
|
538
|
+
rmSync(state.vsockPath, { force: true });
|
|
539
|
+
return await boot() === void 0;
|
|
540
|
+
} catch {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
async function stopBuildMicroVm(runtime, state, jobDir) {
|
|
548
|
+
await copyHookErrors(runtime, state, jobDir);
|
|
549
|
+
if (state.firecracker === void 0) return;
|
|
550
|
+
await runtime.vm.firecracker.stop(state.firecracker);
|
|
551
|
+
if (runtime.vm.firecracker.alive(state.firecracker)) throw new Error(`the microVM (pid ${state.firecracker.pid}) is still running after it was stopped`);
|
|
552
|
+
}
|
|
406
553
|
async function cleanupMicroVm(runtime, state, jobDir) {
|
|
407
554
|
const failures = [];
|
|
408
555
|
if (jobDir !== void 0) await copyHookErrors(runtime, state, jobDir);
|
|
@@ -432,8 +579,8 @@ async function cleanupMicroVm(runtime, state, jobDir) {
|
|
|
432
579
|
async function stopOrphanedMicroVms(input) {
|
|
433
580
|
const stopped = [];
|
|
434
581
|
for (const { job } of input.jobs) {
|
|
435
|
-
const
|
|
436
|
-
if (
|
|
582
|
+
const states = [job.microvm, job.checkMicrovm].filter((state) => state !== void 0);
|
|
583
|
+
if (states.length === 0) continue;
|
|
437
584
|
if (job.pid !== void 0 && job.start !== void 0 && isSameProcess(job.pid, job.start)) {
|
|
438
585
|
try {
|
|
439
586
|
process.kill(-job.pid, "SIGKILL");
|
|
@@ -442,9 +589,11 @@ async function stopOrphanedMicroVms(input) {
|
|
|
442
589
|
const deadline = Date.now() + 2e3;
|
|
443
590
|
while (isSameProcess(job.pid, job.start) && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 10));
|
|
444
591
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
592
|
+
for (const state of states) {
|
|
593
|
+
if (state.firecracker !== void 0 && input.runtime.vm.firecracker.alive(state.firecracker)) {
|
|
594
|
+
await input.runtime.vm.firecracker.stop(state.firecracker);
|
|
595
|
+
stopped.push(state.buildId);
|
|
596
|
+
}
|
|
448
597
|
}
|
|
449
598
|
}
|
|
450
599
|
return stopped;
|
|
@@ -808,8 +957,8 @@ function timeoutAfter(ms, sleep) {
|
|
|
808
957
|
return { done, cancel: () => clearTimeout(timer) };
|
|
809
958
|
}
|
|
810
959
|
function noCleanEnvironment(job) {
|
|
811
|
-
const ran = job.
|
|
812
|
-
return `the host has no clean environment for checks: this build ran under ${ran}, and checks run only in a new container from a
|
|
960
|
+
const ran = job.build?.workDir !== void 0 ? "builtin:process" : "a provision hook of the host's own";
|
|
961
|
+
return `the host has no clean environment for checks: this build ran under ${ran}, and checks run only in a new container or microVM from a build's image`;
|
|
813
962
|
}
|
|
814
963
|
async function runHostChecks(input) {
|
|
815
964
|
const { config, job, jobDir, deps } = input;
|
|
@@ -874,9 +1023,8 @@ async function runHostChecks(input) {
|
|
|
874
1023
|
log.error(`${tag} checks failed: ${reason}${keyTail}`);
|
|
875
1024
|
for (const check of checks) await record(check, "fail", reason, keyTail);
|
|
876
1025
|
};
|
|
877
|
-
let container;
|
|
878
1026
|
try {
|
|
879
|
-
if (job.container === void 0) return await failAll(noCleanEnvironment(job));
|
|
1027
|
+
if (job.container === void 0 && job.microvm === void 0) return await failAll(noCleanEnvironment(job));
|
|
880
1028
|
if (tokens === void 0) return await failAll("the build's repository tokens are no longer held, so the branch cannot be fetched");
|
|
881
1029
|
const branch = job.branch;
|
|
882
1030
|
if (branch === void 0) return await failAll("the build pushed no branch: the claim names none");
|
|
@@ -887,8 +1035,30 @@ async function runHostChecks(input) {
|
|
|
887
1035
|
const limitMinutes = config.checks?.timeLimitMinutes ?? DEFAULT_CHECK_TIME_LIMIT_MINUTES;
|
|
888
1036
|
const minutes = `${limitMinutes} ${limitMinutes === 1 ? "minute" : "minutes"}`;
|
|
889
1037
|
const environmentLog = join(jobDir, "checks", "environment.log");
|
|
1038
|
+
if (job.microvm !== void 0) {
|
|
1039
|
+
if (deps.endBuildMicroVm === void 0) return await failAll("the runner has no way to stop the build's microVM, so no check environment was made");
|
|
1040
|
+
try {
|
|
1041
|
+
await deps.endBuildMicroVm();
|
|
1042
|
+
} catch (err) {
|
|
1043
|
+
return await failAll(`the build's microVM could not be stopped, so no check environment was made: ${describeCallFailure(err)}`);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
let abandoned = false;
|
|
890
1047
|
const setup = async () => {
|
|
891
|
-
const started = await
|
|
1048
|
+
const started = job.microvm !== void 0 ? await startCheckMicroVm({
|
|
1049
|
+
config,
|
|
1050
|
+
runtime,
|
|
1051
|
+
job,
|
|
1052
|
+
jobDir,
|
|
1053
|
+
tokens,
|
|
1054
|
+
log,
|
|
1055
|
+
aborted: () => abandoned,
|
|
1056
|
+
save: (state) => {
|
|
1057
|
+
if (abandoned) return;
|
|
1058
|
+
job.checkMicrovm = state;
|
|
1059
|
+
writeJobFile(jobFilePath, job);
|
|
1060
|
+
}
|
|
1061
|
+
}) : await startCheckContainer({
|
|
892
1062
|
config,
|
|
893
1063
|
runtime,
|
|
894
1064
|
job,
|
|
@@ -901,8 +1071,7 @@ async function runHostChecks(input) {
|
|
|
901
1071
|
}
|
|
902
1072
|
});
|
|
903
1073
|
if (!started.ok) return `the check environment could not be made: ${started.reason}`;
|
|
904
|
-
|
|
905
|
-
const made = started.container;
|
|
1074
|
+
const made = "container" in started ? started.container : started.environment;
|
|
906
1075
|
const steps = [
|
|
907
1076
|
{
|
|
908
1077
|
argv: ["git", "config", "--global", `url.http://127.0.0.1:${RELAY_PORT}/github.com/.insteadOf`, "https://github.com/"],
|
|
@@ -916,10 +1085,11 @@ async function runHostChecks(input) {
|
|
|
916
1085
|
}
|
|
917
1086
|
];
|
|
918
1087
|
for (const step of steps) {
|
|
1088
|
+
if (abandoned) return "the check environment setup ran out of time";
|
|
919
1089
|
const ran = await made.exec(step.argv, step.workdir, { logPath: environmentLog });
|
|
920
1090
|
if (ran.status !== 0) return `the check environment could not be prepared: ${step.failed(ran.status)}`;
|
|
921
1091
|
}
|
|
922
|
-
for (const step of lifecycleSteps(job.container?.lifecycle ?? {})) {
|
|
1092
|
+
for (const step of lifecycleSteps((job.container ?? job.microvm)?.lifecycle ?? {})) {
|
|
923
1093
|
log.line(`${tag} checks lifecycle ${step.name} (${step.source})`);
|
|
924
1094
|
const ran = await made.exec(step.argv, CHECKOUT_DIR, { logPath: environmentLog });
|
|
925
1095
|
if (ran.status !== 0) return `the check environment could not be prepared: ${lifecycleFailureReason(step, ran.status)}`;
|
|
@@ -929,8 +1099,14 @@ async function runHostChecks(input) {
|
|
|
929
1099
|
const setupTimer = timeoutAfter(limitMinutes * 6e4, deps.sleep);
|
|
930
1100
|
let settled;
|
|
931
1101
|
try {
|
|
932
|
-
const outcome = await Promise.race([
|
|
933
|
-
|
|
1102
|
+
const outcome = await Promise.race([
|
|
1103
|
+
setup().catch((err) => `the check environment could not be made: ${describeCallFailure(err)}`),
|
|
1104
|
+
setupTimer.done
|
|
1105
|
+
]);
|
|
1106
|
+
if (outcome === "timeout") {
|
|
1107
|
+
abandoned = true;
|
|
1108
|
+
return await failAll(`the check environment setup ran out of time after ${minutes}`, LIMIT_KEY_TAIL);
|
|
1109
|
+
}
|
|
934
1110
|
settled = outcome;
|
|
935
1111
|
} finally {
|
|
936
1112
|
setupTimer.cancel();
|
|
@@ -976,12 +1152,16 @@ async function runHostChecks(input) {
|
|
|
976
1152
|
} catch (err) {
|
|
977
1153
|
log.error(`${tag} checks stopped: ${describeCallFailure(err)}`);
|
|
978
1154
|
} finally {
|
|
979
|
-
if (
|
|
980
|
-
|
|
981
|
-
await cleanupContainer(runtime, state).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
|
|
1155
|
+
if (job.checkContainer !== void 0) {
|
|
1156
|
+
await cleanupContainer(runtime, job.checkContainer).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
|
|
982
1157
|
delete job.checkContainer;
|
|
983
1158
|
writeJobFile(jobFilePath, job);
|
|
984
1159
|
}
|
|
1160
|
+
if (job.checkMicrovm !== void 0) {
|
|
1161
|
+
await cleanupMicroVm(runtime, job.checkMicrovm).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
|
|
1162
|
+
delete job.checkMicrovm;
|
|
1163
|
+
writeJobFile(jobFilePath, job);
|
|
1164
|
+
}
|
|
985
1165
|
rmSync(scratch, { recursive: true, force: true });
|
|
986
1166
|
if (existsSync(scratch)) log.error(`${tag} the checks' scratch directory could not be removed`);
|
|
987
1167
|
log.line(`${tag} checks done (${formatDuration(now().getTime() - startedMs)})`);
|
|
@@ -1461,6 +1641,36 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1461
1641
|
job.reason = scrubReason(ending.reason, config);
|
|
1462
1642
|
writeJobFile(jobFilePath, job);
|
|
1463
1643
|
}
|
|
1644
|
+
const uploadSessions = async (workdir) => {
|
|
1645
|
+
if (job.container === void 0 && job.microvm === void 0 && job.build?.exec === void 0 && workdirProblem(workdir) !== void 0) {
|
|
1646
|
+
log.line(`${tag} sync skipped: workdir ${workdir} is gone`);
|
|
1647
|
+
} else if (job.itemKey === void 0) {
|
|
1648
|
+
log.line(`${tag} sync skipped: the job file holds no item key`);
|
|
1649
|
+
} else {
|
|
1650
|
+
log.line(`${tag} sync started`);
|
|
1651
|
+
const startedMs = now().getTime();
|
|
1652
|
+
const sync = build !== void 0 ? await syncInContainer({ config, runtime: build.runtime, job, jobDir: jobDirPath }) : vmBuild !== void 0 ? await syncInMicroVm({ config, runtime: vmBuild.runtime, job, jobDir: jobDirPath }) : job.build?.exec !== void 0 ? await runSyncViaExec(config, job, jobDirPath, spawnFn) : await runSync(config, job.item, job.itemKey, workdir, jobDirPath, spawnFn, job.branch, job.commitIdentity?.author);
|
|
1653
|
+
const took = formatDuration(now().getTime() - startedMs);
|
|
1654
|
+
if (sync.ok) log.line(`${tag} sync ok (${took})`);
|
|
1655
|
+
else log.error(`${tag} sync failed: ${sync.reason ?? "no reason given"} \xB7 continuing`);
|
|
1656
|
+
const left = spooledArtifactIds(deps.home ?? homeFromEnv());
|
|
1657
|
+
if (left.length > 0) {
|
|
1658
|
+
job.spooledArtifacts = left.slice(0, MAX_NAMED_SPOOLED);
|
|
1659
|
+
const named = job.spooledArtifacts.join(", ");
|
|
1660
|
+
log.error(
|
|
1661
|
+
`${tag} ${left.length} spooled artifact(s) not uploaded: ${named}${left.length > MAX_NAMED_SPOOLED ? `, and ${left.length - MAX_NAMED_SPOOLED} more` : ""}`
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
const endBuildMicroVm = vmBuild === void 0 ? void 0 : async () => {
|
|
1667
|
+
if (job.workdir && job.sessionsSynced !== true) {
|
|
1668
|
+
await uploadSessions(job.workdir);
|
|
1669
|
+
job.sessionsSynced = true;
|
|
1670
|
+
writeJobFile(jobFilePath, job);
|
|
1671
|
+
}
|
|
1672
|
+
await stopBuildMicroVm(vmBuild.runtime, vmBuild.state, jobDirPath);
|
|
1673
|
+
};
|
|
1464
1674
|
if (job.checkContainer !== void 0 && job.checksRan === true) {
|
|
1465
1675
|
try {
|
|
1466
1676
|
await cleanupContainer(deps.container ?? createContainerRuntime(), job.checkContainer);
|
|
@@ -1470,6 +1680,15 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1470
1680
|
log.error(`${tag} removing an earlier attempt's check environment failed: ${describeCallFailure(err)} \xB7 continuing`);
|
|
1471
1681
|
}
|
|
1472
1682
|
}
|
|
1683
|
+
if (job.checkMicrovm !== void 0 && job.checksRan === true) {
|
|
1684
|
+
try {
|
|
1685
|
+
await cleanupMicroVm(deps.container ?? createContainerRuntime(), job.checkMicrovm);
|
|
1686
|
+
delete job.checkMicrovm;
|
|
1687
|
+
writeJobFile(jobFilePath, job);
|
|
1688
|
+
} catch (err) {
|
|
1689
|
+
log.error(`${tag} removing an earlier attempt's check environment failed: ${describeCallFailure(err)} \xB7 continuing`);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1473
1692
|
if (outcome === "ok" && !job.cleanupRan && job.checksRan !== true) {
|
|
1474
1693
|
setStage(job, jobDirPath, "checks");
|
|
1475
1694
|
job.checksRan = true;
|
|
@@ -1479,7 +1698,7 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1479
1698
|
config,
|
|
1480
1699
|
job,
|
|
1481
1700
|
jobDir: jobDirPath,
|
|
1482
|
-
deps: { fetchImpl: deps.fetchImpl, runtime: deps.container ?? createContainerRuntime(), log, now: deps.now, ...deps.hostChecks }
|
|
1701
|
+
deps: { fetchImpl: deps.fetchImpl, runtime: deps.container ?? createContainerRuntime(), log, now: deps.now, endBuildMicroVm, ...deps.hostChecks }
|
|
1483
1702
|
});
|
|
1484
1703
|
} catch (err) {
|
|
1485
1704
|
log.error(`${tag} the host's checks stopped: ${describeCallFailure(err)} \xB7 continuing`);
|
|
@@ -1504,26 +1723,8 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1504
1723
|
if (!job.cleanupRan) {
|
|
1505
1724
|
if (job.workdir) {
|
|
1506
1725
|
setStage(job, jobDirPath, "sync");
|
|
1507
|
-
if (job.
|
|
1508
|
-
|
|
1509
|
-
} else if (job.itemKey === void 0) {
|
|
1510
|
-
log.line(`${tag} sync skipped: the job file holds no item key`);
|
|
1511
|
-
} else {
|
|
1512
|
-
log.line(`${tag} sync started`);
|
|
1513
|
-
const startedMs = now().getTime();
|
|
1514
|
-
const sync = build !== void 0 ? await syncInContainer({ config, runtime: build.runtime, job, jobDir: jobDirPath }) : vmBuild !== void 0 ? await syncInMicroVm({ config, runtime: vmBuild.runtime, job, jobDir: jobDirPath }) : job.build?.exec !== void 0 ? await runSyncViaExec(config, job, jobDirPath, spawnFn) : await runSync(config, job.item, job.itemKey, job.workdir, jobDirPath, spawnFn, job.branch, job.commitIdentity?.author);
|
|
1515
|
-
const took = formatDuration(now().getTime() - startedMs);
|
|
1516
|
-
if (sync.ok) log.line(`${tag} sync ok (${took})`);
|
|
1517
|
-
else log.error(`${tag} sync failed: ${sync.reason ?? "no reason given"} \xB7 continuing`);
|
|
1518
|
-
const left = spooledArtifactIds(deps.home ?? homeFromEnv());
|
|
1519
|
-
if (left.length > 0) {
|
|
1520
|
-
job.spooledArtifacts = left.slice(0, MAX_NAMED_SPOOLED);
|
|
1521
|
-
const named = job.spooledArtifacts.join(", ");
|
|
1522
|
-
log.error(
|
|
1523
|
-
`${tag} ${left.length} spooled artifact(s) not uploaded: ${named}${left.length > MAX_NAMED_SPOOLED ? `, and ${left.length - MAX_NAMED_SPOOLED} more` : ""}`
|
|
1524
|
-
);
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1726
|
+
if (job.sessionsSynced === true) log.line(`${tag} sync done before the build's microVM stopped`);
|
|
1727
|
+
else await uploadSessions(job.workdir);
|
|
1527
1728
|
}
|
|
1528
1729
|
setStage(job, jobDirPath, "cleanup");
|
|
1529
1730
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
4
|
-
import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-
|
|
3
|
+
import { MIN_ENGINE_MAJOR } from './chunk-M4ERCYDB.js';
|
|
4
|
+
import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-CX22S5BA.js';
|
|
5
5
|
import { spawnSync } from 'child_process';
|
|
6
6
|
import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
|
|
7
7
|
|