@kici-dev/compiler 0.1.20 → 0.1.22
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.d.ts +14 -0
- package/dist/cli.js +58 -7
- package/dist/commands/check-mode.d.ts +19 -0
- package/dist/commands/check-mode.js +21 -0
- package/dist/commands/compile.js +1 -1
- package/dist/commands/held-run-client.d.ts +7 -2
- package/dist/commands/held-run-client.js +9 -3
- package/dist/commands/held-run-resolve.d.ts +5 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +15 -7
- package/dist/commands/run-hold-watch.d.ts +57 -0
- package/dist/commands/run-hold-watch.js +87 -0
- package/dist/commands/run.d.ts +8 -0
- package/dist/commands/run.js +67 -4
- package/dist/commands/test.d.ts +17 -0
- package/dist/llm-context/llms-full.txt +643 -53
- package/dist/llm-context/llms.txt +3 -1
- package/dist/local-executor/index.js +15 -2
- package/dist/local-executor/job-runner.d.ts +3 -0
- package/dist/local-executor/job-runner.js +54 -5
- package/dist/local-executor/output-streamer.js +3 -1
- package/dist/local-executor/types.d.ts +7 -0
- package/dist/lockfile/generator.js +36 -17
- package/dist/remote/config.d.ts +2 -0
- package/dist/remote/config.js +1 -0
- package/dist/remote/platform-client.d.ts +12 -0
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/job-executor.d.ts +6 -1
- package/dist/test-runner/step-context.js +6 -1
- package/dist/types.d.ts +20 -6
- package/package.json +4 -4
- package/sbom.spdx.json +35 -35
|
@@ -14,6 +14,7 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
14
14
|
|
|
15
15
|
- [Basic workflow patterns](https://docs.kici.dev/user/patterns/basic/): Basic CI, PR-only / push-only filters, multiple triggers, manual-only workflows
|
|
16
16
|
- [Conditionals & matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/): Conditional execution with rules, matrix builds (static + dynamic), dynamic job generation
|
|
17
|
+
- [Host restart & wait-for-alive](https://docs.kici.dev/user/patterns/host-restart/): Reboot the host a workflow runs on and continue after it comes back
|
|
17
18
|
- [Integration patterns](https://docs.kici.dev/user/patterns/integrations/): Workflow chaining, generic webhooks, Stripe, self-hosted git forges, plain GitHub repos
|
|
18
19
|
- [Pattern reference](https://docs.kici.dev/user/patterns/reference/): Step context, examples repository, GitHub check run output — cross-cutting reference for all patterns
|
|
19
20
|
- [Scheduling & event patterns](https://docs.kici.dev/user/patterns/scheduling-and-events/): Nightly cron, workflow-complete-triggered deploys, custom event chaining
|
|
@@ -44,7 +45,7 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
44
45
|
## Workflow features
|
|
45
46
|
|
|
46
47
|
- [Account and sign-in](https://docs.kici.dev/user/account-and-login/): How your KiCI account relates to sign-in methods, and how to change the way you sign in.
|
|
47
|
-
- [Approval gates](https://docs.kici.dev/user/approvals/): Pause a workflow for human sign-off at step, job, or workflow granularity with
|
|
48
|
+
- [Approval gates](https://docs.kici.dev/user/approvals/): Pause a workflow for human sign-off at step, job, or workflow granularity with approval
|
|
48
49
|
- [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
|
|
49
50
|
- [Dashboard](https://docs.kici.dev/user/dashboard/): Web UI for monitoring workflow runs, managing sources, secrets, and organization settings.
|
|
50
51
|
- [Dynamic values](https://docs.kici.dev/user/dynamic-values/)
|
|
@@ -52,6 +53,7 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
52
53
|
- [Environments](https://docs.kici.dev/user/environments/): Configure deployment environments with variables, secrets, and protection rules
|
|
53
54
|
- [Event system](https://docs.kici.dev/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
|
|
54
55
|
- [Global workflows](https://docs.kici.dev/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
|
|
56
|
+
- [Idempotent steps and check mode](https://docs.kici.dev/user/idempotent-steps/): Declare desired state with a step check facet, then run in apply or --check preview mode
|
|
55
57
|
- [Private npm registries](https://docs.kici.dev/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
|
|
56
58
|
- [Build provenance and attestations](https://docs.kici.dev/user/provenance/): Generate and verify signed SLSA provenance for the artifacts your workflows build
|
|
57
59
|
- [Secrets](https://docs.kici.dev/user/secrets/): How to access secrets in KiCI workflow steps
|
|
@@ -15,7 +15,7 @@ import path from "node:path";
|
|
|
15
15
|
import pc from "picocolors";
|
|
16
16
|
import { writeFile } from "node:fs/promises";
|
|
17
17
|
import { logger } from "@kici-dev/core";
|
|
18
|
-
import { matchAllWorkflows } from "@kici-dev/engine";
|
|
18
|
+
import { CheckMode, CheckStepOutcome, matchAllWorkflows } from "@kici-dev/engine";
|
|
19
19
|
import os from "node:os";
|
|
20
20
|
//#region src/local-executor/index.ts
|
|
21
21
|
/**
|
|
@@ -169,7 +169,8 @@ async function runWorkflowBody(workflow, ctx, options, secrets, kiciDir, concurr
|
|
|
169
169
|
kiciDir,
|
|
170
170
|
execDir: ctx.execDir,
|
|
171
171
|
jobOutputsMap,
|
|
172
|
-
signal
|
|
172
|
+
signal,
|
|
173
|
+
checkMode: options.checkMode
|
|
173
174
|
});
|
|
174
175
|
},
|
|
175
176
|
isSuccess: (result) => result.status === "success" || result.status === "skipped"
|
|
@@ -305,6 +306,11 @@ async function executeLocal(options) {
|
|
|
305
306
|
if (!isQuiet) logger.info(pc.green(`JUnit XML written to ${options.junit}`));
|
|
306
307
|
}
|
|
307
308
|
if (!isQuiet) displayLocalSummary(workflowResults);
|
|
309
|
+
if (options.checkMode === CheckMode.enum["check-fail-on-drift"] && hasDrift(workflowResults)) {
|
|
310
|
+
if (!isQuiet) logger.info(pc.yellow("Drift detected in check mode (--fail-on-drift): exiting with code 2"));
|
|
311
|
+
if (materialized && !options.keep) await materialized.cleanup();
|
|
312
|
+
process.exit(2);
|
|
313
|
+
}
|
|
308
314
|
return allSucceeded;
|
|
309
315
|
} finally {
|
|
310
316
|
if (materialized) if (allSucceeded && !options.keep) await materialized.cleanup();
|
|
@@ -312,6 +318,13 @@ async function executeLocal(options) {
|
|
|
312
318
|
}
|
|
313
319
|
}
|
|
314
320
|
/**
|
|
321
|
+
* True when any step across all workflows reported drift (`dry-run` outcome) —
|
|
322
|
+
* the signal `check-fail-on-drift` uses to exit non-zero.
|
|
323
|
+
*/
|
|
324
|
+
function hasDrift(results) {
|
|
325
|
+
return results.some((wf) => wf.jobs.some((job) => (job.steps ?? []).some((s) => s.checkOutcome === CheckStepOutcome.enum["dry-run"])));
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
315
328
|
* Get default concurrency based on available CPU parallelism.
|
|
316
329
|
*/
|
|
317
330
|
function getDefaultConcurrency() {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - Single-job step-by-step execution with rules, abort, and output chaining
|
|
9
9
|
*/
|
|
10
10
|
import type { Workflow, OutputsMap } from '@kici-dev/sdk';
|
|
11
|
+
import { CheckMode } from '@kici-dev/engine';
|
|
11
12
|
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
12
13
|
import type { ParsedSecrets } from '../test-runner/secrets-file.js';
|
|
13
14
|
import type { ResolvedJob, LocalJobResult } from './types.js';
|
|
@@ -24,6 +25,8 @@ export interface JobExecutionContext {
|
|
|
24
25
|
execDir: string;
|
|
25
26
|
jobOutputsMap: OutputsMap;
|
|
26
27
|
signal: AbortSignal;
|
|
28
|
+
/** Run mode for idempotent steps. Defaults to `apply` when unset. */
|
|
29
|
+
checkMode?: CheckMode;
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
29
32
|
* Resolve all jobs in a workflow, expanding matrix jobs and evaluating dynamic jobs.
|
|
@@ -8,7 +8,8 @@ import { toEventPayload } from "./to-event-payload.js";
|
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { applyIncludeExclude, expandMatrix, isDynamicJobFn, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
|
|
11
|
-
import { formatMatrixSuffix } from "@kici-dev/engine";
|
|
11
|
+
import { CheckMode, CheckStepOutcome, formatMatrixSuffix } from "@kici-dev/engine";
|
|
12
|
+
import { runIdempotentStep } from "@kici-dev/core/idempotency";
|
|
12
13
|
//#region src/local-executor/job-runner.ts
|
|
13
14
|
/**
|
|
14
15
|
* Job resolution (matrix/dynamic) and single-job execution with step context.
|
|
@@ -51,6 +52,45 @@ function resolveNeedName(need) {
|
|
|
51
52
|
return need.name;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
55
|
+
* Run one local step honoring the run-level {@link CheckMode}, reusing the
|
|
56
|
+
* `runIdempotentStep` primitive for checked steps (never hand-rolled branching).
|
|
57
|
+
* Mirrors the agent step loop's check phase so `kici run local --check` and a
|
|
58
|
+
* remote check run produce identical per-step outcomes.
|
|
59
|
+
*/
|
|
60
|
+
async function runLocalStepWithCheckMode(step, ctx, checkMode) {
|
|
61
|
+
if (!step.check) {
|
|
62
|
+
if (checkMode !== CheckMode.enum.apply) return {
|
|
63
|
+
checkOutcome: CheckStepOutcome.enum.no_check,
|
|
64
|
+
status: "skipped",
|
|
65
|
+
outputs: void 0
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
status: "success",
|
|
69
|
+
outputs: await step.run(ctx)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const res = await runIdempotentStep({
|
|
73
|
+
name: step.name,
|
|
74
|
+
check: () => step.check(ctx),
|
|
75
|
+
summarize: step.summarize,
|
|
76
|
+
apply: (drift) => step.run(ctx, drift),
|
|
77
|
+
whenInSync: step.whenInSync ? () => step.whenInSync(ctx) : void 0
|
|
78
|
+
}, {
|
|
79
|
+
dryRun: checkMode !== CheckMode.enum.apply,
|
|
80
|
+
yes: true,
|
|
81
|
+
log: (line) => ctx.log.info(line)
|
|
82
|
+
});
|
|
83
|
+
const driftSummary = res.drift != null ? step.summarize(res.drift) : void 0;
|
|
84
|
+
const status = res.outcome === CheckStepOutcome.enum.applied ? "success" : "skipped";
|
|
85
|
+
const mappedStatus = res.outcome === CheckStepOutcome.enum["dry-run"] ? "success" : status;
|
|
86
|
+
return {
|
|
87
|
+
checkOutcome: res.outcome,
|
|
88
|
+
status: mappedStatus,
|
|
89
|
+
outputs: res.result,
|
|
90
|
+
...driftSummary !== void 0 && { driftSummary }
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
54
94
|
* Maintain the base-name `{ byMatrix, merged }` envelope as each matrix child
|
|
55
95
|
* completes. `merged` is rebuilt last-write-wins in suffix order so the result
|
|
56
96
|
* is deterministic regardless of child completion order — matching the remote
|
|
@@ -101,7 +141,12 @@ async function resolveJobs(workflow, event) {
|
|
|
101
141
|
scalers: [],
|
|
102
142
|
agents: []
|
|
103
143
|
}) },
|
|
104
|
-
|
|
144
|
+
inventory: {
|
|
145
|
+
query: () => Promise.resolve([]),
|
|
146
|
+
get: () => Promise.resolve(null)
|
|
147
|
+
},
|
|
148
|
+
oidc: { token: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.oidc.token() is not available during local execution")) },
|
|
149
|
+
host: { requestReboot: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.host.requestReboot() is not available during local execution")) }
|
|
105
150
|
}
|
|
106
151
|
});
|
|
107
152
|
for (const genJob of generatedJobs) {
|
|
@@ -267,14 +312,18 @@ async function executeResolvedJobInner(resolvedJob, context, startTime) {
|
|
|
267
312
|
formatter.logStepStart(expandedName, normalizedStep.name);
|
|
268
313
|
const stepStart = Date.now();
|
|
269
314
|
try {
|
|
270
|
-
const
|
|
315
|
+
const checkMode = context.checkMode ?? CheckMode.enum.apply;
|
|
316
|
+
const phase = await runLocalStepWithCheckMode(normalizedStep, stepCtx, checkMode);
|
|
317
|
+
const outputs = phase.outputs;
|
|
271
318
|
const stepDuration = Date.now() - stepStart;
|
|
272
319
|
formatter.logStepComplete(expandedName, normalizedStep.name, stepDuration);
|
|
273
320
|
const stepResult = {
|
|
274
321
|
name: normalizedStep.name,
|
|
275
|
-
status:
|
|
322
|
+
status: phase.status,
|
|
276
323
|
durationMs: stepDuration,
|
|
277
|
-
outputs
|
|
324
|
+
outputs,
|
|
325
|
+
...phase.checkOutcome !== void 0 && { checkOutcome: phase.checkOutcome },
|
|
326
|
+
...phase.driftSummary !== void 0 && { driftSummary: phase.driftSummary }
|
|
278
327
|
};
|
|
279
328
|
if (outputs != null) outputsMap.set(normalizedStep.name, outputs);
|
|
280
329
|
stepResults.push(stepResult);
|
|
@@ -95,7 +95,9 @@ function formatLocalJsonResult(results) {
|
|
|
95
95
|
name: step.name,
|
|
96
96
|
status: step.status,
|
|
97
97
|
durationMs: step.durationMs,
|
|
98
|
-
...step.error && { error: step.error.message }
|
|
98
|
+
...step.error && { error: step.error.message },
|
|
99
|
+
...step.checkOutcome && { checkOutcome: step.checkOutcome },
|
|
100
|
+
...step.driftSummary && { driftSummary: step.driftSummary }
|
|
99
101
|
})),
|
|
100
102
|
...job.ruleResults && job.ruleResults.length > 0 && { ruleResults: job.ruleResults.map((r) => ({
|
|
101
103
|
label: r.label,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MatrixValues, Job } from '@kici-dev/sdk';
|
|
2
|
+
import type { CheckMode } from '@kici-dev/engine';
|
|
2
3
|
import type { JobResult } from '../test-runner/job-executor.js';
|
|
3
4
|
/**
|
|
4
5
|
* All CLI flags for `kici run local`.
|
|
@@ -42,6 +43,12 @@ export interface RunLocalOptions {
|
|
|
42
43
|
inPlace?: boolean;
|
|
43
44
|
/** --keep: always retain the isolated tmp checkout (default: keep only on failure) */
|
|
44
45
|
keep?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Run mode resolved from --check / --fail-on-drift. Threads to the agent step
|
|
48
|
+
* loop: `apply` (default) converges, `check` previews drift, `check-fail-on-drift`
|
|
49
|
+
* previews drift and fails the run if any step reports drift. Defaults to `apply`.
|
|
50
|
+
*/
|
|
51
|
+
checkMode?: CheckMode;
|
|
45
52
|
}
|
|
46
53
|
/**
|
|
47
54
|
* A job after matrix expansion with resolved values.
|
|
@@ -5,10 +5,10 @@ import { resolveHashFiles } from "./hash-files.js";
|
|
|
5
5
|
import { analyzePurity } from "./purity-analyzer.js";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
-
import { getDynamicJobGroup, getDynamicJobNeeds, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isStaticArray, isStaticObject,
|
|
8
|
+
import { getDynamicJobGroup, getDynamicJobNeeds, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isStaticArray, isStaticObject, normalizeApproval, normalizeCacheSpecs } from "@kici-dev/sdk";
|
|
9
9
|
import { sha256 } from "@kici-dev/core";
|
|
10
10
|
import { PackageManager, detectPackageManagerSync, detectYarnFlavorSync } from "@kici-dev/core/package-manager";
|
|
11
|
-
import { validateResourceRequest } from "@kici-dev/engine";
|
|
11
|
+
import { resolveWhenToRunOn, validateResourceRequest } from "@kici-dev/engine";
|
|
12
12
|
import { normalizeRunsOnAllToMatchers, normalizeRunsOnToMatchers } from "@kici-dev/engine/labels/compile";
|
|
13
13
|
import { execSync } from "node:child_process";
|
|
14
14
|
//#region src/lockfile/generator.ts
|
|
@@ -158,7 +158,7 @@ function transformWorkflow(workflow, sourceFile, exportRef, bundleSource, gitRoo
|
|
|
158
158
|
...workflow.concurrency.max !== void 0 && { max: workflow.concurrency.max }
|
|
159
159
|
} },
|
|
160
160
|
...workflow.timeout !== void 0 && { timeout: workflow.timeout },
|
|
161
|
-
...workflow.
|
|
161
|
+
...workflow.approval !== void 0 && { approval: (assertNonStepApprovalScope(workflow.approval, "workflow"), toLockApproval(workflow.approval)) }
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
/**
|
|
@@ -565,12 +565,13 @@ function transformJob(job, configPath, index, gitRoot, uuidToName) {
|
|
|
565
565
|
...job.timeout !== void 0 && { timeout: job.timeout },
|
|
566
566
|
...job.resources !== void 0 && { resources: job.resources },
|
|
567
567
|
...job.init !== void 0 && { init: job.init },
|
|
568
|
-
...job.
|
|
568
|
+
...job.approval !== void 0 && { approval: (assertNonStepApprovalScope(job.approval, "job"), toLockApproval(job.approval)) }
|
|
569
569
|
};
|
|
570
570
|
}
|
|
571
571
|
/**
|
|
572
572
|
* Resolve needs to lock file format.
|
|
573
|
-
* Handles strings, Job objects, DynamicGroupRef, and object forms with
|
|
573
|
+
* Handles strings, Job objects, DynamicGroupRef, and object forms with a `when`
|
|
574
|
+
* run condition (normalized to a `runOn` status-set via the engine helper).
|
|
574
575
|
* Uses the UUID-to-renamed-name mapping so that references to id-less jobs
|
|
575
576
|
* resolve to their lock file names (job-N) instead of the original UUIDs.
|
|
576
577
|
*/
|
|
@@ -582,26 +583,26 @@ function resolveNeedsForLock(needs, uuidToName) {
|
|
|
582
583
|
else if (isDynamicGroupRef(need)) {
|
|
583
584
|
resolvedNeeds.push({
|
|
584
585
|
group: need.group,
|
|
585
|
-
|
|
586
|
+
runOn: resolveWhenToRunOn(need.when)
|
|
586
587
|
});
|
|
587
588
|
groups.push(need.group);
|
|
589
|
+
} else if ("_tag" in need && need._tag === "Job") {
|
|
590
|
+
const name = need.name;
|
|
591
|
+
resolvedNeeds.push(uuidToName?.get(name) ?? name);
|
|
588
592
|
} else if ("group" in need && typeof need.group === "string") {
|
|
589
593
|
const g = need;
|
|
590
594
|
resolvedNeeds.push({
|
|
591
595
|
group: g.group,
|
|
592
|
-
|
|
596
|
+
runOn: resolveWhenToRunOn(g.when)
|
|
593
597
|
});
|
|
594
598
|
groups.push(g.group);
|
|
595
|
-
} else
|
|
599
|
+
} else {
|
|
596
600
|
const n = need;
|
|
597
601
|
const name = uuidToName?.get(n.name) ?? n.name;
|
|
598
602
|
resolvedNeeds.push({
|
|
599
603
|
name,
|
|
600
|
-
|
|
604
|
+
runOn: resolveWhenToRunOn(n.when)
|
|
601
605
|
});
|
|
602
|
-
} else {
|
|
603
|
-
const name = need.name;
|
|
604
|
-
resolvedNeeds.push(uuidToName?.get(name) ?? name);
|
|
605
606
|
}
|
|
606
607
|
return {
|
|
607
608
|
needs: resolvedNeeds,
|
|
@@ -613,15 +614,31 @@ function resolveNeedsForLock(needs, uuidToName) {
|
|
|
613
614
|
* Assigns counter-based IDs to unnamed steps (bare functions and id-less steps).
|
|
614
615
|
* Counter only increments for unnamed entries; named steps keep their names.
|
|
615
616
|
*/
|
|
616
|
-
/** Map an SDK `
|
|
617
|
-
function toLockApproval(
|
|
618
|
-
const n =
|
|
617
|
+
/** Map an SDK `approval` to the normalized lock `approval` block. */
|
|
618
|
+
function toLockApproval(c) {
|
|
619
|
+
const n = normalizeApproval(c);
|
|
619
620
|
return {
|
|
620
621
|
clauses: n.clauses,
|
|
621
622
|
...n.reason !== void 0 && { reason: n.reason },
|
|
622
|
-
...n.timeoutSeconds !== void 0 && { timeoutSeconds: n.timeoutSeconds }
|
|
623
|
+
...n.timeoutSeconds !== void 0 && { timeoutSeconds: n.timeoutSeconds },
|
|
624
|
+
when: n.when
|
|
623
625
|
};
|
|
624
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Validate an approval config at job/workflow scope: `when: 'drift'` is a
|
|
629
|
+
* step-scope-only gate (it fires between a step's check and run), so it is a
|
|
630
|
+
* compile error anywhere else.
|
|
631
|
+
*/
|
|
632
|
+
function assertNonStepApprovalScope(c, scope) {
|
|
633
|
+
if (normalizeApproval(c).when === "drift") throw new Error(`approval.when "drift" is only valid on steps (found at ${scope} scope)`);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Validate a step's approval config: `when: 'drift'` fires between the step's
|
|
637
|
+
* check and run, so it requires a `check` facet. A compile error otherwise.
|
|
638
|
+
*/
|
|
639
|
+
function assertStepApprovalCheckFacet(step) {
|
|
640
|
+
if (step.approval !== void 0 && normalizeApproval(step.approval).when === "drift" && step.check === void 0) throw new Error(`step '${step.name || "(unnamed)"}': approval.when "drift" requires a check facet`);
|
|
641
|
+
}
|
|
625
642
|
function transformSteps(steps, gitRoot) {
|
|
626
643
|
let stepCounter = 0;
|
|
627
644
|
return steps.map((stepOrFn) => {
|
|
@@ -650,7 +667,9 @@ function transformSteps(steps, gitRoot) {
|
|
|
650
667
|
},
|
|
651
668
|
...step.onCancel !== void 0 && { hasOnCancel: true },
|
|
652
669
|
...step.cleanup !== void 0 && { hasCleanup: true },
|
|
653
|
-
...step.
|
|
670
|
+
...step.check !== void 0 && { hasCheck: true },
|
|
671
|
+
...step.whenInSync !== void 0 && { hasWhenInSync: true },
|
|
672
|
+
...step.approval !== void 0 && { approval: (assertStepApprovalCheckFacet(step), toLockApproval(step.approval)) }
|
|
654
673
|
};
|
|
655
674
|
});
|
|
656
675
|
}
|
package/dist/remote/config.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface GlobalConfig {
|
|
|
9
9
|
endpoint?: string;
|
|
10
10
|
/** Platform relay URL */
|
|
11
11
|
platformEndpoint?: string;
|
|
12
|
+
/** OIDC issuer URL the PAT was minted against (provenance; from OAuth login) */
|
|
13
|
+
oidcIssuer?: string;
|
|
12
14
|
/** Routing key for webhook source identification (e.g., 'github:42') */
|
|
13
15
|
routingKey?: string;
|
|
14
16
|
/** Personal access token (from OAuth login) */
|
package/dist/remote/config.js
CHANGED
|
@@ -14,6 +14,7 @@ function sanitizeConfig(raw) {
|
|
|
14
14
|
if (typeof obj.token === "string") config.token = obj.token;
|
|
15
15
|
if (typeof obj.endpoint === "string") config.endpoint = obj.endpoint;
|
|
16
16
|
if (typeof obj.platformEndpoint === "string") config.platformEndpoint = obj.platformEndpoint;
|
|
17
|
+
if (typeof obj.oidcIssuer === "string") config.oidcIssuer = obj.oidcIssuer;
|
|
17
18
|
if (typeof obj.routingKey === "string") config.routingKey = obj.routingKey;
|
|
18
19
|
if (typeof obj.pat === "string") config.pat = obj.pat;
|
|
19
20
|
if (typeof obj.patId === "string") config.patId = obj.patId;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* The overlay tarball does NOT flow through here: `initUpload` returns an
|
|
11
11
|
* external presigned URL the CLI PUTs to directly (data plane).
|
|
12
12
|
*/
|
|
13
|
+
import type { CheckMode, HostTargetSelector } from '@kici-dev/engine';
|
|
13
14
|
export declare class AuthenticationError extends Error {
|
|
14
15
|
constructor(message?: string);
|
|
15
16
|
}
|
|
@@ -70,6 +71,17 @@ export interface PlatformTriggerInput {
|
|
|
70
71
|
secrets?: Record<string, string>;
|
|
71
72
|
encryptedSecrets?: string;
|
|
72
73
|
encryptedSecretsKey?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Run mode for the dispatched run (`apply` | `check` | `check-fail-on-drift`).
|
|
76
|
+
* The Platform relays it onto the dispatch event so the orchestrator runs the
|
|
77
|
+
* agent step loop in the requested mode. Omitted means `apply`.
|
|
78
|
+
*/
|
|
79
|
+
checkMode?: CheckMode;
|
|
80
|
+
/**
|
|
81
|
+
* Host narrowing from `kici run --target`. The Platform relays it verbatim to
|
|
82
|
+
* the orchestrator, which intersects each runsOnAll roster with it.
|
|
83
|
+
*/
|
|
84
|
+
target?: HostTargetSelector;
|
|
73
85
|
}
|
|
74
86
|
export interface PlatformTriggerResponse {
|
|
75
87
|
runId: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Job, Workflow } from '@kici-dev/sdk';
|
|
2
2
|
import type { OutputsMap, StepRefMap } from '@kici-dev/sdk';
|
|
3
|
+
import type { CheckStepOutcome } from '@kici-dev/engine';
|
|
3
4
|
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
4
5
|
import type { RuleResult } from '@kici-dev/sdk';
|
|
5
6
|
import type { ParsedSecrets } from './secrets-file.js';
|
|
@@ -15,10 +16,14 @@ interface SdkOutputSetters {
|
|
|
15
16
|
}
|
|
16
17
|
export interface StepResult {
|
|
17
18
|
name: string;
|
|
18
|
-
status: 'success' | 'failure';
|
|
19
|
+
status: 'success' | 'failure' | 'skipped';
|
|
19
20
|
durationMs: number;
|
|
20
21
|
error?: Error;
|
|
21
22
|
outputs?: Record<string, unknown>;
|
|
23
|
+
/** Idempotent per-step outcome under a non-default check mode. */
|
|
24
|
+
checkOutcome?: CheckStepOutcome;
|
|
25
|
+
/** Human-readable drift summary, present when drift was detected. */
|
|
26
|
+
driftSummary?: string;
|
|
22
27
|
}
|
|
23
28
|
export interface JobResult {
|
|
24
29
|
name: string;
|
|
@@ -133,7 +133,12 @@ function createStepContext(workflowInfo, jobInfo, repoRoot, inputs = {}, matrix,
|
|
|
133
133
|
scalers: [],
|
|
134
134
|
agents: []
|
|
135
135
|
}) },
|
|
136
|
-
|
|
136
|
+
inventory: {
|
|
137
|
+
query: () => Promise.resolve([]),
|
|
138
|
+
get: () => Promise.resolve(null)
|
|
139
|
+
},
|
|
140
|
+
oidc: { token: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.oidc.token() is not available in the local test runner")) },
|
|
141
|
+
host: { requestReboot: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.host.requestReboot() is not available in the local test runner")) }
|
|
137
142
|
},
|
|
138
143
|
cache: {
|
|
139
144
|
restore: async () => ({ hit: false }),
|
package/dist/types.d.ts
CHANGED
|
@@ -12,18 +12,23 @@
|
|
|
12
12
|
* v15 adds per-job init config(s).
|
|
13
13
|
* v17 widens per-job init to typed presets ('mise' / { mise }) and 'auto' detection.
|
|
14
14
|
*/
|
|
15
|
-
import type { ResourceRequest, ApproverClause, RunsOnAllPredicate, OnUnreachableMode, LabelMatcher } from '@kici-dev/engine';
|
|
15
|
+
import type { ResourceRequest, ApproverClause, RunsOnAllPredicate, OnUnreachableMode, LabelMatcher, ExecutionJobStatus } from '@kici-dev/engine';
|
|
16
16
|
/**
|
|
17
17
|
* Normalized approval config carried in the lock file. Mirrors the engine
|
|
18
|
-
* `LockApproval` type. Produced by the compiler from an SDK `
|
|
18
|
+
* `LockApproval` type. Produced by the compiler from an SDK `approval` config.
|
|
19
19
|
*/
|
|
20
20
|
export interface LockApproval {
|
|
21
21
|
readonly clauses: ApproverClause[];
|
|
22
22
|
readonly reason?: string;
|
|
23
23
|
readonly timeoutSeconds?: number;
|
|
24
|
+
/**
|
|
25
|
+
* When the gate fires. `always` (default) gates before the element; `drift`
|
|
26
|
+
* gates between a step's check and run on detected drift (step scope only).
|
|
27
|
+
*/
|
|
28
|
+
readonly when: 'always' | 'drift';
|
|
24
29
|
}
|
|
25
30
|
/** Schema version - re-exported from engine as single source of truth */
|
|
26
|
-
export declare const SCHEMA_VERSION:
|
|
31
|
+
export declare const SCHEMA_VERSION: 22;
|
|
27
32
|
/**
|
|
28
33
|
* Source file reference with meaningful path.
|
|
29
34
|
* Format: file is relative path from git root, export uses hash syntax.
|
|
@@ -334,6 +339,15 @@ export interface LockStep {
|
|
|
334
339
|
readonly hasOnCancel?: boolean;
|
|
335
340
|
/** Whether this step has a cleanup hook. */
|
|
336
341
|
readonly hasCleanup?: boolean;
|
|
342
|
+
/**
|
|
343
|
+
* Whether this step declares an idempotent `check` facet. When true the
|
|
344
|
+
* orchestrator knows the step is check-capable and a run can be dispatched in
|
|
345
|
+
* check mode. The check/apply closures themselves are never serialized — the
|
|
346
|
+
* agent re-evaluates the real workflow TypeScript.
|
|
347
|
+
*/
|
|
348
|
+
readonly hasCheck?: boolean;
|
|
349
|
+
/** Whether this step declares a `whenInSync` facet (produces outputs when in sync). */
|
|
350
|
+
readonly hasWhenInSync?: boolean;
|
|
337
351
|
/** Normalized approval gate; when set the step pauses for a human approval. */
|
|
338
352
|
readonly approval?: LockApproval;
|
|
339
353
|
}
|
|
@@ -358,15 +372,15 @@ export declare function isLockInlineValue(value: unknown): value is LockInlineVa
|
|
|
358
372
|
* (e.g., `kici:role:builder`, `kici:role:init-runner`) are injected by the orchestrator
|
|
359
373
|
* for internal job types (build/init) and are not user-settable.
|
|
360
374
|
*/
|
|
361
|
-
/** Needs entry with per-edge
|
|
375
|
+
/** Needs entry with per-edge run-on status-set (mirrors engine NeedsEntry). */
|
|
362
376
|
export interface LockNeedsEntry {
|
|
363
377
|
readonly name: string;
|
|
364
|
-
readonly
|
|
378
|
+
readonly runOn: ExecutionJobStatus[];
|
|
365
379
|
}
|
|
366
380
|
/** Needs group entry for dynamic group dependencies (mirrors engine NeedsGroupEntry). */
|
|
367
381
|
export interface LockNeedsGroupEntry {
|
|
368
382
|
readonly group: string;
|
|
369
|
-
readonly
|
|
383
|
+
readonly runOn: ExecutionJobStatus[];
|
|
370
384
|
}
|
|
371
385
|
export interface LockJob {
|
|
372
386
|
readonly _type: 'static';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/compiler",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "Compiler and CLI for KiCI workflows. Compiles `.kici/workflows/*.ts` to a `kici.lock.json` file consumed by the orchestrator and agents, and runs workflows locally or against a remote orchestrator.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|
|
@@ -63,11 +63,11 @@
|
|
|
63
63
|
"yaml": "^2.9.0",
|
|
64
64
|
"zod": "^4.4.3",
|
|
65
65
|
"zx": "^8.8.5",
|
|
66
|
-
"@kici-dev/core": "0.1.
|
|
67
|
-
"@kici-dev/engine": "0.1.
|
|
66
|
+
"@kici-dev/core": "0.1.22",
|
|
67
|
+
"@kici-dev/engine": "0.1.22"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@kici-dev/sdk": "0.1.
|
|
70
|
+
"@kici-dev/sdk": "0.1.22"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@types/proper-lockfile": "^4.1.4"
|