@kici-dev/compiler 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +1 -6
- package/dist/assets/api-TJJVHrjC.json +118 -0
- package/dist/assets/descriptor-BTtjzN9L.json +1382 -0
- package/dist/assets/package-BpQF9kR8.json +74 -0
- package/dist/assets/package-Ceo2h27X.json +89 -0
- package/dist/assets/source_context-D0atuL28.json +20 -0
- package/dist/assets/type-BFqO8SCZ.json +202 -0
- package/dist/auth/headless-detect.d.ts +14 -0
- package/dist/auth/headless-detect.js +25 -0
- package/dist/chunk-gOLHoazu.js +4 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +193 -0
- package/dist/commands/cancel.d.ts +22 -0
- package/dist/commands/cancel.js +124 -0
- package/dist/commands/compile.d.ts +17 -0
- package/dist/commands/compile.js +99 -0
- package/dist/commands/docs.d.ts +26 -0
- package/dist/commands/docs.js +60 -0
- package/dist/commands/drain-worker.d.ts +19 -0
- package/dist/commands/drain-worker.js +45 -0
- package/dist/commands/endpoints.d.ts +53 -0
- package/dist/commands/endpoints.js +185 -0
- package/dist/commands/fixture.d.ts +14 -0
- package/dist/commands/fixture.js +104 -0
- package/dist/commands/hook.d.ts +20 -0
- package/dist/commands/hook.js +97 -0
- package/dist/commands/index.d.ts +33 -0
- package/dist/commands/index.js +20 -0
- package/dist/commands/init.d.ts +35 -0
- package/dist/commands/init.js +393 -0
- package/dist/commands/login.d.ts +23 -0
- package/dist/commands/login.js +144 -0
- package/dist/commands/logout.d.ts +18 -0
- package/dist/commands/logout.js +55 -0
- package/dist/commands/org.d.ts +27 -0
- package/dist/commands/org.js +126 -0
- package/dist/commands/run.d.ts +21 -0
- package/dist/commands/run.js +562 -0
- package/dist/commands/secrets-list.d.ts +16 -0
- package/dist/commands/secrets-list.js +85 -0
- package/dist/commands/status.d.ts +30 -0
- package/dist/commands/status.js +210 -0
- package/dist/commands/test.d.ts +61 -0
- package/dist/commands/test.js +220 -0
- package/dist/commands/types.d.ts +18 -0
- package/dist/commands/types.js +74 -0
- package/dist/commands/watch.d.ts +14 -0
- package/dist/commands/watch.js +97 -0
- package/dist/commands/workflows.d.ts +28 -0
- package/dist/commands/workflows.js +154 -0
- package/dist/comment-created.json +30 -0
- package/dist/create-branch.json +17 -0
- package/dist/delete-branch.json +15 -0
- package/dist/dispatch.json +15 -0
- package/dist/errors/capability-gap.d.ts +50 -0
- package/dist/errors/capability-gap.js +54 -0
- package/dist/errors/codes.d.ts +25 -0
- package/dist/errors/formatter.d.ts +32 -0
- package/dist/errors/formatter.js +35 -0
- package/dist/errors/index.d.ts +5 -0
- package/dist/errors/index.js +4 -0
- package/dist/execution/executor.d.ts +61 -0
- package/dist/execution/executor.js +230 -0
- package/dist/execution/index.d.ts +4 -0
- package/dist/execution/index.js +3 -0
- package/dist/execution/sdk-alias.d.ts +33 -0
- package/dist/execution/sdk-alias.js +102 -0
- package/dist/fixtures/compiler.d.ts +47 -0
- package/dist/fixtures/compiler.js +177 -0
- package/dist/fixtures/defaults/comment-created.json +30 -0
- package/dist/fixtures/defaults/create-branch.json +17 -0
- package/dist/fixtures/defaults/delete-branch.json +15 -0
- package/dist/fixtures/defaults/dispatch.json +15 -0
- package/dist/fixtures/defaults/fork.json +19 -0
- package/dist/fixtures/defaults/index.d.ts +16 -0
- package/dist/fixtures/defaults/index.js +105 -0
- package/dist/fixtures/defaults/index.ts +145 -0
- package/dist/fixtures/defaults/pr-closed.json +28 -0
- package/dist/fixtures/defaults/pr-opened.json +27 -0
- package/dist/fixtures/defaults/pr-reopened.json +27 -0
- package/dist/fixtures/defaults/pr-synchronize.json +27 -0
- package/dist/fixtures/defaults/push.json +16 -0
- package/dist/fixtures/defaults/release-published.json +23 -0
- package/dist/fixtures/defaults/review-comment-created.json +36 -0
- package/dist/fixtures/defaults/review-submitted.json +35 -0
- package/dist/fixtures/defaults/star-created.json +15 -0
- package/dist/fixtures/defaults/status.json +26 -0
- package/dist/fixtures/defaults/tag-push.json +20 -0
- package/dist/fixtures/defaults/watch-started.json +15 -0
- package/dist/fixtures/defaults/workflow-run-completed.json +23 -0
- package/dist/fork.json +19 -0
- package/dist/format.d.ts +5 -0
- package/dist/format.js +20 -0
- package/dist/generators/secrets-dts.d.ts +29 -0
- package/dist/generators/secrets-dts.js +67 -0
- package/dist/hooks/detector.d.ts +39 -0
- package/dist/hooks/detector.js +133 -0
- package/dist/hooks/index.d.ts +6 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/installer.d.ts +24 -0
- package/dist/hooks/installer.js +328 -0
- package/dist/hooks/templates.d.ts +26 -0
- package/dist/hooks/templates.js +63 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/llm-context/llms-full.txt +9974 -0
- package/dist/llm-context/llms.txt +61 -0
- package/dist/local-executor/dag-scheduler.d.ts +44 -0
- package/dist/local-executor/dag-scheduler.js +183 -0
- package/dist/local-executor/index.d.ts +23 -0
- package/dist/local-executor/index.js +309 -0
- package/dist/local-executor/job-runner.d.ts +40 -0
- package/dist/local-executor/job-runner.js +307 -0
- package/dist/local-executor/output-streamer.d.ts +31 -0
- package/dist/local-executor/output-streamer.js +166 -0
- package/dist/local-executor/payload-generator.d.ts +16 -0
- package/dist/local-executor/payload-generator.js +138 -0
- package/dist/local-executor/picker.d.ts +33 -0
- package/dist/local-executor/picker.js +109 -0
- package/dist/local-executor/secret-loader.d.ts +18 -0
- package/dist/local-executor/secret-loader.js +105 -0
- package/dist/local-executor/types.d.ts +80 -0
- package/dist/local-executor/types.js +2 -0
- package/dist/local-executor/workflow-lock.d.ts +81 -0
- package/dist/local-executor/workflow-lock.js +0 -0
- package/dist/lockfile/generator.d.ts +39 -0
- package/dist/lockfile/generator.js +688 -0
- package/dist/lockfile/hash-files.d.ts +14 -0
- package/dist/lockfile/hash-files.js +50 -0
- package/dist/lockfile/hasher.d.ts +36 -0
- package/dist/lockfile/hasher.js +46 -0
- package/dist/lockfile/index.d.ts +3 -0
- package/dist/lockfile/index.js +4 -0
- package/dist/lockfile/purity-analyzer.d.ts +25 -0
- package/dist/lockfile/purity-analyzer.js +204 -0
- package/dist/package-F7UXSDHW.json +74 -0
- package/dist/postinstall.d.ts +9 -0
- package/dist/postinstall.js +60 -0
- package/dist/pr-closed.json +28 -0
- package/dist/pr-opened.json +27 -0
- package/dist/pr-reopened.json +27 -0
- package/dist/pr-synchronize.json +27 -0
- package/dist/push.json +16 -0
- package/dist/release-published.json +23 -0
- package/dist/remote/client.d.ts +204 -0
- package/dist/remote/client.js +203 -0
- package/dist/remote/config.d.ts +54 -0
- package/dist/remote/config.js +92 -0
- package/dist/remote/encryption.d.ts +35 -0
- package/dist/remote/encryption.js +87 -0
- package/dist/remote/history.d.ts +91 -0
- package/dist/remote/history.js +146 -0
- package/dist/remote/oauth.d.ts +65 -0
- package/dist/remote/oauth.js +302 -0
- package/dist/remote/observer.d.ts +81 -0
- package/dist/remote/observer.js +174 -0
- package/dist/remote/output/json.d.ts +13 -0
- package/dist/remote/output/json.js +36 -0
- package/dist/remote/output/junit.d.ts +18 -0
- package/dist/remote/output/junit.js +50 -0
- package/dist/remote/output/streaming.d.ts +57 -0
- package/dist/remote/output/streaming.js +124 -0
- package/dist/remote/output/summary.d.ts +39 -0
- package/dist/remote/output/summary.js +99 -0
- package/dist/remote/uploader.d.ts +87 -0
- package/dist/remote/uploader.js +197 -0
- package/dist/review-comment-created.json +36 -0
- package/dist/review-submitted.json +35 -0
- package/dist/star-created.json +15 -0
- package/dist/status.json +26 -0
- package/dist/tag-push.json +20 -0
- package/dist/templates/agents-md.d.ts +8 -0
- package/dist/templates/agents-md.js +130 -0
- package/dist/templates/index.d.ts +43 -0
- package/dist/templates/index.js +48 -0
- package/dist/templates/package-json.d.ts +18 -0
- package/dist/templates/package-json.js +34 -0
- package/dist/templates/tsconfig-json.d.ts +9 -0
- package/dist/templates/tsconfig-json.js +26 -0
- package/dist/templates/workflows/hello-world.d.ts +2 -0
- package/dist/templates/workflows/hello-world.js +16 -0
- package/dist/templates/workflows/hello-world.ts +21 -0
- package/dist/templates/workflows/pr-checks.d.ts +2 -0
- package/dist/templates/workflows/pr-checks.js +40 -0
- package/dist/templates/workflows/pr-checks.ts +57 -0
- package/dist/test-runner/dry-run.d.ts +12 -0
- package/dist/test-runner/dry-run.js +66 -0
- package/dist/test-runner/event-types.d.ts +86 -0
- package/dist/test-runner/event-types.js +330 -0
- package/dist/test-runner/git-detector.d.ts +15 -0
- package/dist/test-runner/git-detector.js +30 -0
- package/dist/test-runner/index.d.ts +13 -0
- package/dist/test-runner/index.js +11 -0
- package/dist/test-runner/job-executor.d.ts +48 -0
- package/dist/test-runner/job-executor.js +245 -0
- package/dist/test-runner/output-formatter.d.ts +47 -0
- package/dist/test-runner/output-formatter.js +92 -0
- package/dist/test-runner/payload-builder.d.ts +20 -0
- package/dist/test-runner/payload-builder.js +216 -0
- package/dist/test-runner/rule-evaluator.d.ts +14 -0
- package/dist/test-runner/rule-evaluator.js +31 -0
- package/dist/test-runner/secrets-file.d.ts +24 -0
- package/dist/test-runner/secrets-file.js +62 -0
- package/dist/test-runner/step-context.d.ts +15 -0
- package/dist/test-runner/step-context.js +140 -0
- package/dist/test-runner/summary.d.ts +7 -0
- package/dist/types.d.ts +525 -0
- package/dist/types.js +33 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.js +3 -0
- package/dist/validation/validator.d.ts +26 -0
- package/dist/validation/validator.js +164 -0
- package/dist/watch-started.json +15 -0
- package/dist/workflow-run-completed.json +23 -0
- package/dist/workflows/hello-world.ts +21 -0
- package/dist/workflows/pr-checks.ts +57 -0
- package/hack/postinstall.mjs +105 -0
- package/package.json +61 -5
- package/sbom.spdx.json +10997 -0
- package/index.js +0 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# KiCI
|
|
2
|
+
|
|
3
|
+
> KiCI is a TypeScript-native CI/CD workflow engine. Workflows are defined in TypeScript (not YAML), compiled into a portable lock file, and executed by self-hosted agents. The docs below cover the SDK, the CLI, workflow patterns, and the runtime architecture an LLM coding agent needs in order to author and test KiCI workflows.
|
|
4
|
+
|
|
5
|
+
The full markdown bundle of every page indexed here is available at https://kici.dev/llms-full.txt.
|
|
6
|
+
|
|
7
|
+
## Getting started
|
|
8
|
+
|
|
9
|
+
- [User guide](https://kici.dev/docs/user/): Writing and testing CI/CD workflows in TypeScript
|
|
10
|
+
- [Getting started with KiCI](https://kici.dev/docs/user/getting-started/): Install the SDK, write your first workflow, compile and test locally
|
|
11
|
+
|
|
12
|
+
## Workflow patterns
|
|
13
|
+
|
|
14
|
+
- [Basic workflow patterns](https://kici.dev/docs/user/patterns/basic/): Basic CI, PR-only / push-only filters, multiple triggers, manual-only workflows
|
|
15
|
+
- [Conditionals & matrix patterns](https://kici.dev/docs/user/patterns/conditionals-matrix/): Conditional execution with rules, matrix builds (static + dynamic), dynamic job generation
|
|
16
|
+
- [Integration patterns](https://kici.dev/docs/user/patterns/integrations/): Workflow chaining, generic webhooks, Stripe, self-hosted git forges, plain GitHub repos
|
|
17
|
+
- [Pattern reference](https://kici.dev/docs/user/patterns/reference/): Step context, examples repository, GitHub check run output — cross-cutting reference for all patterns
|
|
18
|
+
- [Scheduling & event patterns](https://kici.dev/docs/user/patterns/scheduling-and-events/): Nightly cron, workflow-complete-triggered deploys, custom event chaining
|
|
19
|
+
|
|
20
|
+
## SDK reference
|
|
21
|
+
|
|
22
|
+
- [SDK reference: core](https://kici.dev/docs/user/sdk/core/): Factory functions (workflow, job, step) and authoring patterns: needs, output chaining, dynamic groups
|
|
23
|
+
- [SDK reference: idempotent](https://kici.dev/docs/user/sdk/idempotent/): Idempotent helpers for declarative check / apply patterns inside workflow steps
|
|
24
|
+
- [SDK reference: rules, matrix, dynamic jobs](https://kici.dev/docs/user/sdk/rules-matrix-dynamic/): rule(), skip(), matrix builds (static + dynamic), and dynamicJob / dynamicGroup
|
|
25
|
+
- [SDK reference: runtime](https://kici.dev/docs/user/sdk/runtime/): Types index, StepContext, secrets, fixtures
|
|
26
|
+
- [SDK reference: triggers](https://kici.dev/docs/user/sdk/triggers/): GitHub event triggers, kiciEvent, workflowComplete, jobComplete, genericWebhook, schedule, lifecycle
|
|
27
|
+
- [SDK reference: validation & events](https://kici.dev/docs/user/sdk/validation-events/): validateDag(), event definitions, and event emission
|
|
28
|
+
- [SDK reference: waitFor](https://kici.dev/docs/user/sdk/wait-for/): Polling helpers for steps that pause until an external condition becomes true
|
|
29
|
+
- [SDK reference](https://kici.dev/docs/user/sdk-reference/): Complete API reference for @kici-dev/sdk -- workflows, jobs, steps, triggers, rules, matrix, validation, runtime
|
|
30
|
+
|
|
31
|
+
## CLI and authoring
|
|
32
|
+
|
|
33
|
+
- [CLI authentication](https://kici.dev/docs/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
|
|
34
|
+
- [CLI reference](https://kici.dev/docs/user/cli-reference/): All CLI commands: compile, run (local/remote), test, login, logout, org, status, cancel, secrets, types, fixture, init, hook, endpoints, workflows, docs, admin
|
|
35
|
+
- [Lifecycle hooks](https://kici.dev/docs/user/hooks/): SDK hook API for cancel, cleanup, success, failure, and step-level callbacks
|
|
36
|
+
- [Lock file and workflow drift](https://kici.dev/docs/user/lock-file-and-drift/): Keep the lock file in sync with your workflow source and avoid drift
|
|
37
|
+
- [Testing guide](https://kici.dev/docs/user/testing-guide/): Running remote test fixtures with kici run remote
|
|
38
|
+
- [Workflow patterns](https://kici.dev/docs/user/workflow-patterns/): Common patterns: monorepo builds, conditional jobs, dynamic matrices, generic webhooks, scheduling
|
|
39
|
+
|
|
40
|
+
## Workflow features
|
|
41
|
+
|
|
42
|
+
- [Concurrency groups](https://kici.dev/docs/user/concurrency/): Control parallel execution with auto-cancel and queue modes
|
|
43
|
+
- [Dashboard](https://kici.dev/docs/user/dashboard/)
|
|
44
|
+
- [Dynamic values](https://kici.dev/docs/user/dynamic-values/)
|
|
45
|
+
- [Environment variables](https://kici.dev/docs/user/env-vars/): KICI_* environment variable reference for the CLI
|
|
46
|
+
- [Environments](https://kici.dev/docs/user/environments/): Configure deployment environments with variables, secrets, and protection rules
|
|
47
|
+
- [Event system](https://kici.dev/docs/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
|
|
48
|
+
- [Global workflows](https://kici.dev/docs/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
|
|
49
|
+
- [Private npm registries](https://kici.dev/docs/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
|
|
50
|
+
- [Secrets](https://kici.dev/docs/user/secrets/): How to access secrets in KiCI workflow steps
|
|
51
|
+
|
|
52
|
+
## Providers
|
|
53
|
+
|
|
54
|
+
- [GitHub App provider](https://kici.dev/docs/user/providers/github/): Connect KiCI to GitHub via a GitHub App — full Checks API, installation-token clones, and cross-org dispatch
|
|
55
|
+
- [Universal-git provider](https://kici.dev/docs/user/providers/universal-git/): Connect Forgejo, Gitea, Gogs, GitLab, or plain GitHub via webhook + PAT or SSH deploy key
|
|
56
|
+
|
|
57
|
+
## Architecture overview
|
|
58
|
+
|
|
59
|
+
- [Data flows](https://kici.dev/docs/architecture/data-flows/): End-to-end data flows through the KiCI three-tier architecture
|
|
60
|
+
- [Design decisions](https://kici.dev/docs/architecture/design-decisions/): Why KiCI's architecture is shaped the way it is
|
|
61
|
+
- [Architecture overview](https://kici.dev/docs/architecture/overview/): Three-tier relay model, package structure, and component responsibilities
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAG-based parallel job scheduler with concurrency control and fail-fast.
|
|
3
|
+
*
|
|
4
|
+
* Generic scheduler that takes nodes with dependency relationships and
|
|
5
|
+
* executes them via callbacks, respecting concurrency limits and
|
|
6
|
+
* providing abort signals for fail-fast cancellation.
|
|
7
|
+
*/
|
|
8
|
+
export interface DagNode {
|
|
9
|
+
name: string;
|
|
10
|
+
needs: string[];
|
|
11
|
+
}
|
|
12
|
+
interface DagExecutionCallbacks<T> {
|
|
13
|
+
execute: (name: string, signal: AbortSignal) => Promise<T>;
|
|
14
|
+
isSuccess: (result: T) => boolean;
|
|
15
|
+
}
|
|
16
|
+
interface DagOptions {
|
|
17
|
+
maxConcurrency: number;
|
|
18
|
+
failFast: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface DagResult<T> {
|
|
21
|
+
results: Map<string, T>;
|
|
22
|
+
skipped: string[];
|
|
23
|
+
cancelled: string[];
|
|
24
|
+
status: 'success' | 'failure';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Execute a DAG of nodes with bounded concurrency and fail-fast support.
|
|
28
|
+
*
|
|
29
|
+
* Algorithm:
|
|
30
|
+
* 1. Detect cycles
|
|
31
|
+
* 2. Initialize ready queue with zero-dependency nodes
|
|
32
|
+
* 3. Event loop: launch ready nodes up to concurrency limit,
|
|
33
|
+
* await first completion, update state, repeat
|
|
34
|
+
* 4. On failure: if failFast, abort all running and skip pending;
|
|
35
|
+
* if keep-going, only skip transitive dependents
|
|
36
|
+
*/
|
|
37
|
+
export declare function executeDag<T>(nodes: DagNode[], callbacks: DagExecutionCallbacks<T>, options: DagOptions): Promise<DagResult<T>>;
|
|
38
|
+
/**
|
|
39
|
+
* Given a list of DAG nodes and a target job name, return the target
|
|
40
|
+
* plus all transitive dependencies in topological order.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveJobFilter(nodes: DagNode[], targetName: string): DagNode[];
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=dag-scheduler.d.ts.map
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
//#region src/local-executor/dag-scheduler.ts
|
|
3
|
+
/**
|
|
4
|
+
* Detect cycles using Kahn's algorithm.
|
|
5
|
+
* Returns true if a cycle exists.
|
|
6
|
+
*/
|
|
7
|
+
function detectCycle(nodes) {
|
|
8
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
9
|
+
for (const n of nodes) {
|
|
10
|
+
if (!inDegree.has(n.name)) inDegree.set(n.name, 0);
|
|
11
|
+
for (const dep of n.needs) {
|
|
12
|
+
inDegree.set(n.name, (inDegree.get(n.name) ?? 0) + 1);
|
|
13
|
+
if (!inDegree.has(dep)) inDegree.set(dep, 0);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const queue = [];
|
|
17
|
+
for (const [name, deg] of inDegree) if (deg === 0) queue.push(name);
|
|
18
|
+
let processed = 0;
|
|
19
|
+
while (queue.length > 0) {
|
|
20
|
+
const name = queue.shift();
|
|
21
|
+
processed++;
|
|
22
|
+
for (const n of nodes) if (n.needs.includes(name)) {
|
|
23
|
+
const newDeg = inDegree.get(n.name) - 1;
|
|
24
|
+
inDegree.set(n.name, newDeg);
|
|
25
|
+
if (newDeg === 0) queue.push(n.name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return processed < nodes.length;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Execute a DAG of nodes with bounded concurrency and fail-fast support.
|
|
32
|
+
*
|
|
33
|
+
* Algorithm:
|
|
34
|
+
* 1. Detect cycles
|
|
35
|
+
* 2. Initialize ready queue with zero-dependency nodes
|
|
36
|
+
* 3. Event loop: launch ready nodes up to concurrency limit,
|
|
37
|
+
* await first completion, update state, repeat
|
|
38
|
+
* 4. On failure: if failFast, abort all running and skip pending;
|
|
39
|
+
* if keep-going, only skip transitive dependents
|
|
40
|
+
*/
|
|
41
|
+
async function executeDag(nodes, callbacks, options) {
|
|
42
|
+
if (nodes.length === 0) return {
|
|
43
|
+
results: /* @__PURE__ */ new Map(),
|
|
44
|
+
skipped: [],
|
|
45
|
+
cancelled: [],
|
|
46
|
+
status: "success"
|
|
47
|
+
};
|
|
48
|
+
if (detectCycle(nodes)) throw new Error("Circular dependency detected in job graph");
|
|
49
|
+
const nodeMap = new Map(nodes.map((n) => [n.name, n]));
|
|
50
|
+
const results = /* @__PURE__ */ new Map();
|
|
51
|
+
const skipped = [];
|
|
52
|
+
const cancelled = [];
|
|
53
|
+
const failedNames = /* @__PURE__ */ new Set();
|
|
54
|
+
const pending = new Set(nodes.map((n) => n.name));
|
|
55
|
+
const running = /* @__PURE__ */ new Map();
|
|
56
|
+
let hasFailure = false;
|
|
57
|
+
let abortAll = false;
|
|
58
|
+
/**
|
|
59
|
+
* Check if a node is ready to execute:
|
|
60
|
+
* all dependencies completed successfully.
|
|
61
|
+
*/
|
|
62
|
+
function isReady(name) {
|
|
63
|
+
return nodeMap.get(name).needs.every((dep) => results.has(dep) && callbacks.isSuccess(results.get(dep)));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if a node should be skipped because a dependency failed.
|
|
67
|
+
*/
|
|
68
|
+
function shouldSkip(name) {
|
|
69
|
+
return nodeMap.get(name).needs.some((dep) => failedNames.has(dep) || skipped.includes(dep) || cancelled.includes(dep));
|
|
70
|
+
}
|
|
71
|
+
while (pending.size > 0 || running.size > 0) {
|
|
72
|
+
if (abortAll) {
|
|
73
|
+
for (const name of pending) skipped.push(name);
|
|
74
|
+
pending.clear();
|
|
75
|
+
if (running.size > 0) {
|
|
76
|
+
const entries = [...running.entries()];
|
|
77
|
+
const settled = await Promise.allSettled(entries.map(([, v]) => v.promise));
|
|
78
|
+
for (let i = 0; i < entries.length; i++) {
|
|
79
|
+
const [name] = entries[i];
|
|
80
|
+
const s = settled[i];
|
|
81
|
+
if (s.status === "fulfilled") {
|
|
82
|
+
results.set(name, s.value);
|
|
83
|
+
if (!callbacks.isSuccess(s.value)) cancelled.push(name);
|
|
84
|
+
} else cancelled.push(name);
|
|
85
|
+
running.delete(name);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
let skipChanged = true;
|
|
91
|
+
while (skipChanged) {
|
|
92
|
+
skipChanged = false;
|
|
93
|
+
const toSkip = [];
|
|
94
|
+
for (const name of pending) if (shouldSkip(name)) toSkip.push(name);
|
|
95
|
+
for (const name of toSkip) {
|
|
96
|
+
pending.delete(name);
|
|
97
|
+
skipped.push(name);
|
|
98
|
+
skipChanged = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const readyNodes = [];
|
|
102
|
+
for (const name of pending) {
|
|
103
|
+
if (running.size + readyNodes.length >= options.maxConcurrency) break;
|
|
104
|
+
if (isReady(name)) readyNodes.push(name);
|
|
105
|
+
}
|
|
106
|
+
for (const name of readyNodes) {
|
|
107
|
+
pending.delete(name);
|
|
108
|
+
const controller = new AbortController();
|
|
109
|
+
const promise = callbacks.execute(name, controller.signal);
|
|
110
|
+
running.set(name, {
|
|
111
|
+
promise,
|
|
112
|
+
controller
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (running.size === 0) break;
|
|
116
|
+
const wrappedPromises = [...running.entries()].map(async ([name, { promise }]) => {
|
|
117
|
+
return {
|
|
118
|
+
name,
|
|
119
|
+
result: await promise
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
for (const p of wrappedPromises) p.catch(() => {});
|
|
123
|
+
const { name: completedName, result: completedResult } = await Promise.race(wrappedPromises);
|
|
124
|
+
running.delete(completedName);
|
|
125
|
+
results.set(completedName, completedResult);
|
|
126
|
+
if (!callbacks.isSuccess(completedResult)) {
|
|
127
|
+
hasFailure = true;
|
|
128
|
+
failedNames.add(completedName);
|
|
129
|
+
if (options.failFast) {
|
|
130
|
+
for (const [, { controller }] of running) controller.abort();
|
|
131
|
+
abortAll = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
results,
|
|
137
|
+
skipped,
|
|
138
|
+
cancelled,
|
|
139
|
+
status: hasFailure || skipped.length > 0 || cancelled.length > 0 ? "failure" : "success"
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Given a list of DAG nodes and a target job name, return the target
|
|
144
|
+
* plus all transitive dependencies in topological order.
|
|
145
|
+
*/
|
|
146
|
+
function resolveJobFilter(nodes, targetName) {
|
|
147
|
+
const nodeMap = new Map(nodes.map((n) => [n.name, n]));
|
|
148
|
+
if (!nodeMap.has(targetName)) return [];
|
|
149
|
+
const needed = /* @__PURE__ */ new Set();
|
|
150
|
+
const queue = [targetName];
|
|
151
|
+
while (queue.length > 0) {
|
|
152
|
+
const name = queue.shift();
|
|
153
|
+
if (needed.has(name)) continue;
|
|
154
|
+
needed.add(name);
|
|
155
|
+
const node = nodeMap.get(name);
|
|
156
|
+
if (node) {
|
|
157
|
+
for (const dep of node.needs) if (!needed.has(dep)) queue.push(dep);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const subset = nodes.filter((n) => needed.has(n.name));
|
|
161
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
162
|
+
for (const n of subset) {
|
|
163
|
+
if (!inDegree.has(n.name)) inDegree.set(n.name, 0);
|
|
164
|
+
for (const dep of n.needs) if (needed.has(dep)) inDegree.set(n.name, (inDegree.get(n.name) ?? 0) + 1);
|
|
165
|
+
}
|
|
166
|
+
const result = [];
|
|
167
|
+
const topoQueue = [];
|
|
168
|
+
for (const [name, deg] of inDegree) if (deg === 0) topoQueue.push(name);
|
|
169
|
+
while (topoQueue.length > 0) {
|
|
170
|
+
const name = topoQueue.shift();
|
|
171
|
+
result.push(nodeMap.get(name));
|
|
172
|
+
for (const n of subset) if (n.needs.includes(name) && needed.has(n.name)) {
|
|
173
|
+
const newDeg = inDegree.get(n.name) - 1;
|
|
174
|
+
inDegree.set(n.name, newDeg);
|
|
175
|
+
if (newDeg === 0) topoQueue.push(n.name);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
export { executeDag, resolveJobFilter };
|
|
182
|
+
|
|
183
|
+
//# sourceMappingURL=dag-scheduler.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API: executeLocal() orchestrating compile, match, resolve, schedule, report.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline:
|
|
5
|
+
* 1. Compile workflows from .kici/ sources
|
|
6
|
+
* 2. Load workflows (discover runtime modules)
|
|
7
|
+
* 3. Load secrets from local files
|
|
8
|
+
* 4. Build event payload from git state + CLI overrides
|
|
9
|
+
* 5. Match triggers to find active workflows
|
|
10
|
+
* 6. Apply --workflow and --job filters
|
|
11
|
+
* 7. Resolve jobs (matrix expansion, dynamic evaluation)
|
|
12
|
+
* 8. Execute via DAG scheduler with concurrency control
|
|
13
|
+
* 9. Return success/failure
|
|
14
|
+
*/
|
|
15
|
+
import type { RunLocalOptions } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Execute workflows locally -- the full compile-match-resolve-execute pipeline.
|
|
18
|
+
*
|
|
19
|
+
* @param options - CLI options from `kici run local`
|
|
20
|
+
* @returns true if all workflows succeeded, false otherwise
|
|
21
|
+
*/
|
|
22
|
+
export declare function executeLocal(options: RunLocalOptions): Promise<boolean>;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import "../chunk-gOLHoazu.js";
|
|
2
|
+
import { discoverWorkflows, resolveKiciDir } from "../execution/executor.js";
|
|
3
|
+
import "../execution/index.js";
|
|
4
|
+
import { transformTriggers } from "../lockfile/generator.js";
|
|
5
|
+
import { compileCommand } from "../commands/compile.js";
|
|
6
|
+
import { executeDag, resolveJobFilter } from "./dag-scheduler.js";
|
|
7
|
+
import { loadLocalSecrets } from "./secret-loader.js";
|
|
8
|
+
import { generateEventPayload } from "./payload-generator.js";
|
|
9
|
+
import { executeResolvedJob, resolveJobs } from "./job-runner.js";
|
|
10
|
+
import { displayLocalSummary, formatLocalJsonResult, formatLocalJunitResult } from "./output-streamer.js";
|
|
11
|
+
import { PickerCancelledError, runPicker } from "./picker.js";
|
|
12
|
+
import { ConcurrencyKeyEvaluationError, acquireWorkflowLock } from "./workflow-lock.js";
|
|
13
|
+
import pc from "picocolors";
|
|
14
|
+
import { writeFile } from "node:fs/promises";
|
|
15
|
+
import { logger } from "@kici-dev/shared";
|
|
16
|
+
import { matchAllWorkflows } from "@kici-dev/engine";
|
|
17
|
+
import os from "node:os";
|
|
18
|
+
//#region src/local-executor/index.ts
|
|
19
|
+
/**
|
|
20
|
+
* Public API: executeLocal() orchestrating compile, match, resolve, schedule, report.
|
|
21
|
+
*
|
|
22
|
+
* Pipeline:
|
|
23
|
+
* 1. Compile workflows from .kici/ sources
|
|
24
|
+
* 2. Load workflows (discover runtime modules)
|
|
25
|
+
* 3. Load secrets from local files
|
|
26
|
+
* 4. Build event payload from git state + CLI overrides
|
|
27
|
+
* 5. Match triggers to find active workflows
|
|
28
|
+
* 6. Apply --workflow and --job filters
|
|
29
|
+
* 7. Resolve jobs (matrix expansion, dynamic evaluation)
|
|
30
|
+
* 8. Execute via DAG scheduler with concurrency control
|
|
31
|
+
* 9. Return success/failure
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Convert SDK workflows to lock-file-like structure for trigger matching.
|
|
35
|
+
* Simplified version of the one in test.ts -- only needs trigger info.
|
|
36
|
+
*/
|
|
37
|
+
function workflowsToLockFormat(workflows) {
|
|
38
|
+
return workflows.map((w) => ({
|
|
39
|
+
name: w.name,
|
|
40
|
+
contentHash: "",
|
|
41
|
+
compileSchemaVersion: 0,
|
|
42
|
+
triggers: transformTriggers(w.on),
|
|
43
|
+
jobs: w.jobs.map((j, i) => {
|
|
44
|
+
if (typeof j === "function") return {
|
|
45
|
+
_type: "dynamic",
|
|
46
|
+
source: {
|
|
47
|
+
file: "",
|
|
48
|
+
index: i
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
_type: "static",
|
|
53
|
+
name: j.name,
|
|
54
|
+
runsOn: j.runsOn,
|
|
55
|
+
needs: j.needs?.map((n) => typeof n === "string" ? n : n.name) ?? [],
|
|
56
|
+
steps: j.steps.map((s) => {
|
|
57
|
+
if (typeof s === "function") return {
|
|
58
|
+
name: "",
|
|
59
|
+
hasOutputs: false
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
name: s.name,
|
|
63
|
+
hasOutputs: !!s.outputs
|
|
64
|
+
};
|
|
65
|
+
})
|
|
66
|
+
};
|
|
67
|
+
}),
|
|
68
|
+
rules: w.rules?.map((r, i) => ({
|
|
69
|
+
_type: "dynamic",
|
|
70
|
+
label: r.label,
|
|
71
|
+
source: {
|
|
72
|
+
file: "",
|
|
73
|
+
index: i
|
|
74
|
+
}
|
|
75
|
+
}))
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Derive `groupCtx.branch` for the workflow concurrency `group` callback.
|
|
80
|
+
* Mirrors the agent's semantics (`request.branch ?? request.ref`) so a
|
|
81
|
+
* `group({ branch })` function works identically locally and remotely.
|
|
82
|
+
*/
|
|
83
|
+
function deriveBranchForGroupCtx(event) {
|
|
84
|
+
if (event.targetBranch) return event.targetBranch;
|
|
85
|
+
if (event.sourceBranch) return event.sourceBranch;
|
|
86
|
+
const ref = event.payload?.ref;
|
|
87
|
+
if (typeof ref === "string") {
|
|
88
|
+
const m = /^refs\/heads\/(.+)$/.exec(ref);
|
|
89
|
+
if (m) return m[1];
|
|
90
|
+
}
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Execute one matched workflow under its concurrency lock (if any).
|
|
95
|
+
*
|
|
96
|
+
* Returns the result and a flag indicating whether it should count towards
|
|
97
|
+
* `allSucceeded`. Returns `null` when the workflow was filtered out (e.g. by
|
|
98
|
+
* `--job` removing every node).
|
|
99
|
+
*/
|
|
100
|
+
async function runOneMatchedWorkflow(workflow, ctx) {
|
|
101
|
+
const { options, isQuiet, event, secrets, kiciDir, concurrency, failFast } = ctx;
|
|
102
|
+
if (!isQuiet) logger.info(`\n${pc.bold("Running workflow:")} ${workflow.name}\n`);
|
|
103
|
+
let lockHandle = null;
|
|
104
|
+
try {
|
|
105
|
+
lockHandle = await acquireWorkflowLock({
|
|
106
|
+
workflowName: workflow.name,
|
|
107
|
+
workflow,
|
|
108
|
+
event: event.payload,
|
|
109
|
+
branch: deriveBranchForGroupCtx(event),
|
|
110
|
+
debug: options.debug
|
|
111
|
+
});
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (err instanceof ConcurrencyKeyEvaluationError) {
|
|
114
|
+
logger.error(pc.red(`Concurrency group evaluation failed for workflow "${workflow.name}": ${err.cause?.message ?? err.message}`));
|
|
115
|
+
return {
|
|
116
|
+
result: {
|
|
117
|
+
name: workflow.name,
|
|
118
|
+
status: "failure",
|
|
119
|
+
jobs: [],
|
|
120
|
+
durationMs: 0
|
|
121
|
+
},
|
|
122
|
+
succeeded: false
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
return await runWorkflowBody(workflow, ctx, options, secrets, kiciDir, concurrency, failFast);
|
|
129
|
+
} finally {
|
|
130
|
+
await lockHandle?.release();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The actual DAG-execution body for one workflow. Split out so
|
|
135
|
+
* {@link runOneMatchedWorkflow} can wrap it in a lock acquire/release.
|
|
136
|
+
*/
|
|
137
|
+
async function runWorkflowBody(workflow, ctx, options, secrets, kiciDir, concurrency, failFast) {
|
|
138
|
+
const { event } = ctx;
|
|
139
|
+
const resolvedJobs = await resolveJobs(workflow, event);
|
|
140
|
+
let dagNodes = resolvedJobs.map((r) => ({
|
|
141
|
+
name: r.expandedName,
|
|
142
|
+
needs: r.resolvedNeeds
|
|
143
|
+
}));
|
|
144
|
+
if (options.job) {
|
|
145
|
+
dagNodes = resolveJobFilter(dagNodes, options.job);
|
|
146
|
+
if (dagNodes.length === 0) return null;
|
|
147
|
+
}
|
|
148
|
+
const resolvedJobMap = new Map(resolvedJobs.map((r) => [r.expandedName, r]));
|
|
149
|
+
const dagNodeNames = new Set(dagNodes.map((n) => n.name));
|
|
150
|
+
const filteredResolvedJobs = resolvedJobs.filter((r) => dagNodeNames.has(r.expandedName));
|
|
151
|
+
const jobOutputsMap = /* @__PURE__ */ new Map();
|
|
152
|
+
const startTime = Date.now();
|
|
153
|
+
const dagResult = await executeDag(dagNodes, {
|
|
154
|
+
execute: async (name, signal) => {
|
|
155
|
+
const resolved = resolvedJobMap.get(name);
|
|
156
|
+
if (!resolved) return {
|
|
157
|
+
name,
|
|
158
|
+
status: "failure",
|
|
159
|
+
durationMs: 0,
|
|
160
|
+
steps: [],
|
|
161
|
+
error: /* @__PURE__ */ new Error(`Job "${name}" not found in resolved jobs`)
|
|
162
|
+
};
|
|
163
|
+
return executeResolvedJob(resolved, {
|
|
164
|
+
workflowName: workflow.name,
|
|
165
|
+
event,
|
|
166
|
+
secrets,
|
|
167
|
+
kiciDir,
|
|
168
|
+
jobOutputsMap,
|
|
169
|
+
signal
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
isSuccess: (result) => result.status === "success" || result.status === "skipped"
|
|
173
|
+
}, {
|
|
174
|
+
maxConcurrency: concurrency,
|
|
175
|
+
failFast
|
|
176
|
+
});
|
|
177
|
+
const durationMs = Date.now() - startTime;
|
|
178
|
+
const jobResults = [];
|
|
179
|
+
for (const rj of filteredResolvedJobs) {
|
|
180
|
+
const result = dagResult.results.get(rj.expandedName);
|
|
181
|
+
if (result) jobResults.push({
|
|
182
|
+
...result,
|
|
183
|
+
matrixValues: Object.keys(rj.matrixValues).length > 0 ? rj.matrixValues : void 0
|
|
184
|
+
});
|
|
185
|
+
else if (dagResult.skipped.includes(rj.expandedName)) jobResults.push({
|
|
186
|
+
name: rj.expandedName,
|
|
187
|
+
status: "skipped",
|
|
188
|
+
durationMs: 0,
|
|
189
|
+
steps: [],
|
|
190
|
+
matrixValues: Object.keys(rj.matrixValues).length > 0 ? rj.matrixValues : void 0
|
|
191
|
+
});
|
|
192
|
+
else if (dagResult.cancelled.includes(rj.expandedName)) jobResults.push({
|
|
193
|
+
name: rj.expandedName,
|
|
194
|
+
status: "cancelled",
|
|
195
|
+
durationMs: 0,
|
|
196
|
+
steps: [],
|
|
197
|
+
cancelled: true,
|
|
198
|
+
matrixValues: Object.keys(rj.matrixValues).length > 0 ? rj.matrixValues : void 0
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
result: {
|
|
203
|
+
name: workflow.name,
|
|
204
|
+
status: dagResult.status === "success" ? "success" : "failure",
|
|
205
|
+
jobs: jobResults,
|
|
206
|
+
durationMs
|
|
207
|
+
},
|
|
208
|
+
succeeded: dagResult.status === "success"
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Execute workflows locally -- the full compile-match-resolve-execute pipeline.
|
|
213
|
+
*
|
|
214
|
+
* @param options - CLI options from `kici run local`
|
|
215
|
+
* @returns true if all workflows succeeded, false otherwise
|
|
216
|
+
*/
|
|
217
|
+
async function executeLocal(options) {
|
|
218
|
+
const kiciDir = resolveKiciDir(options.kiciDir);
|
|
219
|
+
if (!await compileCommand({
|
|
220
|
+
kiciDir,
|
|
221
|
+
check: false,
|
|
222
|
+
verbose: options.debug ?? false
|
|
223
|
+
})) {
|
|
224
|
+
process.exitCode = 2;
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
const { workflows: workflowsWithSource } = await discoverWorkflows(kiciDir);
|
|
228
|
+
const workflows = workflowsWithSource.map((w) => w.workflow);
|
|
229
|
+
if (workflows.length === 0) {
|
|
230
|
+
logger.info(pc.yellow("No workflows found"));
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
if (options.pick) try {
|
|
234
|
+
const picked = await runPicker(workflows, { filterEvent: options.event });
|
|
235
|
+
options = {
|
|
236
|
+
...options,
|
|
237
|
+
event: picked.event,
|
|
238
|
+
workflow: picked.workflow
|
|
239
|
+
};
|
|
240
|
+
} catch (err) {
|
|
241
|
+
if (err instanceof PickerCancelledError) {
|
|
242
|
+
logger.info(pc.yellow(err.message));
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
throw err;
|
|
246
|
+
}
|
|
247
|
+
if (!options.event) {
|
|
248
|
+
logger.error(pc.red("Missing event argument. Pass an event (e.g. \"push\") or use --pick to select one."));
|
|
249
|
+
process.exitCode = 2;
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
const secrets = await loadLocalSecrets(kiciDir, options.env);
|
|
253
|
+
const event = await generateEventPayload(options.event, options);
|
|
254
|
+
const decisions = matchAllWorkflows(workflowsToLockFormat(workflows), event);
|
|
255
|
+
let matchedWorkflows = workflows.filter((_, i) => decisions[i]?.matched);
|
|
256
|
+
if (matchedWorkflows.length === 0) {
|
|
257
|
+
logger.info(pc.gray("No workflows matched the event"));
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
if (options.workflow) {
|
|
261
|
+
matchedWorkflows = matchedWorkflows.filter((w) => w.name === options.workflow);
|
|
262
|
+
if (matchedWorkflows.length === 0) {
|
|
263
|
+
logger.info(pc.yellow(`No workflow named "${options.workflow}" matched the event`));
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const isQuiet = Boolean(options.quiet || options.json);
|
|
268
|
+
const concurrency = options.concurrency ?? getDefaultConcurrency();
|
|
269
|
+
const failFast = !options.keepGoing;
|
|
270
|
+
let allSucceeded = true;
|
|
271
|
+
const workflowResults = [];
|
|
272
|
+
const ctx = {
|
|
273
|
+
options,
|
|
274
|
+
isQuiet,
|
|
275
|
+
event,
|
|
276
|
+
secrets,
|
|
277
|
+
kiciDir,
|
|
278
|
+
concurrency,
|
|
279
|
+
failFast
|
|
280
|
+
};
|
|
281
|
+
for (const workflow of matchedWorkflows) {
|
|
282
|
+
const outcome = await runOneMatchedWorkflow(workflow, ctx);
|
|
283
|
+
if (outcome === null) continue;
|
|
284
|
+
workflowResults.push(outcome.result);
|
|
285
|
+
if (!outcome.succeeded) allSucceeded = false;
|
|
286
|
+
}
|
|
287
|
+
if (options.json) console.log(formatLocalJsonResult(workflowResults));
|
|
288
|
+
else if (options.junit) {
|
|
289
|
+
const junitXml = formatLocalJunitResult(workflowResults);
|
|
290
|
+
await writeFile(options.junit, junitXml);
|
|
291
|
+
if (!isQuiet) logger.info(pc.green(`JUnit XML written to ${options.junit}`));
|
|
292
|
+
}
|
|
293
|
+
if (!isQuiet) displayLocalSummary(workflowResults);
|
|
294
|
+
return allSucceeded;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get default concurrency based on available CPU parallelism.
|
|
298
|
+
*/
|
|
299
|
+
function getDefaultConcurrency() {
|
|
300
|
+
try {
|
|
301
|
+
return os.availableParallelism();
|
|
302
|
+
} catch {
|
|
303
|
+
return os.cpus().length || 4;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
//#endregion
|
|
307
|
+
export { executeLocal };
|
|
308
|
+
|
|
309
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job resolution (matrix/dynamic) and single-job execution with step context.
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Static job resolution
|
|
6
|
+
* - Matrix expansion into multiple ResolvedJob instances
|
|
7
|
+
* - Dynamic job function evaluation
|
|
8
|
+
* - Single-job step-by-step execution with rules, abort, and output chaining
|
|
9
|
+
*/
|
|
10
|
+
import type { Workflow, OutputsMap } from '@kici-dev/sdk';
|
|
11
|
+
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
12
|
+
import type { ParsedSecrets } from '../test-runner/secrets-file.js';
|
|
13
|
+
import type { ResolvedJob, LocalJobResult } from './types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Context passed to executeResolvedJob for single-job execution.
|
|
16
|
+
*/
|
|
17
|
+
interface JobExecutionContext {
|
|
18
|
+
workflowName: string;
|
|
19
|
+
event: SimulatedEvent;
|
|
20
|
+
secrets: ParsedSecrets;
|
|
21
|
+
kiciDir: string;
|
|
22
|
+
jobOutputsMap: OutputsMap;
|
|
23
|
+
signal: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve all jobs in a workflow, expanding matrix jobs and evaluating dynamic jobs.
|
|
27
|
+
*
|
|
28
|
+
* - Static jobs: create ResolvedJob with name, needs, job reference
|
|
29
|
+
* - Matrix jobs: use expandMatrix() from SDK, create one ResolvedJob per combination
|
|
30
|
+
* - Dynamic job functions: evaluate the function, recursively resolve results
|
|
31
|
+
* - Fan-in: if a job needs a matrix-expanded job, it needs ALL instances
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveJobs(workflow: Workflow, event: SimulatedEvent): Promise<ResolvedJob[]>;
|
|
34
|
+
/**
|
|
35
|
+
* Execute a single resolved job: evaluate rules, run steps sequentially,
|
|
36
|
+
* check abort signal between steps, collect outputs.
|
|
37
|
+
*/
|
|
38
|
+
export declare function executeResolvedJob(resolvedJob: ResolvedJob, context: JobExecutionContext): Promise<LocalJobResult>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=job-runner.d.ts.map
|