@kici-dev/compiler 0.1.26 → 0.1.27
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/dist/cli-banner.d.ts +6 -3
- package/dist/cli-banner.js +13 -5
- package/dist/cli.js +66 -43
- package/dist/commands/check-mode.d.ts +1 -1
- package/dist/commands/compile.js +3 -3
- package/dist/commands/index.d.ts +5 -1
- package/dist/commands/index.js +6 -3
- package/dist/commands/init.js +1 -1
- package/dist/commands/local-trust-root.d.ts +6 -0
- package/dist/commands/local-trust-root.js +52 -0
- package/dist/commands/local.d.ts +39 -0
- package/dist/commands/local.js +137 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +40 -0
- package/dist/commands/logout.js +7 -0
- package/dist/commands/preview.js +1 -1
- package/dist/commands/run-banner.d.ts +31 -0
- package/dist/commands/run-banner.js +25 -0
- package/dist/commands/run-routed.d.ts +53 -0
- package/dist/commands/run-routed.js +176 -0
- package/dist/commands/run.d.ts +0 -17
- package/dist/commands/run.js +7 -50
- package/dist/commands/secrets-list.d.ts +2 -2
- package/dist/commands/secrets-list.js +4 -4
- package/dist/commands/types.d.ts +3 -3
- package/dist/commands/types.js +4 -4
- package/dist/execution/executor.js +7 -7
- package/dist/generators/secrets-dts.d.ts +9 -9
- package/dist/generators/secrets-dts.js +12 -12
- package/dist/llm-context/llms-architecture.txt +2 -2
- package/dist/llm-context/llms-cli.txt +674 -340
- package/dist/llm-context/llms-features.txt +334 -336
- package/dist/llm-context/llms-full.txt +1207 -892
- package/dist/llm-context/llms-getting-started.txt +7 -15
- package/dist/llm-context/llms-patterns.txt +5 -23
- package/dist/llm-context/llms-providers.txt +10 -1
- package/dist/llm-context/llms-sdk.txt +12 -12
- package/dist/llm-context/llms.txt +5 -5
- package/dist/local-executor/secret-loader.d.ts +1 -1
- package/dist/local-plane/orchestrator-process.d.ts +65 -0
- package/dist/local-plane/orchestrator-process.js +115 -0
- package/dist/local-plane/paths.d.ts +39 -0
- package/dist/local-plane/paths.js +54 -0
- package/dist/local-plane/plane-manager.d.ts +151 -0
- package/dist/local-plane/plane-manager.js +340 -0
- package/dist/local-plane/plane-seed.d.ts +76 -0
- package/dist/local-plane/plane-seed.js +59 -0
- package/dist/local-plane/plane-trigger.d.ts +62 -0
- package/dist/local-plane/plane-trigger.js +97 -0
- package/dist/local-plane/platform-attach.d.ts +53 -0
- package/dist/local-plane/platform-attach.js +111 -0
- package/dist/local-plane/postgres.d.ts +44 -0
- package/dist/local-plane/postgres.js +165 -0
- package/dist/local-plane/resolve-plane.d.ts +37 -0
- package/dist/local-plane/resolve-plane.js +76 -0
- package/dist/local-plane/run-follow.d.ts +41 -0
- package/dist/local-plane/run-follow.js +104 -0
- package/dist/local-plane/scaler-config.d.ts +54 -0
- package/dist/local-plane/scaler-config.js +114 -0
- package/dist/local-plane/secret-seed.d.ts +59 -0
- package/dist/local-plane/secret-seed.js +112 -0
- package/dist/local-plane/source-provider.d.ts +43 -0
- package/dist/local-plane/source-provider.js +165 -0
- package/dist/local-plane/trusted-routing.d.ts +32 -0
- package/dist/local-plane/trusted-routing.js +47 -0
- package/dist/lockfile/generator.js +7 -7
- package/dist/remote/dashboard-client.d.ts +7 -7
- package/dist/remote/dashboard-client.js +7 -7
- package/dist/remote/platform-client.d.ts +1 -1
- package/dist/remote/secret-loader.d.ts +18 -0
- package/dist/{local-executor → remote}/secret-loader.js +4 -4
- package/dist/remote/secret-upload.d.ts +1 -1
- package/dist/remote/secret-upload.js +2 -2
- package/dist/remote/uploader.d.ts +1 -1
- package/dist/remote/uploader.js +2 -2
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +2 -2
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/job-executor.js +1 -1
- package/dist/test-runner/runs-on-display.d.ts +9 -0
- package/dist/{local-executor → test-runner}/runs-on-display.js +1 -1
- package/dist/test-runner/step-context.d.ts +1 -1
- package/dist/test-runner/step-context.js +2 -2
- package/dist/types.d.ts +6 -6
- package/package.json +7 -4
- package/sbom.spdx.json +9884 -1459
- package/dist/local-executor/dag-scheduler.js +0 -183
- package/dist/local-executor/index.js +0 -378
- package/dist/local-executor/job-runner.js +0 -411
- package/dist/local-executor/materializer.js +0 -132
- package/dist/local-executor/output-streamer.js +0 -168
- package/dist/local-executor/payload-generator.js +0 -138
- package/dist/local-executor/picker.js +0 -109
- package/dist/local-executor/to-event-payload.js +0 -21
- package/dist/local-executor/types.js +0 -2
- package/dist/local-executor/workflow-lock.js +0 -0
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
-
import { ensureTsLoaderHook } from "../execution/ts-loader.js";
|
|
3
|
-
import { formatter } from "../test-runner/output-formatter.js";
|
|
4
|
-
import { createStepContext } from "../test-runner/step-context.js";
|
|
5
|
-
import { createRuleContext, evaluateRules as evaluateRulesWithFormatting } from "../test-runner/rule-evaluator.js";
|
|
6
|
-
import { localRunsOnString } from "./runs-on-display.js";
|
|
7
|
-
import { toEventPayload } from "./to-event-payload.js";
|
|
8
|
-
import { pathToFileURL } from "node:url";
|
|
9
|
-
import path from "node:path";
|
|
10
|
-
import { applyIncludeExclude, expandMatrix, flattenStepInputs, isDynamicJobFn, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
|
|
11
|
-
import { computeBackoffDelay } from "@kici-dev/core";
|
|
12
|
-
import { CheckMode, CheckStepOutcome, formatMatrixSuffix } from "@kici-dev/engine";
|
|
13
|
-
import { runIdempotentStep } from "@kici-dev/core/idempotency";
|
|
14
|
-
//#region src/local-executor/job-runner.ts
|
|
15
|
-
/**
|
|
16
|
-
* Job resolution (matrix/dynamic) and single-job execution with step context.
|
|
17
|
-
*
|
|
18
|
-
* Handles:
|
|
19
|
-
* - Static job resolution
|
|
20
|
-
* - Matrix expansion into multiple ResolvedJob instances
|
|
21
|
-
* - Dynamic job function evaluation
|
|
22
|
-
* - Single-job step-by-step execution with rules, abort, and output chaining
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* Resolve SDK output setter functions from the workflow's module instance.
|
|
26
|
-
* Ensures output maps are set on the same SDK module the workflow code uses.
|
|
27
|
-
*/
|
|
28
|
-
async function resolveSdkSetters(kiciDir) {
|
|
29
|
-
if (kiciDir) try {
|
|
30
|
-
const sdkUrl = pathToFileURL(path.join(kiciDir, "node_modules", "@kici-dev", "sdk", "dist", "index.js")).href;
|
|
31
|
-
ensureTsLoaderHook();
|
|
32
|
-
const sdk = await import(sdkUrl);
|
|
33
|
-
return {
|
|
34
|
-
setStepOutputsMap: sdk.setStepOutputsMap,
|
|
35
|
-
setStepRefMap: sdk.setStepRefMap,
|
|
36
|
-
setJobOutputsMap: sdk.setJobOutputsMap
|
|
37
|
-
};
|
|
38
|
-
} catch {}
|
|
39
|
-
return {
|
|
40
|
-
setStepOutputsMap,
|
|
41
|
-
setStepRefMap,
|
|
42
|
-
setJobOutputsMap
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Resolve a needs entry to a string name.
|
|
47
|
-
* Handles Job objects, strings, DynamicGroupRef, and object forms.
|
|
48
|
-
*/
|
|
49
|
-
function resolveNeedName(need) {
|
|
50
|
-
if (typeof need === "string") return need;
|
|
51
|
-
if ("name" in need) return need.name;
|
|
52
|
-
if ("group" in need) return `__group:${need.group}`;
|
|
53
|
-
return need.name;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Run one local step honoring the run-level {@link CheckMode}, reusing the
|
|
57
|
-
* `runIdempotentStep` primitive for checked steps (never hand-rolled branching).
|
|
58
|
-
* Mirrors the agent step loop's check phase so `kici run local --check` and a
|
|
59
|
-
* remote check run produce identical per-step outcomes.
|
|
60
|
-
*/
|
|
61
|
-
async function runLocalStepWithCheckMode(step, ctx, checkMode) {
|
|
62
|
-
if (!step.check) {
|
|
63
|
-
if (checkMode !== CheckMode.enum.apply) return {
|
|
64
|
-
checkOutcome: CheckStepOutcome.enum.no_check,
|
|
65
|
-
status: "skipped",
|
|
66
|
-
outputs: void 0
|
|
67
|
-
};
|
|
68
|
-
return {
|
|
69
|
-
status: "success",
|
|
70
|
-
outputs: await step.run(ctx)
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
const res = await runIdempotentStep({
|
|
74
|
-
name: step.name,
|
|
75
|
-
check: () => step.check(ctx),
|
|
76
|
-
summarize: step.summarize,
|
|
77
|
-
apply: (drift) => step.run(ctx, drift),
|
|
78
|
-
whenInSync: step.whenInSync ? () => step.whenInSync(ctx) : void 0
|
|
79
|
-
}, {
|
|
80
|
-
dryRun: checkMode !== CheckMode.enum.apply,
|
|
81
|
-
yes: true,
|
|
82
|
-
log: (line) => ctx.log.info(line)
|
|
83
|
-
});
|
|
84
|
-
const driftSummary = res.drift != null ? step.summarize(res.drift) : void 0;
|
|
85
|
-
const status = res.outcome === CheckStepOutcome.enum.applied ? "success" : "skipped";
|
|
86
|
-
const mappedStatus = res.outcome === CheckStepOutcome.enum["dry-run"] ? "success" : status;
|
|
87
|
-
return {
|
|
88
|
-
checkOutcome: res.outcome,
|
|
89
|
-
status: mappedStatus,
|
|
90
|
-
outputs: res.result,
|
|
91
|
-
...driftSummary !== void 0 && { driftSummary }
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Run a local step through its retry policy. An attempt is one full
|
|
96
|
-
* `runLocalStepWithCheckMode` call (which throws on failure). A thrown attempt
|
|
97
|
-
* is retried while attempts remain AND `retryIf(err)` is true; backoff sleeps
|
|
98
|
-
* between attempts. Mirrors the agent step loop so `kici run local` retries
|
|
99
|
-
* identically to a remote run.
|
|
100
|
-
*/
|
|
101
|
-
async function runLocalStepWithRetry(step, ctx, checkMode, log) {
|
|
102
|
-
const retry = step.retry;
|
|
103
|
-
const max = retry?.maxAttempts ?? 1;
|
|
104
|
-
let lastErr;
|
|
105
|
-
for (let n = 1; n <= max; n++) try {
|
|
106
|
-
return await runLocalStepWithCheckMode(step, ctx, checkMode);
|
|
107
|
-
} catch (err) {
|
|
108
|
-
lastErr = err;
|
|
109
|
-
if (!(n < max && (retry?.retryIf?.(err) ?? true))) break;
|
|
110
|
-
const delay = computeBackoffDelay(n, {
|
|
111
|
-
maxAttempts: max,
|
|
112
|
-
delayMs: retry.delayMs,
|
|
113
|
-
backoff: retry.backoff,
|
|
114
|
-
maxDelayMs: retry.maxDelayMs
|
|
115
|
-
});
|
|
116
|
-
log(`Step '${step.name}' attempt ${n}/${max} failed: ${err instanceof Error ? err.message : String(err)}; retrying in ${delay}ms`);
|
|
117
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
118
|
-
}
|
|
119
|
-
throw lastErr;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Maintain the base-name `{ byMatrix, merged }` envelope as each matrix child
|
|
123
|
-
* completes. `merged` is rebuilt last-write-wins in suffix order so the result
|
|
124
|
-
* is deterministic regardless of child completion order — matching the remote
|
|
125
|
-
* path's name-ordered merge.
|
|
126
|
-
*/
|
|
127
|
-
function updateBaseMatrixEnvelope(jobOutputsMap, baseName, matrixValues, childOutputs) {
|
|
128
|
-
const existing = jobOutputsMap.get(baseName);
|
|
129
|
-
const byMatrix = existing && "byMatrix" in existing ? { ...existing.byMatrix } : {};
|
|
130
|
-
const suffix = formatMatrixSuffix(matrixValues);
|
|
131
|
-
byMatrix[suffix] = childOutputs;
|
|
132
|
-
let merged = {};
|
|
133
|
-
for (const key of Object.keys(byMatrix).sort()) merged = {
|
|
134
|
-
...merged,
|
|
135
|
-
...byMatrix[key]
|
|
136
|
-
};
|
|
137
|
-
jobOutputsMap.set(baseName, {
|
|
138
|
-
byMatrix,
|
|
139
|
-
merged
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Resolve all jobs in a workflow, expanding matrix jobs and evaluating dynamic jobs.
|
|
144
|
-
*
|
|
145
|
-
* - Static jobs: create ResolvedJob with name, needs, job reference
|
|
146
|
-
* - Matrix jobs: use expandMatrix() from SDK, create one ResolvedJob per combination
|
|
147
|
-
* - Dynamic job functions: evaluate the function, recursively resolve results
|
|
148
|
-
* - Fan-in: if a job needs a matrix-expanded job, it needs ALL instances
|
|
149
|
-
*/
|
|
150
|
-
async function resolveJobs(workflow, event) {
|
|
151
|
-
const resolved = [];
|
|
152
|
-
const expansionMap = /* @__PURE__ */ new Map();
|
|
153
|
-
for (const jobOrFn of workflow.jobs) if (isDynamicJobFn(jobOrFn)) {
|
|
154
|
-
const generatedJobs = await jobOrFn({
|
|
155
|
-
$: (await import("zx")).$,
|
|
156
|
-
ctx: {
|
|
157
|
-
workflow: { name: workflow.name },
|
|
158
|
-
event: toEventPayload(event)
|
|
159
|
-
},
|
|
160
|
-
log: {
|
|
161
|
-
info: () => {},
|
|
162
|
-
warn: () => {},
|
|
163
|
-
error: () => {},
|
|
164
|
-
debug: () => {}
|
|
165
|
-
},
|
|
166
|
-
env: { ...process.env },
|
|
167
|
-
kici: {
|
|
168
|
-
infrastructure: { list: () => Promise.resolve({
|
|
169
|
-
scalers: [],
|
|
170
|
-
agents: []
|
|
171
|
-
}) },
|
|
172
|
-
inventory: {
|
|
173
|
-
query: () => Promise.resolve([]),
|
|
174
|
-
get: () => Promise.resolve(null)
|
|
175
|
-
},
|
|
176
|
-
oidc: { token: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.oidc.token() is not available during local execution")) },
|
|
177
|
-
host: { requestReboot: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.host.requestReboot() is not available during local execution")) },
|
|
178
|
-
bootstrap: {
|
|
179
|
-
ensureInitRunner: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.bootstrap.ensureInitRunner() is not available during local execution")),
|
|
180
|
-
preBootSend: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.bootstrap.preBootSend() is not available during local execution"))
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
for (const genJob of generatedJobs) {
|
|
185
|
-
const genResolved = await resolveStaticJob(genJob, expansionMap);
|
|
186
|
-
resolved.push(...genResolved);
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
const jobResolved = await resolveStaticJob(jobOrFn, expansionMap);
|
|
190
|
-
resolved.push(...jobResolved);
|
|
191
|
-
}
|
|
192
|
-
for (const r of resolved) r.resolvedNeeds = resolveNeedsWithExpansion(r.job, expansionMap);
|
|
193
|
-
return resolved;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Resolve a single static job, potentially expanding its matrix.
|
|
197
|
-
*/
|
|
198
|
-
async function resolveStaticJob(job, expansionMap) {
|
|
199
|
-
if (!job.matrix) {
|
|
200
|
-
expansionMap.set(job.name, [job.name]);
|
|
201
|
-
return [{
|
|
202
|
-
job,
|
|
203
|
-
expandedName: job.name,
|
|
204
|
-
matrixValues: {},
|
|
205
|
-
resolvedNeeds: []
|
|
206
|
-
}];
|
|
207
|
-
}
|
|
208
|
-
let staticMatrix;
|
|
209
|
-
if (typeof job.matrix === "function") {
|
|
210
|
-
const matrixContext = {
|
|
211
|
-
$: (await import("zx")).$,
|
|
212
|
-
ctx: {
|
|
213
|
-
workflow: { name: "" },
|
|
214
|
-
job: {
|
|
215
|
-
name: job.name,
|
|
216
|
-
runsOn: localRunsOnString(job.runsOn)
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
log: {
|
|
220
|
-
info: () => {},
|
|
221
|
-
warn: () => {},
|
|
222
|
-
error: () => {},
|
|
223
|
-
debug: () => {}
|
|
224
|
-
},
|
|
225
|
-
env: { ...process.env }
|
|
226
|
-
};
|
|
227
|
-
staticMatrix = await job.matrix(matrixContext);
|
|
228
|
-
} else staticMatrix = job.matrix;
|
|
229
|
-
let expanded = expandMatrix(staticMatrix);
|
|
230
|
-
if (job.include || job.exclude) expanded = applyIncludeExclude(expanded, job.include, job.exclude);
|
|
231
|
-
const expandedNames = [];
|
|
232
|
-
const results = [];
|
|
233
|
-
for (const matrixValues of expanded) {
|
|
234
|
-
const suffix = formatMatrixSuffix(matrixValues);
|
|
235
|
-
const expandedName = `${job.name} (${suffix})`;
|
|
236
|
-
expandedNames.push(expandedName);
|
|
237
|
-
results.push({
|
|
238
|
-
job,
|
|
239
|
-
expandedName,
|
|
240
|
-
matrixValues,
|
|
241
|
-
resolvedNeeds: []
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
expansionMap.set(job.name, expandedNames);
|
|
245
|
-
return results;
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Resolve needs with matrix expansion awareness.
|
|
249
|
-
* If a job needs a matrix-expanded job, it needs ALL expanded instances (fan-in).
|
|
250
|
-
*/
|
|
251
|
-
function resolveNeedsWithExpansion(job, expansionMap) {
|
|
252
|
-
if (!job.needs || job.needs.length === 0) return [];
|
|
253
|
-
const resolvedNeeds = [];
|
|
254
|
-
for (const need of job.needs) {
|
|
255
|
-
const needName = resolveNeedName(need);
|
|
256
|
-
const expandedNames = expansionMap.get(needName);
|
|
257
|
-
if (expandedNames) resolvedNeeds.push(...expandedNames);
|
|
258
|
-
else resolvedNeeds.push(needName);
|
|
259
|
-
}
|
|
260
|
-
return resolvedNeeds;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Execute a single resolved job: evaluate rules, run steps sequentially,
|
|
264
|
-
* check abort signal between steps, collect outputs.
|
|
265
|
-
*/
|
|
266
|
-
async function executeResolvedJob(resolvedJob, context) {
|
|
267
|
-
const { expandedName, matrixValues } = resolvedJob;
|
|
268
|
-
const startTime = Date.now();
|
|
269
|
-
const hasMatrix = Object.keys(matrixValues).length > 0;
|
|
270
|
-
try {
|
|
271
|
-
return await executeResolvedJobInner(resolvedJob, context, startTime);
|
|
272
|
-
} catch (e) {
|
|
273
|
-
const durationMs = Date.now() - startTime;
|
|
274
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
275
|
-
formatter.logJobFailure(expandedName, durationMs, error);
|
|
276
|
-
return {
|
|
277
|
-
name: expandedName,
|
|
278
|
-
status: "failure",
|
|
279
|
-
durationMs,
|
|
280
|
-
steps: [],
|
|
281
|
-
error,
|
|
282
|
-
matrixValues: hasMatrix ? matrixValues : void 0
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
async function executeResolvedJobInner(resolvedJob, context, startTime) {
|
|
287
|
-
const { job, expandedName, matrixValues } = resolvedJob;
|
|
288
|
-
formatter.logJobStart(expandedName);
|
|
289
|
-
const sdkSetters = await resolveSdkSetters(context.kiciDir);
|
|
290
|
-
let ruleResults;
|
|
291
|
-
if (job.rules && job.rules.length > 0) {
|
|
292
|
-
const ruleContext = createRuleContext(context.event, context.event.changedFiles, context.dispatchInputs ?? {});
|
|
293
|
-
const ruleEval = await evaluateRulesWithFormatting(job.rules, ruleContext, expandedName);
|
|
294
|
-
ruleResults = ruleEval.results;
|
|
295
|
-
if (!ruleEval.allPassed) return {
|
|
296
|
-
name: expandedName,
|
|
297
|
-
status: "skipped",
|
|
298
|
-
durationMs: Date.now() - startTime,
|
|
299
|
-
steps: [],
|
|
300
|
-
ruleResults,
|
|
301
|
-
matrixValues: Object.keys(matrixValues).length > 0 ? matrixValues : void 0
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
const outputsMap = /* @__PURE__ */ new Map();
|
|
305
|
-
const refMap = /* @__PURE__ */ new WeakMap();
|
|
306
|
-
sdkSetters.setStepOutputsMap(outputsMap);
|
|
307
|
-
sdkSetters.setStepRefMap(refMap);
|
|
308
|
-
setStepOutputsMap(outputsMap);
|
|
309
|
-
setStepRefMap(refMap);
|
|
310
|
-
sdkSetters.setJobOutputsMap(context.jobOutputsMap);
|
|
311
|
-
setJobOutputsMap(context.jobOutputsMap);
|
|
312
|
-
const hasMatrix = Object.keys(matrixValues).length > 0;
|
|
313
|
-
const repoRoot = context.execDir;
|
|
314
|
-
const stepCtx = createStepContext({ name: context.workflowName }, {
|
|
315
|
-
name: expandedName,
|
|
316
|
-
runsOn: localRunsOnString(job.runsOn)
|
|
317
|
-
}, repoRoot, void 0, hasMatrix ? matrixValues : void 0, context.secrets, void 0, context.event.payload, context.event.provider, context.dispatchInputs ?? {}, context.signal);
|
|
318
|
-
const stepResults = [];
|
|
319
|
-
let stepCounter = 0;
|
|
320
|
-
for (const stepOrFn of flattenStepInputs(job.steps)) {
|
|
321
|
-
if (context.signal.aborted) return {
|
|
322
|
-
name: expandedName,
|
|
323
|
-
status: "cancelled",
|
|
324
|
-
durationMs: Date.now() - startTime,
|
|
325
|
-
steps: stepResults,
|
|
326
|
-
ruleResults,
|
|
327
|
-
matrixValues: hasMatrix ? matrixValues : void 0,
|
|
328
|
-
cancelled: true
|
|
329
|
-
};
|
|
330
|
-
let normalizedStep;
|
|
331
|
-
if (typeof stepOrFn === "function") {
|
|
332
|
-
stepCounter++;
|
|
333
|
-
const name = `step-${stepCounter}`;
|
|
334
|
-
refMap.set(stepOrFn, name);
|
|
335
|
-
normalizedStep = {
|
|
336
|
-
_tag: "Step",
|
|
337
|
-
name,
|
|
338
|
-
run: stepOrFn
|
|
339
|
-
};
|
|
340
|
-
} else {
|
|
341
|
-
normalizedStep = stepOrFn;
|
|
342
|
-
if (!normalizedStep.name) stepCounter++;
|
|
343
|
-
}
|
|
344
|
-
formatter.logStepStart(expandedName, normalizedStep.name);
|
|
345
|
-
const stepStart = Date.now();
|
|
346
|
-
try {
|
|
347
|
-
const checkMode = context.checkMode ?? CheckMode.enum.apply;
|
|
348
|
-
const phase = await runLocalStepWithRetry(normalizedStep, stepCtx, checkMode, (line) => formatter.logJobLine(expandedName, line));
|
|
349
|
-
const outputs = phase.outputs;
|
|
350
|
-
const stepDuration = Date.now() - stepStart;
|
|
351
|
-
formatter.logStepComplete(expandedName, normalizedStep.name, stepDuration);
|
|
352
|
-
const stepResult = {
|
|
353
|
-
name: normalizedStep.name,
|
|
354
|
-
status: phase.status,
|
|
355
|
-
durationMs: stepDuration,
|
|
356
|
-
outputs,
|
|
357
|
-
...phase.checkOutcome !== void 0 && { checkOutcome: phase.checkOutcome },
|
|
358
|
-
...phase.driftSummary !== void 0 && { driftSummary: phase.driftSummary }
|
|
359
|
-
};
|
|
360
|
-
if (outputs != null) outputsMap.set(normalizedStep.name, outputs);
|
|
361
|
-
stepResults.push(stepResult);
|
|
362
|
-
} catch (e) {
|
|
363
|
-
const stepDuration = Date.now() - stepStart;
|
|
364
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
365
|
-
formatter.logStepError(expandedName, normalizedStep.name, error);
|
|
366
|
-
stepResults.push({
|
|
367
|
-
name: normalizedStep.name,
|
|
368
|
-
status: "failure",
|
|
369
|
-
durationMs: stepDuration,
|
|
370
|
-
error
|
|
371
|
-
});
|
|
372
|
-
const durationMs = Date.now() - startTime;
|
|
373
|
-
formatter.logJobFailure(expandedName, durationMs, error);
|
|
374
|
-
return {
|
|
375
|
-
name: expandedName,
|
|
376
|
-
status: "failure",
|
|
377
|
-
durationMs,
|
|
378
|
-
steps: stepResults,
|
|
379
|
-
ruleResults,
|
|
380
|
-
matrixValues: hasMatrix ? matrixValues : void 0,
|
|
381
|
-
error
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
const stepsWithOutputs = stepResults.filter((s) => s.outputs != null);
|
|
386
|
-
let jobOutputs;
|
|
387
|
-
if (stepsWithOutputs.length === 1 && stepResults.length === 1) jobOutputs = stepsWithOutputs[0].outputs;
|
|
388
|
-
else if (stepsWithOutputs.length > 0) {
|
|
389
|
-
const aggregated = {};
|
|
390
|
-
for (const stepResult of stepsWithOutputs) aggregated[stepResult.name] = stepResult.outputs;
|
|
391
|
-
jobOutputs = aggregated;
|
|
392
|
-
}
|
|
393
|
-
if (jobOutputs) {
|
|
394
|
-
context.jobOutputsMap.set(expandedName, jobOutputs);
|
|
395
|
-
if (expandedName !== job.name) updateBaseMatrixEnvelope(context.jobOutputsMap, job.name, matrixValues, jobOutputs);
|
|
396
|
-
}
|
|
397
|
-
const durationMs = Date.now() - startTime;
|
|
398
|
-
formatter.logJobComplete(expandedName, durationMs);
|
|
399
|
-
return {
|
|
400
|
-
name: expandedName,
|
|
401
|
-
status: "success",
|
|
402
|
-
durationMs,
|
|
403
|
-
steps: stepResults,
|
|
404
|
-
ruleResults,
|
|
405
|
-
matrixValues: hasMatrix ? matrixValues : void 0
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
//#endregion
|
|
409
|
-
export { executeResolvedJob, resolveJobs };
|
|
410
|
-
|
|
411
|
-
//# sourceMappingURL=job-runner.js.map
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
-
import { selectOverlayFiles } from "../remote/uploader.js";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import fs from "node:fs/promises";
|
|
5
|
-
import { logger } from "@kici-dev/core";
|
|
6
|
-
import { execSync } from "node:child_process";
|
|
7
|
-
import os from "node:os";
|
|
8
|
-
import { randomBytes } from "node:crypto";
|
|
9
|
-
import { gcStaleTmpDirs } from "@kici-dev/core/tmp-gc";
|
|
10
|
-
//#region src/local-executor/materializer.ts
|
|
11
|
-
/**
|
|
12
|
-
* Materialize an isolated tmp checkout of the repo for `kici run local`.
|
|
13
|
-
*
|
|
14
|
-
* Reproduces the same workspace `kici run remote` reconstructs on the agent:
|
|
15
|
-
* a clone checked out at HEAD, with the local overlay (dirty + untracked,
|
|
16
|
-
* minus gitignored, with `.kiciignore` applied) copied on top and local
|
|
17
|
-
* deletions removed. Steps then execute against this copy so the developer's
|
|
18
|
-
* real working tree is never mutated.
|
|
19
|
-
*
|
|
20
|
-
* Secrets are NOT part of the overlay: gitignored files such as
|
|
21
|
-
* `.kici/.env.local` are excluded by the shared selection, so the tmp checkout
|
|
22
|
-
* never receives them — the run reads them from the original `.kici/`.
|
|
23
|
-
*/
|
|
24
|
-
/** Max number of overlay files copied concurrently. */
|
|
25
|
-
const COPY_BATCH_SIZE = 32;
|
|
26
|
-
/**
|
|
27
|
-
* Materialize the repo at `repoRoot` into an isolated tmp checkout.
|
|
28
|
-
*
|
|
29
|
-
* @param repoRoot - Path to the git repository root
|
|
30
|
-
* @param opts - Optional configuration (tmp base directory)
|
|
31
|
-
* @returns The tmp checkout path and a cleanup callback
|
|
32
|
-
* @throws If `repoRoot` is not inside a git work tree
|
|
33
|
-
*/
|
|
34
|
-
async function materializeCheckout(repoRoot, opts) {
|
|
35
|
-
requireGitRepo(repoRoot);
|
|
36
|
-
const base = opts?.runDir ?? os.tmpdir();
|
|
37
|
-
await fs.mkdir(base, { recursive: true });
|
|
38
|
-
const tmpDir = path.join(base, `kici-run-${randomBytes(3).toString("hex")}`);
|
|
39
|
-
const { sha, existingFiles, deletedFiles } = await selectOverlayFiles(repoRoot);
|
|
40
|
-
execSync(`git clone --no-hardlinks --quiet ${shellQuote(repoRoot)} ${shellQuote(tmpDir)}`, { stdio: "ignore" });
|
|
41
|
-
execSync(`git checkout --quiet ${sha}`, {
|
|
42
|
-
cwd: tmpDir,
|
|
43
|
-
stdio: "ignore"
|
|
44
|
-
});
|
|
45
|
-
await applyOverlay(repoRoot, tmpDir, existingFiles, deletedFiles);
|
|
46
|
-
return {
|
|
47
|
-
path: tmpDir,
|
|
48
|
-
cleanup: async () => {
|
|
49
|
-
await fs.rm(tmpDir, {
|
|
50
|
-
recursive: true,
|
|
51
|
-
force: true
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Checkouts retained for inspection (failed runs, --keep) stay around this
|
|
58
|
-
* long before the next `kici run local` invocation collects them.
|
|
59
|
-
*/
|
|
60
|
-
const RUN_CHECKOUT_GC_MAX_AGE_MS = 4320 * 60 * 1e3;
|
|
61
|
-
/** Matches the kici-run-<6 hex> dirs materializeCheckout creates — nothing else. */
|
|
62
|
-
const RUN_CHECKOUT_PATTERN = /^kici-run-[0-9a-f]{6}$/;
|
|
63
|
-
/**
|
|
64
|
-
* Collect stale retained checkouts under the run base. Invoked on every
|
|
65
|
-
* `kici run local`; never throws (the GC must not break the run).
|
|
66
|
-
*/
|
|
67
|
-
async function gcStaleRunCheckouts(base) {
|
|
68
|
-
return gcStaleTmpDirs({
|
|
69
|
-
base,
|
|
70
|
-
pattern: RUN_CHECKOUT_PATTERN,
|
|
71
|
-
maxAgeMs: RUN_CHECKOUT_GC_MAX_AGE_MS,
|
|
72
|
-
log: (m) => logger.debug(m)
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Throw an actionable error if `repoRoot` is not a git work tree.
|
|
77
|
-
*/
|
|
78
|
-
function requireGitRepo(repoRoot) {
|
|
79
|
-
try {
|
|
80
|
-
execSync("git rev-parse --is-inside-work-tree", {
|
|
81
|
-
cwd: repoRoot,
|
|
82
|
-
stdio: "ignore"
|
|
83
|
-
});
|
|
84
|
-
} catch {
|
|
85
|
-
throw new Error(`kici run local needs a git repository to build an isolated checkout, but "${repoRoot}" is not inside a git work tree. Initialize a repo, or re-run with --in-place to execute against the working directory directly.`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Copy the overlay files onto the clone and remove local deletions.
|
|
90
|
-
*/
|
|
91
|
-
async function applyOverlay(repoRoot, tmpDir, existingFiles, deletedFiles) {
|
|
92
|
-
for (let i = 0; i < existingFiles.length; i += COPY_BATCH_SIZE) {
|
|
93
|
-
const batch = existingFiles.slice(i, i + COPY_BATCH_SIZE);
|
|
94
|
-
await Promise.all(batch.map((file) => copyOverlayFile(repoRoot, tmpDir, file)));
|
|
95
|
-
}
|
|
96
|
-
for (let i = 0; i < deletedFiles.length; i += COPY_BATCH_SIZE) {
|
|
97
|
-
const batch = deletedFiles.slice(i, i + COPY_BATCH_SIZE);
|
|
98
|
-
await Promise.all(batch.map((file) => fs.rm(path.join(tmpDir, file), { force: true })));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Copy a single overlay file, preserving its mode (e.g. the exec bit).
|
|
103
|
-
*
|
|
104
|
-
* Symlinks are recreated as links rather than dereferenced — the same shape the
|
|
105
|
-
* remote path's tarball preserves. Following them would copy the link target's
|
|
106
|
-
* content (and a directory symlink such as `node_modules/@scope/pkg` would
|
|
107
|
-
* throw `EISDIR`), losing the link identity the workspace relies on.
|
|
108
|
-
*/
|
|
109
|
-
async function copyOverlayFile(repoRoot, tmpDir, file) {
|
|
110
|
-
const src = path.join(repoRoot, file);
|
|
111
|
-
const dest = path.join(tmpDir, file);
|
|
112
|
-
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
113
|
-
const srcStat = await fs.lstat(src);
|
|
114
|
-
if (srcStat.isSymbolicLink()) {
|
|
115
|
-
const target = await fs.readlink(src);
|
|
116
|
-
await fs.rm(dest, { force: true });
|
|
117
|
-
await fs.symlink(target, dest);
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
await fs.copyFile(src, dest);
|
|
121
|
-
await fs.chmod(dest, srcStat.mode);
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Minimal single-quote shell escaping for paths passed to git via execSync.
|
|
125
|
-
*/
|
|
126
|
-
function shellQuote(value) {
|
|
127
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
128
|
-
}
|
|
129
|
-
//#endregion
|
|
130
|
-
export { gcStaleRunCheckouts, materializeCheckout };
|
|
131
|
-
|
|
132
|
-
//# sourceMappingURL=materializer.js.map
|