@outerlayer/cli 0.10.2 → 0.11.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 +7 -0
- package/dist/build-info.json +1 -1
- package/dist/{builder-EVO6F2NO.js → builder-PP436VGZ.js} +2 -2
- package/dist/{check-NQYQHITZ.js → check-YDXOLVJ5.js} +16 -15
- package/dist/{chunk-VVIRHTH4.js → chunk-2GBCTEN4.js} +1 -1
- package/dist/{chunk-ERHJETKP.js → chunk-3YFCWYBU.js} +128 -30
- package/dist/{chunk-M6G7TJVF.js → chunk-4Q7OMKAT.js} +44 -4
- package/dist/{chunk-MC6J453K.js → chunk-7C4T5BGN.js} +2 -2
- package/dist/{chunk-472KC6JV.js → chunk-BEZSECPA.js} +1 -1
- package/dist/{chunk-KZ5M4QOB.js → chunk-BHOBM3IZ.js} +32 -14
- package/dist/{chunk-JSUUXA5W.js → chunk-CJGQQZWB.js} +1 -1
- package/dist/{chunk-BREEX2Z7.js → chunk-E5DC4VZL.js} +115 -58
- package/dist/{chunk-R2UYH4QV.js → chunk-EOWWTFUU.js} +3 -3
- package/dist/{chunk-PDTSUOVT.js → chunk-FI5UYJMQ.js} +1 -1
- package/dist/{chunk-V42JY4F6.js → chunk-HYZRY4KZ.js} +2 -2
- package/dist/chunk-O765JR4W.js +35 -0
- package/dist/{chunk-6CXYORUS.js → chunk-QKFB4K7X.js} +1 -1
- package/dist/{chunk-ESZJ5QIC.js → chunk-R7BJYFD2.js} +4 -4
- package/dist/{chunk-S6IUUNCD.js → chunk-RSNSZ2LJ.js} +1 -1
- package/dist/{chunk-NTLGWCWK.js → chunk-TFIOXPZT.js} +1 -1
- package/dist/{chunk-M2PBLRB7.js → chunk-XR3BJN33.js} +23 -3
- package/dist/{cli-ZCWMJCEK.js → cli-RQZXTOEI.js} +39 -35
- package/dist/{command-LNEFKIVC.js → command-U6UGIBVT.js} +9 -9
- package/dist/config-MKBX2ZQD.js +11 -0
- package/dist/{docker-GM526LGX.js → docker-WVRRMK6R.js} +3 -3
- package/dist/{doctor-QSV4DYT2.js → doctor-ZF4PYWVN.js} +10 -10
- package/dist/{exec-client-QTUO6TZT.js → exec-client-CQV746UF.js} +3 -3
- package/dist/{guest-init-4UW4HM6N.js → guest-init-LJP5GPZF.js} +3 -3
- package/dist/index.js +1 -1
- package/dist/{init-MJ2PO2VQ.js → init-M5IBAGVE.js} +9 -9
- package/dist/{init-OKCDXEOG.js → init-PUY2BUHH.js} +8 -8
- package/dist/{init-cmd-5TEM2QNH.js → init-cmd-NPNR23UL.js} +1 -1
- package/dist/{install-PYNCWSTV.js → install-HKOVISUP.js} +3 -3
- package/dist/{loop-DBF3UJAD.js → loop-3FNROLW7.js} +16 -15
- package/dist/{machine-TBJKZ6TN.js → machine-RWDDJRWB.js} +7 -7
- package/dist/pending-MXLSRYMO.js +7 -0
- package/dist/{status-KPDVZMJ6.js → status-2OW5AKWL.js} +17 -9
- package/dist/{work-claim-cmd-DYD6II56.js → work-claim-cmd-LXXSNEGF.js} +3 -3
- package/dist/{work-open-pr-cmd-N2RBDKZ4.js → work-open-pr-cmd-YZAENDP6.js} +5 -4
- package/package.json +1 -1
- package/skill-pack/maintained/amend/SKILL.md +10 -1
- package/skill-pack/maintained/outerlayer/SKILL.md +19 -3
- package/skill-pack/maintained/outerlayer/references/open-pull-request.md +11 -0
- package/dist/config-X45X7AS4.js +0 -11
|
@@ -3,17 +3,17 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import { readDefaultBranchHead, cloneRepository, buildRepositoryImage } from './chunk-3FMPZPJQ.js';
|
|
4
4
|
import { startTimeOf, isSameProcess } from './chunk-N5FOE5PS.js';
|
|
5
5
|
import { currentCliBuild } from './chunk-OGFGZQA3.js';
|
|
6
|
-
import { createDockerHost, childEnvironment, spawnRun } from './chunk-
|
|
6
|
+
import { createDockerHost, childEnvironment, spawnRun } from './chunk-2GBCTEN4.js';
|
|
7
7
|
import { RecipeError } from './chunk-QOZKPLVJ.js';
|
|
8
|
-
import { builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, containerRunArgs, containerExecArgs } from './chunk-
|
|
8
|
+
import { builderSpec, runnerLabelValue, RUNNER_LABEL, BUILD_LABEL, prepareRunArgs, containerRunArgs, containerExecArgs } from './chunk-CJGQQZWB.js';
|
|
9
9
|
import { CLAUDE_CODE_VERSION, firecrackerArch, MICROVM_ARCHES, DEFAULT_BASE_IMAGE, GUEST_HELPER_SHA256, GUEST_ASSETS_BASE_URL, GUEST_KERNEL_SHA256, FIRECRACKER_BINARY_SHA256, FIRECRACKER_SHA256, FIRECRACKER_VERSION, GUEST_ASSETS_VERSION } from './chunk-CX22S5BA.js';
|
|
10
|
-
import { execCapture } from './chunk-
|
|
11
|
-
import { GUEST_CID } from './chunk-
|
|
10
|
+
import { execCapture } from './chunk-QKFB4K7X.js';
|
|
11
|
+
import { GUEST_CID } from './chunk-RSNSZ2LJ.js';
|
|
12
12
|
import { WorkCommandError, postJson } from './chunk-DTDZXDQ3.js';
|
|
13
13
|
import { setCustomScrubbers, setLocalIdentity, setRepoRoot, parseTranscript, scrubDeep, scrubText } from './chunk-IU4NZROR.js';
|
|
14
14
|
import { hostAllowed } from './chunk-UXYLWVTQ.js';
|
|
15
15
|
import { parseDuKilobytes } from './chunk-7RQ73DLO.js';
|
|
16
|
-
import { RELAY_PORT, CHECKOUT_DIR, JOB_MOUNT, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING } from './chunk-
|
|
16
|
+
import { repositoriesOf, primaryOf, RELAY_PORT, CHECKOUT_DIR, JOB_MOUNT, claudeCredential, buildEnvironment, startBuildServices, checkEnvironment, AGENT_CREDENTIAL_MISSING, checkoutPath } from './chunk-4Q7OMKAT.js';
|
|
17
17
|
import { defaultGitRunner, materializeContext } from './chunk-RWQWPZB4.js';
|
|
18
18
|
import { readContextManifest, readSourceCache } from './chunk-BMX3L7CR.js';
|
|
19
19
|
import { orderSessions, extractFacts, sessionsToTimeline } from './chunk-TB7EP24C.js';
|
|
@@ -133,6 +133,9 @@ function refusalCode(err) {
|
|
|
133
133
|
if (!(err instanceof WorkCommandError)) return "unavailable";
|
|
134
134
|
return err.code ?? `http_${err.status ?? "unknown"}`;
|
|
135
135
|
}
|
|
136
|
+
function keyOf(kind, repository) {
|
|
137
|
+
return repository === void 0 ? kind : `${kind}:${repository}`;
|
|
138
|
+
}
|
|
136
139
|
var BuildTokens = class {
|
|
137
140
|
constructor(options) {
|
|
138
141
|
this.options = options;
|
|
@@ -146,23 +149,35 @@ var BuildTokens = class {
|
|
|
146
149
|
now() {
|
|
147
150
|
return (this.options.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
148
151
|
}
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
/** `repository` as the holder keys it: `undefined` for the primary, which is the one a request names no repository for. */
|
|
153
|
+
secondary(repository) {
|
|
154
|
+
if (repository === void 0) return void 0;
|
|
155
|
+
const primary = this.options.primary;
|
|
156
|
+
return primary !== void 0 && primary.toLowerCase() === repository.toLowerCase() ? void 0 : repository;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The token of `kind` for `repository`, or for the primary when none is
|
|
160
|
+
* named, asking the gateway for a new one when the build holds none or its
|
|
161
|
+
* token is within ten minutes of expiry.
|
|
162
|
+
*/
|
|
163
|
+
token(kind, repository) {
|
|
164
|
+
const other = this.secondary(repository);
|
|
165
|
+
const key = keyOf(kind, other);
|
|
166
|
+
const held = this.held.get(key);
|
|
152
167
|
if (held !== void 0 && !isDue(held.expiresAtMs, this.now().getTime())) return Promise.resolve(held.token);
|
|
153
|
-
let asking = this.asking.get(
|
|
168
|
+
let asking = this.asking.get(key);
|
|
154
169
|
if (asking === void 0) {
|
|
155
|
-
asking = this.issueOrKeep(kind).finally(() => this.asking.delete(
|
|
156
|
-
this.asking.set(
|
|
170
|
+
asking = this.issueOrKeep(kind, other).finally(() => this.asking.delete(key));
|
|
171
|
+
this.asking.set(key, asking);
|
|
157
172
|
}
|
|
158
173
|
return asking;
|
|
159
174
|
}
|
|
160
175
|
/** A due token that has not yet expired is kept when the gateway cannot issue a new one. */
|
|
161
|
-
async issueOrKeep(kind) {
|
|
176
|
+
async issueOrKeep(kind, repository) {
|
|
162
177
|
try {
|
|
163
|
-
return await this.issue(kind);
|
|
178
|
+
return await this.issue(kind, repository);
|
|
164
179
|
} catch (err) {
|
|
165
|
-
const held = this.held.get(kind);
|
|
180
|
+
const held = this.held.get(keyOf(kind, repository));
|
|
166
181
|
if (held !== void 0 && this.now().getTime() < held.expiresAtMs && classifyRefusal(err).kind === "retry") return held.token;
|
|
167
182
|
throw err;
|
|
168
183
|
}
|
|
@@ -221,8 +236,8 @@ var BuildTokens = class {
|
|
|
221
236
|
this.issued.push(token);
|
|
222
237
|
return { token, expiresAt, repository };
|
|
223
238
|
}
|
|
224
|
-
async issue(kind) {
|
|
225
|
-
const asked = await this.ask({ access: kind });
|
|
239
|
+
async issue(kind, repository) {
|
|
240
|
+
const asked = await this.ask({ access: kind, ...repository === void 0 ? {} : { repository } });
|
|
226
241
|
let err;
|
|
227
242
|
if (!asked.ok) {
|
|
228
243
|
err = new WorkCommandError(asked.message ?? asked.reason, asked.retryable, asked.status, asked.code);
|
|
@@ -234,9 +249,9 @@ var BuildTokens = class {
|
|
|
234
249
|
await this.revoke([token]);
|
|
235
250
|
throw new WorkCommandError("the build's tokens were already revoked", false, void 0, "tokens_revoked");
|
|
236
251
|
}
|
|
237
|
-
this.held.set(kind, { token, expiresAtMs });
|
|
252
|
+
this.held.set(keyOf(kind, repository), { token, expiresAtMs });
|
|
238
253
|
this.issued.push(token);
|
|
239
|
-
if (kind === "read") this.readSink?.(token);
|
|
254
|
+
if (kind === "read" && repository === void 0) this.readSink?.(token);
|
|
240
255
|
return token;
|
|
241
256
|
}
|
|
242
257
|
err = new WorkCommandError("the gateway's token answer carried no token or no expiry", false, void 0, "malformed_token_response");
|
|
@@ -935,31 +950,45 @@ function adoptionFailureReason(result) {
|
|
|
935
950
|
}
|
|
936
951
|
return `context_adoption_failed: adopting the control plane's context failed (exit ${result.status})${firstLine3(result.stderr) === "" ? "" : `: ${firstLine3(result.stderr)}`}`;
|
|
937
952
|
}
|
|
938
|
-
async function
|
|
939
|
-
const { exec, log, tag
|
|
940
|
-
|
|
941
|
-
const
|
|
942
|
-
if (rewrite.status !== 0) return { ok: false, reason: `configuring the build's git remote failed (exit ${rewrite.status})` };
|
|
943
|
-
const clone = await exec(["git", "clone", "--filter=blob:none", "--single-branch", `https://github.com/${repository}.git`, CHECKOUT_DIR], "/build/work");
|
|
953
|
+
async function checkOut(input, entry) {
|
|
954
|
+
const { exec, log, tag } = input;
|
|
955
|
+
const path = checkoutPath(entry);
|
|
956
|
+
const clone = await exec(["git", "clone", "--filter=blob:none", "--single-branch", `https://github.com/${entry.repository}.git`, path], "/build/work");
|
|
944
957
|
if (clone.status !== 0) {
|
|
945
958
|
const detail = gitErrorLine(clone.stderr);
|
|
946
|
-
return { ok: false, reason: `the clone of ${repository} failed (exit ${clone.status})${detail === "" ? "" : `: ${detail}`}` };
|
|
959
|
+
return { ok: false, reason: `the clone of ${entry.repository} failed (exit ${clone.status})${detail === "" ? "" : `: ${detail}`}` };
|
|
947
960
|
}
|
|
948
961
|
try {
|
|
949
962
|
const prepared = await prepareBranch({
|
|
950
|
-
workdir:
|
|
951
|
-
branch:
|
|
963
|
+
workdir: path,
|
|
964
|
+
branch: entry.branch,
|
|
952
965
|
git: async (cwd, args) => {
|
|
953
966
|
const ran = await exec(["git", ...args], cwd, { quiet: true });
|
|
954
967
|
return { status: ran.status, stdout: ran.stdout.trim(), stderr: ran.stderr.trim() };
|
|
955
968
|
}
|
|
956
969
|
});
|
|
957
|
-
|
|
958
|
-
|
|
970
|
+
log.line(`${tag} ${entry.primary ? "" : `${entry.repository} `}on ${prepared.branch} at ${prepared.head.slice(0, 8)}`);
|
|
971
|
+
return { ok: true, startCommit: prepared.startCommit };
|
|
959
972
|
} catch (err) {
|
|
960
|
-
if (err instanceof BranchPrepareError) return { ok: false, reason: err.message };
|
|
973
|
+
if (err instanceof BranchPrepareError) return { ok: false, reason: entry.primary ? err.message : `${entry.repository}: ${err.message}` };
|
|
961
974
|
throw err;
|
|
962
975
|
}
|
|
976
|
+
}
|
|
977
|
+
async function runBuildSteps(input) {
|
|
978
|
+
const { exec, log, tag, repository } = input;
|
|
979
|
+
let startCommit = "";
|
|
980
|
+
const repositories = repositoriesOf({ repositories: input.repositories, repository, branch: input.branch });
|
|
981
|
+
const primary = primaryOf(repositories) ?? { repository, branch: input.branch, primary: true };
|
|
982
|
+
const rewrite = await exec(["git", "config", "--global", `url.http://127.0.0.1:${RELAY_PORT}/github.com/.insteadOf`, "https://github.com/"], "/build/work");
|
|
983
|
+
if (rewrite.status !== 0) return { ok: false, reason: `configuring the build's git remote failed (exit ${rewrite.status})` };
|
|
984
|
+
const first = await checkOut(input, { ...primary, primary: true });
|
|
985
|
+
if (!first.ok) return first;
|
|
986
|
+
startCommit = first.startCommit;
|
|
987
|
+
for (const entry of repositories.filter((candidate) => candidate.repository !== primary.repository)) {
|
|
988
|
+
if (input.aborted()) return aborted();
|
|
989
|
+
const checked = await checkOut(input, { ...entry, primary: false });
|
|
990
|
+
if (!checked.ok) return checked;
|
|
991
|
+
}
|
|
963
992
|
for (const step of lifecycleSteps(input.lifecycle)) {
|
|
964
993
|
if (input.aborted()) return aborted();
|
|
965
994
|
log.line(`${tag} lifecycle ${step.name} (${step.source})`);
|
|
@@ -1639,6 +1668,7 @@ function readBody(req, limit) {
|
|
|
1639
1668
|
}
|
|
1640
1669
|
function createGitRemote(options) {
|
|
1641
1670
|
const gitUrl = options.gitUrl ?? "https://github.com";
|
|
1671
|
+
const claimed = options.repositories ?? (options.repository === void 0 || options.branch === void 0 || options.mirrorDir === void 0 ? [] : [{ repository: options.repository, branch: options.branch, mirrorDir: options.mirrorDir }]);
|
|
1642
1672
|
const maxLfsBodyBytes = 1024 * 1024;
|
|
1643
1673
|
const hopByHop = /* @__PURE__ */ new Set(["connection", "keep-alive", "transfer-encoding", "upgrade", "proxy-authenticate", "proxy-authorization", "te", "trailer", "set-cookie"]);
|
|
1644
1674
|
const log = (line) => options.log?.(line);
|
|
@@ -1685,9 +1715,9 @@ function createGitRemote(options) {
|
|
|
1685
1715
|
input.feed(upstream);
|
|
1686
1716
|
});
|
|
1687
1717
|
}
|
|
1688
|
-
async function tokenOrRefuse(res, kind) {
|
|
1718
|
+
async function tokenOrRefuse(res, kind, repository) {
|
|
1689
1719
|
try {
|
|
1690
|
-
return await options.tokens.token(kind);
|
|
1720
|
+
return await options.tokens.token(kind, repository);
|
|
1691
1721
|
} catch (err) {
|
|
1692
1722
|
log(`could not get a ${kind} token: ${refusalCode(err)}`);
|
|
1693
1723
|
plain(res, 502, `the runner could not get a ${kind} token: ${refusalCode(err)}
|
|
@@ -1695,7 +1725,7 @@ function createGitRemote(options) {
|
|
|
1695
1725
|
return void 0;
|
|
1696
1726
|
}
|
|
1697
1727
|
}
|
|
1698
|
-
async function push(req, res, route) {
|
|
1728
|
+
async function push(req, res, route, target) {
|
|
1699
1729
|
const encoding = req.headers["content-encoding"];
|
|
1700
1730
|
const gzip = encoding === "gzip";
|
|
1701
1731
|
const { held, section } = await readPrefix(req, gzip);
|
|
@@ -1720,8 +1750,8 @@ function createGitRemote(options) {
|
|
|
1720
1750
|
let bodyPath;
|
|
1721
1751
|
try {
|
|
1722
1752
|
let fastForward;
|
|
1723
|
-
if (needsFastForwardCheck(commands,
|
|
1724
|
-
const read = await tokenOrRefuse(res, "read");
|
|
1753
|
+
if (needsFastForwardCheck(commands, target.branch)) {
|
|
1754
|
+
const read = await tokenOrRefuse(res, "read", target.repository);
|
|
1725
1755
|
if (read === void 0) return void await drain(req);
|
|
1726
1756
|
mkdirSync(options.spoolDir, { recursive: true, mode: 448 });
|
|
1727
1757
|
bodyPath = join(options.spoolDir, randomBytes(8).toString("hex"));
|
|
@@ -1742,21 +1772,21 @@ function createGitRemote(options) {
|
|
|
1742
1772
|
if (size > cap) return void await refuseInBand(`the push is larger than the ${cap} bytes the runner holds to check it`);
|
|
1743
1773
|
try {
|
|
1744
1774
|
fastForward = await isFastForward({
|
|
1745
|
-
mirrorDir:
|
|
1775
|
+
mirrorDir: target.mirrorDir,
|
|
1746
1776
|
remoteUrl: `${gitUrl}/${route.owner}/${route.name}.git`,
|
|
1747
1777
|
token: read,
|
|
1748
|
-
branch:
|
|
1778
|
+
branch: target.branch,
|
|
1749
1779
|
bodyPath,
|
|
1750
1780
|
gzip
|
|
1751
1781
|
});
|
|
1752
1782
|
} catch (err) {
|
|
1753
1783
|
log(`fast-forward check could not run: ${err instanceof Error ? err.message : String(err)}`);
|
|
1754
|
-
return void await refuseInBand(`the runner could not check whether this push fast-forwards ${
|
|
1784
|
+
return void await refuseInBand(`the runner could not check whether this push fast-forwards ${target.branch}`);
|
|
1755
1785
|
}
|
|
1756
1786
|
}
|
|
1757
|
-
const verdict = judgePush({ commands, branch:
|
|
1787
|
+
const verdict = judgePush({ commands, branch: target.branch, fastForward });
|
|
1758
1788
|
if (!verdict.allowed) return void await refuseInBand(verdict.message);
|
|
1759
|
-
const token = await tokenOrRefuse(res, "push");
|
|
1789
|
+
const token = await tokenOrRefuse(res, "push", target.repository);
|
|
1760
1790
|
if (token === void 0) return void await drain(req);
|
|
1761
1791
|
options.tokens.markUsed("push");
|
|
1762
1792
|
const headers = upstreamHeaders(req.headers, basicAuthorization(token));
|
|
@@ -1793,8 +1823,15 @@ function createGitRemote(options) {
|
|
|
1793
1823
|
req.resume();
|
|
1794
1824
|
return plain(res, 403, "this remote is read-only: a check cannot push\n");
|
|
1795
1825
|
}
|
|
1796
|
-
const
|
|
1797
|
-
|
|
1826
|
+
const target = claimed.find((entry) => isOwnRepository(entry.repository, route.owner, route.name));
|
|
1827
|
+
const pushing = route.kind === "receive-pack" || route.kind === "info-refs" && route.service === "git-receive-pack";
|
|
1828
|
+
if (target === void 0 && pushing) {
|
|
1829
|
+
log(`push refused: ${route.owner}/${route.name} is not a repository of this build's claim`);
|
|
1830
|
+
req.resume();
|
|
1831
|
+
return plain(res, 403, `the runner refused this push: ${route.owner}/${route.name} is not a repository of this build's claim
|
|
1832
|
+
`);
|
|
1833
|
+
}
|
|
1834
|
+
if (target !== void 0 && route.kind === "receive-pack") return push(req, res, route, target);
|
|
1798
1835
|
let kind = route.kind === "info-refs" && route.service === "git-receive-pack" ? "push" : "read";
|
|
1799
1836
|
let body;
|
|
1800
1837
|
if (route.kind === "lfs-batch") {
|
|
@@ -1804,8 +1841,8 @@ function createGitRemote(options) {
|
|
|
1804
1841
|
if (options.readOnly === true && kind === "push") return plain(res, 403, "this remote is read-only: a check cannot upload\n");
|
|
1805
1842
|
}
|
|
1806
1843
|
let authorization;
|
|
1807
|
-
if (
|
|
1808
|
-
const token = await tokenOrRefuse(res, kind);
|
|
1844
|
+
if (target !== void 0) {
|
|
1845
|
+
const token = await tokenOrRefuse(res, kind, target.repository);
|
|
1809
1846
|
if (token === void 0) return void await drain(req);
|
|
1810
1847
|
authorization = basicAuthorization(token);
|
|
1811
1848
|
if (kind === "read" || route.kind === "lfs-batch") options.tokens.markUsed(kind);
|
|
@@ -2268,14 +2305,14 @@ function keepGhConfigCurrent(tokens, buildDir2, onError) {
|
|
|
2268
2305
|
});
|
|
2269
2306
|
}
|
|
2270
2307
|
function gitRemoteFor(runtime, input) {
|
|
2271
|
-
const [owner = "", name = ""] = input.repository.split("/");
|
|
2272
2308
|
const runnerDirPath = runnerDirOfBuild(input.buildDir);
|
|
2273
2309
|
return createGitRemote({
|
|
2274
|
-
|
|
2275
|
-
|
|
2310
|
+
repositories: input.repositories.map((entry) => {
|
|
2311
|
+
const [owner = "", name = ""] = entry.repository.split("/");
|
|
2312
|
+
return { repository: entry.repository, branch: entry.branch, mirrorDir: mirrorDir(runnerDirPath, owner, name) };
|
|
2313
|
+
}),
|
|
2276
2314
|
tokens: input.tokens,
|
|
2277
2315
|
gitUrl: runtime.github.gitUrl,
|
|
2278
|
-
mirrorDir: mirrorDir(runnerDirPath, owner, name),
|
|
2279
2316
|
spoolDir: spoolDir(runnerDirPath),
|
|
2280
2317
|
onWorkflowRefused: () => {
|
|
2281
2318
|
input.tokens.reason = WORKFLOW_PERMISSION_REASON;
|
|
@@ -2349,10 +2386,10 @@ async function provisionContainer(input) {
|
|
|
2349
2386
|
const logPath = join(jobDir, "provision.log");
|
|
2350
2387
|
const fail = (reason) => ({ ok: false, reason });
|
|
2351
2388
|
if (claudeCredential(runtime.hostEnv) === void 0) return fail(AGENT_CREDENTIAL_MISSING);
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2389
|
+
const repositories = repositoriesOf({ repositories: input.repositories, repository: item.specRepository, branch: input.branch });
|
|
2390
|
+
const primary = primaryOf(repositories);
|
|
2391
|
+
if (primary === void 0) return fail("the work item names no repository to clone");
|
|
2392
|
+
const repository = primary.repository;
|
|
2356
2393
|
const engine = await host.version();
|
|
2357
2394
|
if (engine === null) return fail("Docker is not available: `docker version` failed");
|
|
2358
2395
|
if (!(Number.parseInt(engine, 10) >= MIN_ENGINE_MAJOR)) {
|
|
@@ -2385,6 +2422,7 @@ async function provisionContainer(input) {
|
|
|
2385
2422
|
item: item.number,
|
|
2386
2423
|
itemKey,
|
|
2387
2424
|
repository,
|
|
2425
|
+
repositories,
|
|
2388
2426
|
hostEnv: runtime.hostEnv,
|
|
2389
2427
|
containerEnv: report.apply.containerEnv,
|
|
2390
2428
|
builtIns: true
|
|
@@ -2421,11 +2459,19 @@ async function provisionContainer(input) {
|
|
|
2421
2459
|
err instanceof WorkCommandError ? `the build's read token could not be had: ${refusalCode(err)}` : `the build's gh configuration could not be written: ${describeCallFailure(err)}`
|
|
2422
2460
|
);
|
|
2423
2461
|
}
|
|
2462
|
+
for (const entry of repositories) {
|
|
2463
|
+
if (entry.repository === primary.repository) continue;
|
|
2464
|
+
try {
|
|
2465
|
+
await tokens.token("read", entry.repository);
|
|
2466
|
+
} catch (err) {
|
|
2467
|
+
return bail(`the build's read token for ${entry.repository} could not be had: ${refusalCode(err)}`);
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2424
2470
|
keepGhConfigCurrent(tokens, buildDir2, (err) => log.error(`${tag} the read token could not be written for gh: ${describeCallFailure(err)}`));
|
|
2425
2471
|
tokens.markUsed("read");
|
|
2426
2472
|
environment.credentials = tokens.used();
|
|
2427
2473
|
try {
|
|
2428
|
-
const gitRemote = gitRemoteFor(runtime, {
|
|
2474
|
+
const gitRemote = gitRemoteFor(runtime, { repositories, tokens, buildDir: buildDir2, log: (line) => log.line(`${tag} ${line}`) });
|
|
2429
2475
|
runtime.tunnels.set(buildId, await startServicesFor(runtime, config, buildDir2, jobDir, itemKey, true, gitRemote));
|
|
2430
2476
|
} catch (err) {
|
|
2431
2477
|
return bail(`the tunnel could not start: ${describeCallFailure(err)}`);
|
|
@@ -2476,7 +2522,8 @@ async function provisionContainer(input) {
|
|
|
2476
2522
|
const exec = (argv, workdir, opts) => host.exec({ container: name, user: agentUser, workdir, env, argv }, opts?.quiet === true ? void 0 : { logPath });
|
|
2477
2523
|
const steps = await runBuildSteps({
|
|
2478
2524
|
repository,
|
|
2479
|
-
branch:
|
|
2525
|
+
branch: primary.branch,
|
|
2526
|
+
repositories,
|
|
2480
2527
|
lifecycle: report.apply.lifecycle,
|
|
2481
2528
|
exec,
|
|
2482
2529
|
log,
|
|
@@ -2519,6 +2566,7 @@ function containerCommandLine(input) {
|
|
|
2519
2566
|
itemKey: input.job.itemKey,
|
|
2520
2567
|
branch: input.job.branch,
|
|
2521
2568
|
repository: state.repository,
|
|
2569
|
+
repositories: input.job.repositories,
|
|
2522
2570
|
hostEnv: input.runtime.hostEnv,
|
|
2523
2571
|
containerEnv: state.containerEnv ?? {},
|
|
2524
2572
|
builtIns: state.builtIns === true,
|
|
@@ -2544,6 +2592,7 @@ function containerSenderCommand(input) {
|
|
|
2544
2592
|
itemKey: input.job.itemKey,
|
|
2545
2593
|
branch: input.job.branch,
|
|
2546
2594
|
repository: state.repository,
|
|
2595
|
+
repositories: input.job.repositories,
|
|
2547
2596
|
hostEnv: input.runtime.hostEnv,
|
|
2548
2597
|
containerEnv: state.containerEnv ?? {},
|
|
2549
2598
|
builtIns: state.builtIns === true,
|
|
@@ -2597,17 +2646,18 @@ async function startServicesFor(runtime, config, buildDir2, jobDir, itemKey, bui
|
|
|
2597
2646
|
destinationOptions: runtime.destinationOptions
|
|
2598
2647
|
});
|
|
2599
2648
|
}
|
|
2600
|
-
async function ensureTunnel(runtime, state, jobDir, config, itemKey, branch) {
|
|
2649
|
+
async function ensureTunnel(runtime, state, jobDir, config, itemKey, branch, repositories) {
|
|
2601
2650
|
if (runtime.tunnels.has(state.buildId)) return;
|
|
2602
2651
|
const tokens = runtime.credentials.get(jobDir);
|
|
2603
|
-
const
|
|
2652
|
+
const served = repositoriesOf({ repositories, repository: state.repository, branch });
|
|
2653
|
+
const gitRemote = tokens === void 0 || served.length === 0 ? void 0 : gitRemoteFor(runtime, { repositories: served, tokens, buildDir: state.buildDir });
|
|
2604
2654
|
runtime.tunnels.set(state.buildId, await startServicesFor(runtime, config, state.buildDir, jobDir, itemKey, state.builtIns === true, gitRemote));
|
|
2605
2655
|
}
|
|
2606
2656
|
async function syncInContainer(input) {
|
|
2607
2657
|
const { runtime, job } = input;
|
|
2608
2658
|
const state = job.container;
|
|
2609
2659
|
try {
|
|
2610
|
-
await ensureTunnel(runtime, state, input.jobDir, input.config, job.itemKey, job.branch);
|
|
2660
|
+
await ensureTunnel(runtime, state, input.jobDir, input.config, job.itemKey, job.branch, job.repositories);
|
|
2611
2661
|
if (state.stopped) {
|
|
2612
2662
|
await runtime.host.start(state.container);
|
|
2613
2663
|
state.stopped = false;
|
|
@@ -2619,6 +2669,7 @@ async function syncInContainer(input) {
|
|
|
2619
2669
|
itemKey: job.itemKey,
|
|
2620
2670
|
branch: job.branch,
|
|
2621
2671
|
repository: state.repository,
|
|
2672
|
+
repositories: job.repositories,
|
|
2622
2673
|
hostEnv: runtime.hostEnv,
|
|
2623
2674
|
containerEnv: state.containerEnv ?? {},
|
|
2624
2675
|
builtIns: state.builtIns === true,
|
|
@@ -2711,7 +2762,13 @@ async function startCheckContainer(input) {
|
|
|
2711
2762
|
mkdirSync(buildDir2, { recursive: true });
|
|
2712
2763
|
chmodSync(buildDir2, 493);
|
|
2713
2764
|
try {
|
|
2714
|
-
const gitRemote = gitRemoteFor(runtime, {
|
|
2765
|
+
const gitRemote = gitRemoteFor(runtime, {
|
|
2766
|
+
repositories: repositoriesOf({ repositories: job.repositories, repository, branch: job.branch }),
|
|
2767
|
+
tokens,
|
|
2768
|
+
buildDir: buildDir2,
|
|
2769
|
+
log: (line) => log.line(`${tag} ${line}`),
|
|
2770
|
+
readOnly: true
|
|
2771
|
+
});
|
|
2715
2772
|
runtime.tunnels.set(buildId, await startServicesFor(runtime, config, buildDir2, checksDir, void 0, false, gitRemote));
|
|
2716
2773
|
} catch (err) {
|
|
2717
2774
|
return fail(`the tunnel could not start: ${describeCallFailure(err)}`);
|
|
@@ -1004,7 +1004,7 @@ function doctorExitCode(checks) {
|
|
|
1004
1004
|
|
|
1005
1005
|
// src/doctor-report.ts
|
|
1006
1006
|
async function gatherDoctorChecks() {
|
|
1007
|
-
const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-
|
|
1007
|
+
const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-MKBX2ZQD.js');
|
|
1008
1008
|
const configPath = defaultRunnerConfigPath();
|
|
1009
1009
|
const checks = runDoctor({ runnerHost: hasRunnerBlock(configPath) });
|
|
1010
1010
|
const credential = await credentialChecks();
|
|
@@ -1016,7 +1016,7 @@ async function gatherDoctorChecks() {
|
|
|
1016
1016
|
const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
|
|
1017
1017
|
checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
|
|
1018
1018
|
} else if (hasRunnerBlock(configPath)) {
|
|
1019
|
-
const { runnerHostChecks } = await import('./doctor-
|
|
1019
|
+
const { runnerHostChecks } = await import('./doctor-ZF4PYWVN.js');
|
|
1020
1020
|
const hooks = (() => {
|
|
1021
1021
|
try {
|
|
1022
1022
|
return loadRunnerConfig(configPath).hooks;
|
|
@@ -1051,7 +1051,7 @@ async function gatherDoctorChecks() {
|
|
|
1051
1051
|
const { existsSync: existsSync2 } = await import('fs');
|
|
1052
1052
|
if (process.platform !== "linux" || !existsSync2("/run/systemd/system")) return void 0;
|
|
1053
1053
|
const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
|
|
1054
|
-
const { DEFAULT_UNIT_NAME } = await import('./install-
|
|
1054
|
+
const { DEFAULT_UNIT_NAME } = await import('./install-HKOVISUP.js');
|
|
1055
1055
|
const { dirname: dirname2 } = await import('path');
|
|
1056
1056
|
return inspectService(DEFAULT_UNIT_NAME, dirname2(configPath), realInspectProbes());
|
|
1057
1057
|
})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { EXEC_PORT } from './chunk-
|
|
4
|
-
import { RELAY_PORT } from './chunk-
|
|
3
|
+
import { EXEC_PORT } from './chunk-RSNSZ2LJ.js';
|
|
4
|
+
import { RELAY_PORT } from './chunk-4Q7OMKAT.js';
|
|
5
5
|
import { spawn, spawnSync } from 'child_process';
|
|
6
6
|
import { writeFileSync, chmodSync, mkdirSync } from 'fs';
|
|
7
7
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { RUNNER_DIR_MODE, RUNNER_FILE_MODE } from './chunk-ZM2IMMYN.js';
|
|
4
|
+
import { readFileSync, existsSync, rmSync, mkdirSync, writeFileSync, renameSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
|
|
7
|
+
createRequire(import.meta.url);
|
|
8
|
+
var FILE = "update-pending.json";
|
|
9
|
+
function readPendingUpdate(runnerDirPath) {
|
|
10
|
+
try {
|
|
11
|
+
const raw = JSON.parse(readFileSync(join(runnerDirPath, FILE), "utf8"));
|
|
12
|
+
if (typeof raw.version !== "string" || typeof raw.since !== "string") return void 0;
|
|
13
|
+
const record = { version: raw.version, since: raw.since };
|
|
14
|
+
const holding = raw.holding;
|
|
15
|
+
if (holding && Array.isArray(holding.builds) && typeof holding.since === "string") {
|
|
16
|
+
record.holding = { builds: holding.builds.filter((n) => typeof n === "number"), since: holding.since };
|
|
17
|
+
}
|
|
18
|
+
return record;
|
|
19
|
+
} catch {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function writePendingUpdate(runnerDirPath, record) {
|
|
24
|
+
const path = join(runnerDirPath, FILE);
|
|
25
|
+
if (record === void 0) {
|
|
26
|
+
if (existsSync(path)) rmSync(path);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
mkdirSync(runnerDirPath, { recursive: true, mode: RUNNER_DIR_MODE });
|
|
30
|
+
const temp = `${path}.partial-${process.pid}`;
|
|
31
|
+
writeFileSync(temp, JSON.stringify(record, null, 2), { mode: RUNNER_FILE_MODE });
|
|
32
|
+
renameSync(temp, path);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { readPendingUpdate, writePendingUpdate };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { isPlaceholderCommand } from './chunk-
|
|
4
|
-
import { describeCallFailure } from './chunk-
|
|
3
|
+
import { isPlaceholderCommand } from './chunk-7C4T5BGN.js';
|
|
4
|
+
import { describeCallFailure } from './chunk-E5DC4VZL.js';
|
|
5
5
|
import { WorkCommandError, getJson } from './chunk-DTDZXDQ3.js';
|
|
6
6
|
import { sanitizeTerminal } from './chunk-VDI5POCC.js';
|
|
7
|
-
import { builtinKind } from './chunk-
|
|
8
|
-
import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-
|
|
7
|
+
import { builtinKind } from './chunk-XR3BJN33.js';
|
|
8
|
+
import { CLAUDE_CREDENTIAL_VARIABLES, claudeCredential } from './chunk-4Q7OMKAT.js';
|
|
9
9
|
import { signerFor } from './chunk-V7NIVOFD.js';
|
|
10
10
|
|
|
11
11
|
createRequire(import.meta.url);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { RELAY_PORT, GATEWAY_SOCKET, GATEWAY_PORT, CLAUDE_SOCKET, CLAUDE_PORT, DESTINATION_BASE_PORT } from './chunk-
|
|
3
|
+
import { RELAY_PORT, GATEWAY_SOCKET, GATEWAY_PORT, CLAUDE_SOCKET, CLAUDE_PORT, DESTINATION_BASE_PORT } from './chunk-4Q7OMKAT.js';
|
|
4
4
|
import { symlinkSync, unlinkSync } from 'fs';
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { runnerUnit, UNIT_MARKER } from './chunk-FBTE2A2X.js';
|
|
4
4
|
import { checkFit } from './chunk-JKIWMRFH.js';
|
|
5
|
-
import { builtinKind } from './chunk-
|
|
5
|
+
import { builtinKind } from './chunk-XR3BJN33.js';
|
|
6
6
|
import { runnerDir } from './chunk-ZM2IMMYN.js';
|
|
7
7
|
import { dirname } from 'path';
|
|
8
8
|
import { spawnSync } from 'child_process';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { MAX_ALLOW_HOSTS, allowHostProblem } from './chunk-UXYLWVTQ.js';
|
|
4
4
|
import { parseSize } from './chunk-7RQ73DLO.js';
|
|
5
|
-
import { isRefusedInConfig, resolveDestinations, isReservedVariable } from './chunk-
|
|
5
|
+
import { isRefusedInConfig, resolveDestinations, isReservedVariable } from './chunk-4Q7OMKAT.js';
|
|
6
6
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
7
7
|
import { existsSync, statSync, accessSync, constants, readFileSync } from 'fs';
|
|
8
8
|
import { hostname } from 'os';
|
|
@@ -86,8 +86,11 @@ var RUNNER_KEYS = [
|
|
|
86
86
|
"build",
|
|
87
87
|
"housekeeping",
|
|
88
88
|
"checks",
|
|
89
|
-
"autoUpdate"
|
|
89
|
+
"autoUpdate",
|
|
90
|
+
"update"
|
|
90
91
|
];
|
|
92
|
+
var UPDATE_KEYS = ["maxWaitHours"];
|
|
93
|
+
var DEFAULT_UPDATE_MAX_WAIT_HOURS = 6;
|
|
91
94
|
function runnerNumbers() {
|
|
92
95
|
return [
|
|
93
96
|
{ key: "concurrency", min: 1 },
|
|
@@ -205,6 +208,7 @@ function loadRunnerConfig(configPath) {
|
|
|
205
208
|
const build = resolveBuild(configPath, runner.build);
|
|
206
209
|
const housekeeping = resolveHousekeeping(configPath, runner.housekeeping, build.disk);
|
|
207
210
|
const checks = resolveChecks(configPath, runner.checks);
|
|
211
|
+
const update = resolveUpdate(configPath, runner.update);
|
|
208
212
|
if (config.scrub?.literals !== void 0 && !Array.isArray(config.scrub.literals)) {
|
|
209
213
|
fail(configPath, "scrub.literals", "must be a list of strings");
|
|
210
214
|
}
|
|
@@ -233,6 +237,7 @@ function loadRunnerConfig(configPath) {
|
|
|
233
237
|
build,
|
|
234
238
|
housekeeping,
|
|
235
239
|
checks,
|
|
240
|
+
update,
|
|
236
241
|
// A host with executables of its own for provision and cleanup chose
|
|
237
242
|
// its own environment, and an update could change what its hooks run
|
|
238
243
|
// under; it opts in. The built-in hooks are the runner's own. Provision
|
|
@@ -300,6 +305,21 @@ function resolveChecks(configPath, block) {
|
|
|
300
305
|
}
|
|
301
306
|
return { timeLimitMinutes: limit };
|
|
302
307
|
}
|
|
308
|
+
function resolveUpdate(configPath, block) {
|
|
309
|
+
const given = block ?? {};
|
|
310
|
+
if (typeof given !== "object" || Array.isArray(given)) fail(configPath, "runner.update", "must be an object");
|
|
311
|
+
const keys = given;
|
|
312
|
+
for (const key of Object.keys(keys)) {
|
|
313
|
+
if (UPDATE_KEYS.includes(key)) continue;
|
|
314
|
+
const near = nearestKey(key, UPDATE_KEYS);
|
|
315
|
+
fail(configPath, `runner.update.${key}`, near ? `is not a key this block accepts; did you mean ${near}?` : "is not a key this block accepts");
|
|
316
|
+
}
|
|
317
|
+
const hours = keys.maxWaitHours === void 0 ? DEFAULT_UPDATE_MAX_WAIT_HOURS : keys.maxWaitHours;
|
|
318
|
+
if (typeof hours !== "number" || !Number.isFinite(hours) || hours < 0) {
|
|
319
|
+
fail(configPath, "runner.update.maxWaitHours", "must be a number of hours, 0 or more");
|
|
320
|
+
}
|
|
321
|
+
return { maxWaitHours: hours };
|
|
322
|
+
}
|
|
303
323
|
function wholeBytes(bytes) {
|
|
304
324
|
if (bytes === void 0) return void 0;
|
|
305
325
|
const whole = Math.floor(bytes);
|
|
@@ -359,4 +379,4 @@ function resolveBuild(configPath, block) {
|
|
|
359
379
|
};
|
|
360
380
|
}
|
|
361
381
|
|
|
362
|
-
export { DEFAULT_CHECK_TIME_LIMIT_MINUTES, RunnerConfigError, builtinKind, checkHooks, defaultRunnerConfigPath, hasRunnerBlock, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, loadRunnerConfig, resolveBuild, resolveHousekeeping, runnerVmOf };
|
|
382
|
+
export { DEFAULT_CHECK_TIME_LIMIT_MINUTES, DEFAULT_UPDATE_MAX_WAIT_HOURS, RunnerConfigError, builtinKind, checkHooks, defaultRunnerConfigPath, hasRunnerBlock, isBuiltinContainer, isBuiltinProcess, isBuiltinVm, loadRunnerConfig, resolveBuild, resolveHousekeeping, runnerVmOf };
|