@outerlayer/cli 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +5 -5
- package/dist/{build-JF5UOWKJ.js → build-MS34XEGN.js} +2 -1
- package/dist/build-info.json +1 -1
- package/dist/builder-G52A6GW2.js +13 -0
- package/dist/{check-ASZL5BEO.js → check-FOF3PUKI.js} +21 -18
- package/dist/{chunk-2VO25HHO.js → chunk-3S4P55NE.js} +2 -2
- package/dist/{chunk-MQPRB5K7.js → chunk-4A5E2G4E.js} +6 -6
- package/dist/{chunk-PUN7LMNS.js → chunk-4ACZCY4I.js} +2 -2
- package/dist/{chunk-DGAIVMMN.js → chunk-5QLNZGAQ.js} +2 -2
- package/dist/chunk-5TNS3PL7.js +263 -0
- package/dist/chunk-7RQ73DLO.js +23 -0
- package/dist/{chunk-AJKEHABI.js → chunk-7VNURP6M.js} +248 -50
- package/dist/{chunk-6ZZHWY2Z.js → chunk-AIL2R4SI.js} +1 -1
- package/dist/{chunk-7FTNIAHS.js → chunk-AX23QKHS.js} +7 -3
- package/dist/{chunk-E4PIFIC3.js → chunk-CM5QTKM4.js} +58 -149
- package/dist/chunk-CX22S5BA.js +45 -0
- package/dist/{chunk-D6FXM5OW.js → chunk-GAYWY4GI.js} +8 -8
- package/dist/{chunk-AII7CKTH.js → chunk-IJBVPLZZ.js} +54 -16
- package/dist/{chunk-SSU747O6.js → chunk-ILEA3YHM.js} +1 -1
- package/dist/{chunk-6IYLDODX.js → chunk-IRCBTGWF.js} +4 -4
- package/dist/{chunk-OUUINEYB.js → chunk-JKIWMRFH.js} +1 -1
- package/dist/{chunk-X2FSFU4C.js → chunk-K35RW5HP.js} +2 -2
- package/dist/{chunk-6QWV3YDJ.js → chunk-NALVDFIS.js} +2 -2
- package/dist/{chunk-Q5G2ODUZ.js → chunk-OEK4JEB3.js} +33 -4
- package/dist/chunk-PTW6W5NR.js +127 -0
- package/dist/{chunk-DTXOJDEH.js → chunk-PWBILI5P.js} +3 -2
- package/dist/{chunk-CEBIA5FD.js → chunk-TTNFGMHO.js} +42 -50
- package/dist/{chunk-AQ54MKSQ.js → chunk-TUUMW4TC.js} +2 -2
- package/dist/{chunk-774HEQL6.js → chunk-U3GOI2B4.js} +91 -8
- package/dist/{chunk-F6GFZXZ3.js → chunk-UXYLWVTQ.js} +1 -18
- package/dist/{cli-LEVCGAQV.js → cli-AOJPAVFX.js} +94 -68
- package/dist/{command-FLSYCGIB.js → command-SY3SVLAI.js} +31 -16
- package/dist/{config-I3TVFYLH.js → config-Z3USDBLU.js} +3 -2
- package/dist/{connect-cmd-MJMUONDY.js → connect-cmd-PNZ6IJLJ.js} +2 -2
- package/dist/{dist-PXBSA7QX.js → dist-3ZTZF2TP.js} +1 -1
- package/dist/docker-676FD3CQ.js +15 -0
- package/dist/{doctor-FVDT3KJZ.js → doctor-WR6TRPSU.js} +36 -13
- package/dist/{emit-artifact-cmd-7RPMRSVP.js → emit-artifact-cmd-AVM4YXMQ.js} +4 -4
- package/dist/{emit-criteria-cmd-MKI7WETO.js → emit-criteria-cmd-LDBE4YDS.js} +3 -3
- package/dist/{emit-finding-cmd-RMLGZASB.js → emit-finding-cmd-6R5WBPCH.js} +3 -3
- package/dist/{emit-result-cmd-MTXAZ2GV.js → emit-result-cmd-2DMN6O6U.js} +4 -4
- package/dist/index.js +1 -1
- package/dist/{init-BACXESVV.js → init-KDK5QLQZ.js} +13 -10
- package/dist/{init-VLW7H6RW.js → init-R2FJ5KNS.js} +22 -15
- package/dist/{init-cmd-VJFB5L7X.js → init-cmd-BYZYAJ5P.js} +5 -5
- package/dist/{install-NMAPLLBQ.js → install-FHTVJZS6.js} +5 -4
- package/dist/{install-cmd-B5SEU3G7.js → install-cmd-DCF7TVXD.js} +2 -1
- package/dist/{loop-D3243JKT.js → loop-AG3PLXPO.js} +21 -18
- package/dist/{machine-EB3GEVI4.js → machine-LV6WBRRK.js} +12 -9
- package/dist/{session-sender-LMACEJJS.js → session-sender-Q5HAMOL2.js} +3 -3
- package/dist/{status-F4THTJJK.js → status-HH4OSOHR.js} +15 -12
- package/dist/{sync-cmd-M6JA3OEE.js → sync-cmd-CAEPJR7B.js} +2 -2
- package/dist/{watch-NB5TGPEZ.js → watch-LAAZFBHH.js} +2 -2
- package/dist/{work-claim-cmd-UHMWOS3G.js → work-claim-cmd-ARIYZARE.js} +8 -7
- package/dist/{work-cmd-6UMCFTAW.js → work-cmd-B3FOXZAL.js} +4 -4
- package/dist/{work-comment-cmd-E4LEUIXZ.js → work-comment-cmd-O5WGRI6K.js} +4 -4
- package/dist/{work-open-pr-cmd-OPZWGSEQ.js → work-open-pr-cmd-M6CQW7KW.js} +5 -5
- package/dist/{work-pr-cmd-IDIAFFRU.js → work-pr-cmd-C4YEGKD6.js} +4 -4
- package/package.json +1 -1
- package/dist/docker-NEGDLU6D.js +0 -7
- package/dist/{agent-setup-TXLDY5RV.js → agent-setup-C7PLRXZO.js} +1 -1
|
@@ -1,21 +1,22 @@
|
|
|
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-3S4P55NE.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-AIL2R4SI.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-CM5QTKM4.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-U3GOI2B4.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-5QLNZGAQ.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';
|
|
@@ -121,7 +122,9 @@ async function provisionMicroVm(input) {
|
|
|
121
122
|
repository,
|
|
122
123
|
imageId: report.imageId,
|
|
123
124
|
containerEnv: report.apply.containerEnv,
|
|
124
|
-
builtIns: true
|
|
125
|
+
builtIns: true,
|
|
126
|
+
lifecycle: report.apply.lifecycle,
|
|
127
|
+
...report.docker === true ? { docker: true } : {}
|
|
125
128
|
};
|
|
126
129
|
const env = buildEnvironment({
|
|
127
130
|
config,
|
|
@@ -403,6 +406,147 @@ async function copyHookErrors(runtime, state, jobDir) {
|
|
|
403
406
|
} catch {
|
|
404
407
|
}
|
|
405
408
|
}
|
|
409
|
+
async function startCheckMicroVm(input) {
|
|
410
|
+
const { config, runtime, job, jobDir, tokens, log } = input;
|
|
411
|
+
const { vm } = runtime;
|
|
412
|
+
const tag = `#${job.item}`;
|
|
413
|
+
const built = job.microvm;
|
|
414
|
+
const repository = built?.repository ?? job.repository;
|
|
415
|
+
if (built === void 0 || built.imageId === void 0 || repository === void 0 || job.branch === void 0) {
|
|
416
|
+
return { ok: false, reason: "the build's image, repository or branch is not recorded" };
|
|
417
|
+
}
|
|
418
|
+
const branch = job.branch;
|
|
419
|
+
const runnerDirPath = runnerDir(config.configPath);
|
|
420
|
+
const tooDeep = socketPathProblem(buildDir(config.configPath, "0".repeat(12)));
|
|
421
|
+
if (tooDeep !== void 0) return { ok: false, reason: tooDeep };
|
|
422
|
+
const buildId = randomBytes(6).toString("hex");
|
|
423
|
+
const buildDir2 = buildDir(config.configPath, buildId);
|
|
424
|
+
const agentUser = built.agentUser ?? ADDED_USER;
|
|
425
|
+
const state = {
|
|
426
|
+
buildId,
|
|
427
|
+
buildDir: buildDir2,
|
|
428
|
+
repository,
|
|
429
|
+
imageId: built.imageId,
|
|
430
|
+
agentUser,
|
|
431
|
+
containerEnv: built.containerEnv,
|
|
432
|
+
imageEnv: built.imageEnv,
|
|
433
|
+
lifecycle: built.lifecycle,
|
|
434
|
+
...built.docker === true ? { docker: true } : {}
|
|
435
|
+
};
|
|
436
|
+
const checksDir = join(jobDir, "checks");
|
|
437
|
+
mkdirSync(checksDir, { recursive: true });
|
|
438
|
+
const consolePath = join(checksDir, "vm-console.log");
|
|
439
|
+
input.save(state);
|
|
440
|
+
const fail = async (reason) => {
|
|
441
|
+
try {
|
|
442
|
+
await cleanupMicroVm(runtime, state);
|
|
443
|
+
} catch (err) {
|
|
444
|
+
log.error(`${tag} cleanup of the check environment failed: ${describeCallFailure(err)}`);
|
|
445
|
+
}
|
|
446
|
+
return { ok: false, reason };
|
|
447
|
+
};
|
|
448
|
+
const abandoned = () => input.aborted?.() === true;
|
|
449
|
+
const ABANDONED = "the check environment was abandoned before it was ready";
|
|
450
|
+
let assets;
|
|
451
|
+
try {
|
|
452
|
+
assets = await vm.assets(runnerDirPath);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
return fail(err instanceof AssetError ? err.message : `the microVM's assets could not be had: ${describeCallFailure(err)}`);
|
|
455
|
+
}
|
|
456
|
+
if (abandoned()) return fail(ABANDONED);
|
|
457
|
+
try {
|
|
458
|
+
state.rootfs = await ensureRootfs({ host: vm.rootfs, runnerDir: runnerDirPath, imageId: built.imageId });
|
|
459
|
+
} catch (err) {
|
|
460
|
+
return fail(`the image could not be converted to a root disk: ${describeCallFailure(err)}`);
|
|
461
|
+
}
|
|
462
|
+
if (abandoned()) return fail(ABANDONED);
|
|
463
|
+
state.buildDisk = join(buildDir2, "build.ext4");
|
|
464
|
+
state.vsockPath = vsockPath(buildDir2);
|
|
465
|
+
input.save(state);
|
|
466
|
+
mkdirSync(buildDir2, { recursive: true, mode: 448 });
|
|
467
|
+
try {
|
|
468
|
+
const gitRemote = gitRemoteFor(runtime, { repository, branch, tokens, buildDir: buildDir2, log: (line) => log.line(`${tag} ${line}`), readOnly: true });
|
|
469
|
+
runtime.tunnels.set(buildId, await startServicesFor(runtime, config, buildDir2, checksDir, void 0, false, gitRemote));
|
|
470
|
+
} catch (err) {
|
|
471
|
+
return fail(`the tunnel could not start: ${describeCallFailure(err)}`);
|
|
472
|
+
}
|
|
473
|
+
if (abandoned()) return fail(ABANDONED);
|
|
474
|
+
const limits = {
|
|
475
|
+
cpus: config.build?.cpus ?? DEFAULT_CPUS,
|
|
476
|
+
memoryBytes: parseSize(config.build?.memory ?? DEFAULT_MEMORY) ?? parseSize(DEFAULT_MEMORY),
|
|
477
|
+
diskBytes: parseSize(config.build?.disk ?? DEFAULT_DISK) ?? parseSize(DEFAULT_DISK)
|
|
478
|
+
};
|
|
479
|
+
const links = vsockLinks({ destinations: config.build?.destinations?.length ?? 0, builtIns: false });
|
|
480
|
+
const configPath = join(buildDir2, "vm.json");
|
|
481
|
+
try {
|
|
482
|
+
linkVsockSockets({ buildDir: buildDir2, vsockPath: state.vsockPath, links });
|
|
483
|
+
await createBuildDisk({ host: vm.rootfs, runnerDir: runnerDirPath, bytes: limits.diskBytes, dest: state.buildDisk });
|
|
484
|
+
writeFileSync(
|
|
485
|
+
configPath,
|
|
486
|
+
JSON.stringify(
|
|
487
|
+
machineConfig(limits, { kernel: assets.kernel, rootfs: state.rootfs, buildDisk: state.buildDisk, vsock: state.vsockPath, relayPorts: links.map((link) => link.port), docker: built.docker }),
|
|
488
|
+
null,
|
|
489
|
+
2
|
|
490
|
+
),
|
|
491
|
+
{ mode: 384 }
|
|
492
|
+
);
|
|
493
|
+
} catch (err) {
|
|
494
|
+
return fail(`the check environment's disk and machine could not be made: ${describeCallFailure(err)}`);
|
|
495
|
+
}
|
|
496
|
+
if (abandoned()) return fail(ABANDONED);
|
|
497
|
+
try {
|
|
498
|
+
state.cgroup = vm.cgroup.create(buildId, limits.memoryBytes);
|
|
499
|
+
} catch (err) {
|
|
500
|
+
return fail(`the check environment's memory limit could not be set: ${describeCallFailure(err)}`);
|
|
501
|
+
}
|
|
502
|
+
if (state.cgroup !== void 0) input.save(state);
|
|
503
|
+
const boot = async () => {
|
|
504
|
+
try {
|
|
505
|
+
state.firecracker = await vm.firecracker.spawn({ binary: assets.firecracker, configPath, consolePath, cgroup: state.cgroup });
|
|
506
|
+
} catch (err) {
|
|
507
|
+
return `Firecracker did not start: ${describeCallFailure(err)}`;
|
|
508
|
+
}
|
|
509
|
+
input.save(state);
|
|
510
|
+
if (abandoned()) return ABANDONED;
|
|
511
|
+
const ready = await waitForAgent(state.vsockPath, { attempts: BOOT_ATTEMPTS, delayMs: BOOT_POLL_MS, sleep: runtime.sleep });
|
|
512
|
+
if (!ready) {
|
|
513
|
+
const why = vm.firecracker.alive(state.firecracker) ? "its exec agent did not answer" : "it exited before its exec agent answered";
|
|
514
|
+
return `the microVM did not start: ${why}; its console is in ${consolePath}`;
|
|
515
|
+
}
|
|
516
|
+
if (built.docker === true) return waitForDocker(runtime, state, agentUser, () => /* @__PURE__ */ new Date());
|
|
517
|
+
return void 0;
|
|
518
|
+
};
|
|
519
|
+
const failed = await boot();
|
|
520
|
+
if (failed !== void 0) return fail(failed);
|
|
521
|
+
if (abandoned()) return fail(ABANDONED);
|
|
522
|
+
const env = checkEnvironment({ config, item: job.item, repository, hostEnv: runtime.hostEnv, containerEnv: built.containerEnv ?? {} });
|
|
523
|
+
return {
|
|
524
|
+
ok: true,
|
|
525
|
+
environment: {
|
|
526
|
+
state,
|
|
527
|
+
exec: async (argv, workdir, opts) => {
|
|
528
|
+
const ran = await vm.exec(state.vsockPath, { argv, user: agentUser, cwd: workdir, env: execEnvironment(state.imageEnv, env) });
|
|
529
|
+
if (opts?.logPath !== void 0) appendFileSync(opts.logPath, ran.stdout + ran.stderr);
|
|
530
|
+
return ran;
|
|
531
|
+
},
|
|
532
|
+
restart: async () => {
|
|
533
|
+
try {
|
|
534
|
+
await vm.firecracker.stop(state.firecracker);
|
|
535
|
+
rmSync(state.vsockPath, { force: true });
|
|
536
|
+
return await boot() === void 0;
|
|
537
|
+
} catch {
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
async function stopBuildMicroVm(runtime, state, jobDir) {
|
|
545
|
+
await copyHookErrors(runtime, state, jobDir);
|
|
546
|
+
if (state.firecracker === void 0) return;
|
|
547
|
+
await runtime.vm.firecracker.stop(state.firecracker);
|
|
548
|
+
if (runtime.vm.firecracker.alive(state.firecracker)) throw new Error(`the microVM (pid ${state.firecracker.pid}) is still running after it was stopped`);
|
|
549
|
+
}
|
|
406
550
|
async function cleanupMicroVm(runtime, state, jobDir) {
|
|
407
551
|
const failures = [];
|
|
408
552
|
if (jobDir !== void 0) await copyHookErrors(runtime, state, jobDir);
|
|
@@ -432,8 +576,8 @@ async function cleanupMicroVm(runtime, state, jobDir) {
|
|
|
432
576
|
async function stopOrphanedMicroVms(input) {
|
|
433
577
|
const stopped = [];
|
|
434
578
|
for (const { job } of input.jobs) {
|
|
435
|
-
const
|
|
436
|
-
if (
|
|
579
|
+
const states = [job.microvm, job.checkMicrovm].filter((state) => state !== void 0);
|
|
580
|
+
if (states.length === 0) continue;
|
|
437
581
|
if (job.pid !== void 0 && job.start !== void 0 && isSameProcess(job.pid, job.start)) {
|
|
438
582
|
try {
|
|
439
583
|
process.kill(-job.pid, "SIGKILL");
|
|
@@ -442,9 +586,11 @@ async function stopOrphanedMicroVms(input) {
|
|
|
442
586
|
const deadline = Date.now() + 2e3;
|
|
443
587
|
while (isSameProcess(job.pid, job.start) && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 10));
|
|
444
588
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
589
|
+
for (const state of states) {
|
|
590
|
+
if (state.firecracker !== void 0 && input.runtime.vm.firecracker.alive(state.firecracker)) {
|
|
591
|
+
await input.runtime.vm.firecracker.stop(state.firecracker);
|
|
592
|
+
stopped.push(state.buildId);
|
|
593
|
+
}
|
|
448
594
|
}
|
|
449
595
|
}
|
|
450
596
|
return stopped;
|
|
@@ -808,8 +954,8 @@ function timeoutAfter(ms, sleep) {
|
|
|
808
954
|
return { done, cancel: () => clearTimeout(timer) };
|
|
809
955
|
}
|
|
810
956
|
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
|
|
957
|
+
const ran = job.build?.workDir !== void 0 ? "builtin:process" : "a provision hook of the host's own";
|
|
958
|
+
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
959
|
}
|
|
814
960
|
async function runHostChecks(input) {
|
|
815
961
|
const { config, job, jobDir, deps } = input;
|
|
@@ -874,9 +1020,8 @@ async function runHostChecks(input) {
|
|
|
874
1020
|
log.error(`${tag} checks failed: ${reason}${keyTail}`);
|
|
875
1021
|
for (const check of checks) await record(check, "fail", reason, keyTail);
|
|
876
1022
|
};
|
|
877
|
-
let container;
|
|
878
1023
|
try {
|
|
879
|
-
if (job.container === void 0) return await failAll(noCleanEnvironment(job));
|
|
1024
|
+
if (job.container === void 0 && job.microvm === void 0) return await failAll(noCleanEnvironment(job));
|
|
880
1025
|
if (tokens === void 0) return await failAll("the build's repository tokens are no longer held, so the branch cannot be fetched");
|
|
881
1026
|
const branch = job.branch;
|
|
882
1027
|
if (branch === void 0) return await failAll("the build pushed no branch: the claim names none");
|
|
@@ -887,8 +1032,30 @@ async function runHostChecks(input) {
|
|
|
887
1032
|
const limitMinutes = config.checks?.timeLimitMinutes ?? DEFAULT_CHECK_TIME_LIMIT_MINUTES;
|
|
888
1033
|
const minutes = `${limitMinutes} ${limitMinutes === 1 ? "minute" : "minutes"}`;
|
|
889
1034
|
const environmentLog = join(jobDir, "checks", "environment.log");
|
|
1035
|
+
if (job.microvm !== void 0) {
|
|
1036
|
+
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");
|
|
1037
|
+
try {
|
|
1038
|
+
await deps.endBuildMicroVm();
|
|
1039
|
+
} catch (err) {
|
|
1040
|
+
return await failAll(`the build's microVM could not be stopped, so no check environment was made: ${describeCallFailure(err)}`);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
let abandoned = false;
|
|
890
1044
|
const setup = async () => {
|
|
891
|
-
const started = await
|
|
1045
|
+
const started = job.microvm !== void 0 ? await startCheckMicroVm({
|
|
1046
|
+
config,
|
|
1047
|
+
runtime,
|
|
1048
|
+
job,
|
|
1049
|
+
jobDir,
|
|
1050
|
+
tokens,
|
|
1051
|
+
log,
|
|
1052
|
+
aborted: () => abandoned,
|
|
1053
|
+
save: (state) => {
|
|
1054
|
+
if (abandoned) return;
|
|
1055
|
+
job.checkMicrovm = state;
|
|
1056
|
+
writeJobFile(jobFilePath, job);
|
|
1057
|
+
}
|
|
1058
|
+
}) : await startCheckContainer({
|
|
892
1059
|
config,
|
|
893
1060
|
runtime,
|
|
894
1061
|
job,
|
|
@@ -901,8 +1068,7 @@ async function runHostChecks(input) {
|
|
|
901
1068
|
}
|
|
902
1069
|
});
|
|
903
1070
|
if (!started.ok) return `the check environment could not be made: ${started.reason}`;
|
|
904
|
-
|
|
905
|
-
const made = started.container;
|
|
1071
|
+
const made = "container" in started ? started.container : started.environment;
|
|
906
1072
|
const steps = [
|
|
907
1073
|
{
|
|
908
1074
|
argv: ["git", "config", "--global", `url.http://127.0.0.1:${RELAY_PORT}/github.com/.insteadOf`, "https://github.com/"],
|
|
@@ -916,10 +1082,11 @@ async function runHostChecks(input) {
|
|
|
916
1082
|
}
|
|
917
1083
|
];
|
|
918
1084
|
for (const step of steps) {
|
|
1085
|
+
if (abandoned) return "the check environment setup ran out of time";
|
|
919
1086
|
const ran = await made.exec(step.argv, step.workdir, { logPath: environmentLog });
|
|
920
1087
|
if (ran.status !== 0) return `the check environment could not be prepared: ${step.failed(ran.status)}`;
|
|
921
1088
|
}
|
|
922
|
-
for (const step of lifecycleSteps(job.container?.lifecycle ?? {})) {
|
|
1089
|
+
for (const step of lifecycleSteps((job.container ?? job.microvm)?.lifecycle ?? {})) {
|
|
923
1090
|
log.line(`${tag} checks lifecycle ${step.name} (${step.source})`);
|
|
924
1091
|
const ran = await made.exec(step.argv, CHECKOUT_DIR, { logPath: environmentLog });
|
|
925
1092
|
if (ran.status !== 0) return `the check environment could not be prepared: ${lifecycleFailureReason(step, ran.status)}`;
|
|
@@ -929,8 +1096,14 @@ async function runHostChecks(input) {
|
|
|
929
1096
|
const setupTimer = timeoutAfter(limitMinutes * 6e4, deps.sleep);
|
|
930
1097
|
let settled;
|
|
931
1098
|
try {
|
|
932
|
-
const outcome = await Promise.race([
|
|
933
|
-
|
|
1099
|
+
const outcome = await Promise.race([
|
|
1100
|
+
setup().catch((err) => `the check environment could not be made: ${describeCallFailure(err)}`),
|
|
1101
|
+
setupTimer.done
|
|
1102
|
+
]);
|
|
1103
|
+
if (outcome === "timeout") {
|
|
1104
|
+
abandoned = true;
|
|
1105
|
+
return await failAll(`the check environment setup ran out of time after ${minutes}`, LIMIT_KEY_TAIL);
|
|
1106
|
+
}
|
|
934
1107
|
settled = outcome;
|
|
935
1108
|
} finally {
|
|
936
1109
|
setupTimer.cancel();
|
|
@@ -976,12 +1149,16 @@ async function runHostChecks(input) {
|
|
|
976
1149
|
} catch (err) {
|
|
977
1150
|
log.error(`${tag} checks stopped: ${describeCallFailure(err)}`);
|
|
978
1151
|
} finally {
|
|
979
|
-
if (
|
|
980
|
-
|
|
981
|
-
await cleanupContainer(runtime, state).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
|
|
1152
|
+
if (job.checkContainer !== void 0) {
|
|
1153
|
+
await cleanupContainer(runtime, job.checkContainer).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
|
|
982
1154
|
delete job.checkContainer;
|
|
983
1155
|
writeJobFile(jobFilePath, job);
|
|
984
1156
|
}
|
|
1157
|
+
if (job.checkMicrovm !== void 0) {
|
|
1158
|
+
await cleanupMicroVm(runtime, job.checkMicrovm).catch((err) => log.error(`${tag} removing the check environment failed: ${describeCallFailure(err)}`));
|
|
1159
|
+
delete job.checkMicrovm;
|
|
1160
|
+
writeJobFile(jobFilePath, job);
|
|
1161
|
+
}
|
|
985
1162
|
rmSync(scratch, { recursive: true, force: true });
|
|
986
1163
|
if (existsSync(scratch)) log.error(`${tag} the checks' scratch directory could not be removed`);
|
|
987
1164
|
log.line(`${tag} checks done (${formatDuration(now().getTime() - startedMs)})`);
|
|
@@ -1461,6 +1638,36 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1461
1638
|
job.reason = scrubReason(ending.reason, config);
|
|
1462
1639
|
writeJobFile(jobFilePath, job);
|
|
1463
1640
|
}
|
|
1641
|
+
const uploadSessions = async (workdir) => {
|
|
1642
|
+
if (job.container === void 0 && job.microvm === void 0 && job.build?.exec === void 0 && workdirProblem(workdir) !== void 0) {
|
|
1643
|
+
log.line(`${tag} sync skipped: workdir ${workdir} is gone`);
|
|
1644
|
+
} else if (job.itemKey === void 0) {
|
|
1645
|
+
log.line(`${tag} sync skipped: the job file holds no item key`);
|
|
1646
|
+
} else {
|
|
1647
|
+
log.line(`${tag} sync started`);
|
|
1648
|
+
const startedMs = now().getTime();
|
|
1649
|
+
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);
|
|
1650
|
+
const took = formatDuration(now().getTime() - startedMs);
|
|
1651
|
+
if (sync.ok) log.line(`${tag} sync ok (${took})`);
|
|
1652
|
+
else log.error(`${tag} sync failed: ${sync.reason ?? "no reason given"} \xB7 continuing`);
|
|
1653
|
+
const left = spooledArtifactIds(deps.home ?? homeFromEnv());
|
|
1654
|
+
if (left.length > 0) {
|
|
1655
|
+
job.spooledArtifacts = left.slice(0, MAX_NAMED_SPOOLED);
|
|
1656
|
+
const named = job.spooledArtifacts.join(", ");
|
|
1657
|
+
log.error(
|
|
1658
|
+
`${tag} ${left.length} spooled artifact(s) not uploaded: ${named}${left.length > MAX_NAMED_SPOOLED ? `, and ${left.length - MAX_NAMED_SPOOLED} more` : ""}`
|
|
1659
|
+
);
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
const endBuildMicroVm = vmBuild === void 0 ? void 0 : async () => {
|
|
1664
|
+
if (job.workdir && job.sessionsSynced !== true) {
|
|
1665
|
+
await uploadSessions(job.workdir);
|
|
1666
|
+
job.sessionsSynced = true;
|
|
1667
|
+
writeJobFile(jobFilePath, job);
|
|
1668
|
+
}
|
|
1669
|
+
await stopBuildMicroVm(vmBuild.runtime, vmBuild.state, jobDirPath);
|
|
1670
|
+
};
|
|
1464
1671
|
if (job.checkContainer !== void 0 && job.checksRan === true) {
|
|
1465
1672
|
try {
|
|
1466
1673
|
await cleanupContainer(deps.container ?? createContainerRuntime(), job.checkContainer);
|
|
@@ -1470,6 +1677,15 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1470
1677
|
log.error(`${tag} removing an earlier attempt's check environment failed: ${describeCallFailure(err)} \xB7 continuing`);
|
|
1471
1678
|
}
|
|
1472
1679
|
}
|
|
1680
|
+
if (job.checkMicrovm !== void 0 && job.checksRan === true) {
|
|
1681
|
+
try {
|
|
1682
|
+
await cleanupMicroVm(deps.container ?? createContainerRuntime(), job.checkMicrovm);
|
|
1683
|
+
delete job.checkMicrovm;
|
|
1684
|
+
writeJobFile(jobFilePath, job);
|
|
1685
|
+
} catch (err) {
|
|
1686
|
+
log.error(`${tag} removing an earlier attempt's check environment failed: ${describeCallFailure(err)} \xB7 continuing`);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1473
1689
|
if (outcome === "ok" && !job.cleanupRan && job.checksRan !== true) {
|
|
1474
1690
|
setStage(job, jobDirPath, "checks");
|
|
1475
1691
|
job.checksRan = true;
|
|
@@ -1479,7 +1695,7 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1479
1695
|
config,
|
|
1480
1696
|
job,
|
|
1481
1697
|
jobDir: jobDirPath,
|
|
1482
|
-
deps: { fetchImpl: deps.fetchImpl, runtime: deps.container ?? createContainerRuntime(), log, now: deps.now, ...deps.hostChecks }
|
|
1698
|
+
deps: { fetchImpl: deps.fetchImpl, runtime: deps.container ?? createContainerRuntime(), log, now: deps.now, endBuildMicroVm, ...deps.hostChecks }
|
|
1483
1699
|
});
|
|
1484
1700
|
} catch (err) {
|
|
1485
1701
|
log.error(`${tag} the host's checks stopped: ${describeCallFailure(err)} \xB7 continuing`);
|
|
@@ -1504,26 +1720,8 @@ async function finishJob(job, jobDirPath, outcome, config, deps, ending) {
|
|
|
1504
1720
|
if (!job.cleanupRan) {
|
|
1505
1721
|
if (job.workdir) {
|
|
1506
1722
|
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
|
-
}
|
|
1723
|
+
if (job.sessionsSynced === true) log.line(`${tag} sync done before the build's microVM stopped`);
|
|
1724
|
+
else await uploadSessions(job.workdir);
|
|
1527
1725
|
}
|
|
1528
1726
|
setStage(job, jobDirPath, "cleanup");
|
|
1529
1727
|
try {
|
|
@@ -5,7 +5,7 @@ import { RepoFilter } from './chunk-TBT347UY.js';
|
|
|
5
5
|
import { hookExecSpoolPath } from './chunk-F7CU5ABH.js';
|
|
6
6
|
import { applyReportedCost } from './chunk-BKO6JEZI.js';
|
|
7
7
|
import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-ZYHKJKSU.js';
|
|
8
|
-
import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-
|
|
8
|
+
import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-GAYWY4GI.js';
|
|
9
9
|
import { C } from './chunk-YRYMNBGQ.js';
|
|
10
10
|
import { readLaunchSpoolState, launchStatusIsDecisive, launchedItemsBySession, launchesPath } from './chunk-GQD22KKN.js';
|
|
11
11
|
import { readLatestProvenanceForSession, readContextProvenance } from './chunk-ZJSNLVCE.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { runnerUnit, UNIT_MARKER } from './chunk-FBTE2A2X.js';
|
|
4
|
-
import { checkFit } from './chunk-
|
|
5
|
-
import { builtinKind } from './chunk-
|
|
4
|
+
import { checkFit } from './chunk-JKIWMRFH.js';
|
|
5
|
+
import { builtinKind } from './chunk-PWBILI5P.js';
|
|
6
6
|
import { runnerDir } from './chunk-ZM2IMMYN.js';
|
|
7
7
|
import { dirname } from 'path';
|
|
8
8
|
import { spawnSync } from 'child_process';
|
|
@@ -165,7 +165,11 @@ function installService(request, deps) {
|
|
|
165
165
|
function ensureLoginTask(deps, unitName) {
|
|
166
166
|
const tasks = deps.tasks;
|
|
167
167
|
const name = taskNameFor(unitName);
|
|
168
|
-
|
|
168
|
+
if (deps.distro === void 0 || deps.distro === "") {
|
|
169
|
+
const manual2 = `from Windows, run: schtasks /Create /F /TN ${name} /SC ONLOGON /TR "${taskCommand("<distribution>")}", with the name \`wsl -l\` shows`;
|
|
170
|
+
return [`WSL_DISTRO_NAME is not set, as under sudo, so the scheduled task that starts WSL2 at login was not made: ${manual2}`];
|
|
171
|
+
}
|
|
172
|
+
const command = taskCommand(deps.distro);
|
|
169
173
|
const manual = `from Windows, run: schtasks /Create /F /TN ${name} /SC ONLOGON /TR "${command}"`;
|
|
170
174
|
if (tasks === void 0 || !tasks.available()) {
|
|
171
175
|
return [`Windows interop is off, so the scheduled task that starts WSL2 at login was not made: ${manual}`];
|