@kici-dev/sdk 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-cloud-init.d.ts +66 -0
- package/dist/agent-cloud-init.js +128 -0
- package/dist/api-types.d.ts +26 -0
- package/dist/api-types.js +20 -1
- package/dist/context.d.ts +21 -0
- package/dist/filter-context.d.ts +32 -0
- package/dist/filter-context.js +36 -0
- package/dist/filter.d.ts +81 -0
- package/dist/filter.js +2 -0
- package/dist/filter.test-d.d.ts +2 -0
- package/dist/filter.test-d.js +30 -0
- package/dist/git-types.d.ts +162 -0
- package/dist/git-types.js +32 -0
- package/dist/idempotent.js +3 -2
- package/dist/index.d.ts +11 -1
- package/dist/index.js +11 -5
- package/dist/invoke.d.ts +29 -0
- package/dist/invoke.js +27 -0
- package/dist/job-outputs.test-d.js +32 -18
- package/dist/job.js +93 -1
- package/dist/needs-context.d.ts +31 -2
- package/dist/needs-context.js +3 -1
- package/dist/rules/changed-files.d.ts +37 -0
- package/dist/rules/changed-files.js +54 -0
- package/dist/rules/context.d.ts +12 -11
- package/dist/rules/context.js +7 -25
- package/dist/rules/evaluator.js +2 -1
- package/dist/rules/index.js +2 -1
- package/dist/rules/types.d.ts +9 -0
- package/dist/testing/step-context.js +4 -2
- package/dist/triggers/pr.js +2 -0
- package/dist/triggers/push.js +2 -0
- package/dist/triggers/tag.js +2 -0
- package/dist/triggers/types.d.ts +26 -0
- package/dist/types.d.ts +119 -3
- package/dist/workflow.js +2 -0
- package/package.json +4 -3
- package/sbom.spdx.json +64 -34
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import "./rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/git-types.ts
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a call site's credential.
|
|
5
|
+
*
|
|
6
|
+
* Order: an explicit per-call value wins; otherwise `default` from the job map;
|
|
7
|
+
* otherwise undefined, which means the source credential — all a read needs.
|
|
8
|
+
*
|
|
9
|
+
* An unknown name THROWS rather than falling back to `default`: silently using
|
|
10
|
+
* a different credential than the author named is precisely the confusion this
|
|
11
|
+
* map exists to remove.
|
|
12
|
+
*/
|
|
13
|
+
function resolveCredential(perCall, map) {
|
|
14
|
+
if (perCall && typeof perCall === "object") return perCall;
|
|
15
|
+
if (typeof perCall === "string") {
|
|
16
|
+
const found = map?.[perCall];
|
|
17
|
+
if (!found) throw new Error(`Unknown git credential '${perCall}'. Declare it in the job's gitCredentials map. Known: ${Object.keys(map ?? {}).join(", ") || "(none)"}`);
|
|
18
|
+
return found;
|
|
19
|
+
}
|
|
20
|
+
return map?.default;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Every credential field names a secret. Catch the easy, silent mistake of
|
|
24
|
+
* pasting the credential itself — which would commit it to a git repository.
|
|
25
|
+
*/
|
|
26
|
+
function assertSecretName(value, field, subject = "git credential") {
|
|
27
|
+
if (value.startsWith("-----BEGIN") || /^gh[pousr]_/.test(value) || value.startsWith("github_pat_")) throw new Error(`${subject} '${field}' looks like the credential itself, not the name of a secret holding it. Store it with \`kici-admin secret set\` and name it here.`);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { assertSecretName, resolveCredential };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=git-types.js.map
|
package/dist/idempotent.js
CHANGED
|
@@ -83,7 +83,7 @@ function idempotentStep(name, opts) {
|
|
|
83
83
|
* lockfile change.
|
|
84
84
|
*/
|
|
85
85
|
function checkStep(name, options) {
|
|
86
|
-
|
|
86
|
+
const checkOptions = {
|
|
87
87
|
check: options.check,
|
|
88
88
|
summarize: options.summarize,
|
|
89
89
|
run: (ctx, drift) => options.apply(ctx, drift),
|
|
@@ -94,7 +94,8 @@ function checkStep(name, options) {
|
|
|
94
94
|
...options.retry !== void 0 && { retry: options.retry },
|
|
95
95
|
...options.cache !== void 0 && { cache: options.cache },
|
|
96
96
|
...options.rules !== void 0 && { rules: options.rules }
|
|
97
|
-
}
|
|
97
|
+
};
|
|
98
|
+
return step(name, checkOptions);
|
|
98
99
|
}
|
|
99
100
|
//#endregion
|
|
100
101
|
export { checkStep, idempotent, idempotentStep };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export { job } from './job.js';
|
|
|
3
3
|
export { workflow } from './workflow.js';
|
|
4
4
|
export { parallel, isParallelGroup, flattenStepInputs } from './parallel.js';
|
|
5
5
|
export type { ParallelGroup, ParallelOptions } from './parallel.js';
|
|
6
|
+
export { invokeSource } from './invoke.js';
|
|
7
|
+
export type { InvokeConfig } from './invoke.js';
|
|
6
8
|
export { normalizeApproval } from './approval.js';
|
|
7
9
|
export type { ApprovalConfig, ApprovalWhen, ApproverClause, NormalizedApproval, } from './approval.js';
|
|
8
10
|
export { pr, push, tag, comment, review, reviewComment, release, dispatch, create, delete as delete, status, workflowRun, fork, star, watch, webhook, kiciEvent, workflowComplete, workflowsFailedBatch, jobComplete, genericWebhook, schedule, lifecycle, defineDispatchInputs, } from './triggers/index.js';
|
|
@@ -21,7 +23,7 @@ export type { SourceLocation, OutputProxy, Step, StepOptions, StepOptionsBase, S
|
|
|
21
23
|
export { isDynamicJobFn, dynamicJob, getDynamicJobGroup, getDynamicJobNeeds, DYNAMIC_JOB_GROUP_TAG, DYNAMIC_JOB_NEEDS_TAG, } from './types.js';
|
|
22
24
|
export type { TaggedDynamicJobFn, ResultAwareDynamicJobConfig, ResultAwareDynamicJobFn, DynamicJobNeed, NeedsWhen, NeedsWhenInput, } from './types.js';
|
|
23
25
|
export { buildNeedsContext } from './needs-context.js';
|
|
24
|
-
export type { UpstreamSnapshot, NeedsContext, NeedEntry, GroupNeedEntry } from './needs-context.js';
|
|
26
|
+
export type { UpstreamSnapshot, NeedsContext, NeedEntry, GroupNeedEntry, InvokeNeedEntry, InvokeResult, } from './needs-context.js';
|
|
25
27
|
export { CacheSpecSchema, normalizeCacheSpecs } from './cache-types.js';
|
|
26
28
|
export type { CacheSpec, CacheInput } from './cache-types.js';
|
|
27
29
|
export type { CacheRestoreResult, CacheApi } from './cache-types.js';
|
|
@@ -33,6 +35,9 @@ export { dynamicGroup, isDynamicGroupRef, DYNAMIC_GROUP_TAG } from './dynamic-gr
|
|
|
33
35
|
export type { DynamicGroupRef } from './dynamic-group.js';
|
|
34
36
|
export type { StepContext, Logger, WorkflowInfo, JobInfo, AgentInfo, MatrixJobOutputs, HostJobOutputs, RepoInfo, StepSecretsTyped, KnownSecretKeys, } from './context.js';
|
|
35
37
|
export { isMatrixJobOutputs, isHostJobOutputs } from './context.js';
|
|
38
|
+
export type { FilterContext, FilterFn } from './filter.js';
|
|
39
|
+
export { createFilterContext } from './filter-context.js';
|
|
40
|
+
export type { CreateFilterContextInput } from './filter-context.js';
|
|
36
41
|
export type { FanoutPosition } from './fanout-context.js';
|
|
37
42
|
export { buildKiciApi } from './api-types.js';
|
|
38
43
|
export type { KiciApi, KiciApiTransport, InfrastructureApi, InfrastructureListResult, InventoryApi, HostApi, HostInventoryEntry, InventorySelector, } from './api-types.js';
|
|
@@ -58,4 +63,9 @@ export type { WaitForHostAliveOptions, RestartHostOptions } from './host-restart
|
|
|
58
63
|
export { agentVersionConverge } from './fleet/agent-version-converge.js';
|
|
59
64
|
export type { AgentVersionConvergeOptions, AgentVersionDrift, } from './fleet/agent-version-converge.js';
|
|
60
65
|
export { z } from 'zod';
|
|
66
|
+
export { SCALER_EVENT_NAMES, ScaleDownReason, ScalerScaleUpPayload, ScalerScaleDownPayload, } from '@kici-dev/engine';
|
|
67
|
+
export { buildAgentCloudInit } from './agent-cloud-init.js';
|
|
68
|
+
export type { CloudInitCredentials, ClaimCodeCredentials, AgentCloudInitCredentials, AgentDeliveryMode, UserDataEncoding, CloudInitWriteFile, AgentCloudInitOptions, } from './agent-cloud-init.js';
|
|
69
|
+
export { assertSecretName } from './git-types.js';
|
|
70
|
+
export type { ForgeName, GitHubPermissions, WriteOptions, GitGrant, Sourced, ContainerRegistryAuth, GitCredentialRef, GitCredentialMap, RepoHandle, GitTokenResult, GitHubApi, GitApi, } from './git-types.js';
|
|
61
71
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "./rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { buildAgentCloudInit } from "./agent-cloud-init.js";
|
|
2
3
|
import { buildKiciApi } from "./api-types.js";
|
|
3
4
|
import { normalizeApproval } from "./approval.js";
|
|
4
5
|
import { ARTIFACT_NAME_MAX_LENGTH, ArtifactNameSchema } from "./artifacts-types.js";
|
|
@@ -6,15 +7,19 @@ import { CacheSpecSchema, normalizeCacheSpecs } from "./cache-types.js";
|
|
|
6
7
|
import { isHostJobOutputs, isMatrixJobOutputs } from "./context.js";
|
|
7
8
|
import { DYNAMIC_GROUP_TAG, dynamicGroup, isDynamicGroupRef } from "./dynamic-group.js";
|
|
8
9
|
import { SecretNotFoundError } from "./errors.js";
|
|
9
|
-
import {
|
|
10
|
+
import { ChangedFilesUnavailableError } from "./rules/changed-files.js";
|
|
11
|
+
import { createFilterContext } from "./filter-context.js";
|
|
12
|
+
import { workflow } from "./workflow.js";
|
|
10
13
|
import { createJobOutputProxy, createSnapshotOutputProxy, createStepOutputProxy, getJobOutputsMap, getStepOutputsMap, getStepRefMap, resolveJobOutputs, resolveStepOutputs, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "./outputs.js";
|
|
14
|
+
import { assertSecretName } from "./git-types.js";
|
|
15
|
+
import { job } from "./job.js";
|
|
11
16
|
import { step } from "./step.js";
|
|
17
|
+
import { fixture } from "./fixture.js";
|
|
12
18
|
import { WaitForTimeoutError, waitFor, waitForStep } from "./wait-for.js";
|
|
13
19
|
import { restartHost, waitForHostAlive } from "./host-restart.js";
|
|
14
20
|
import { checkStep, idempotent, idempotentStep } from "./idempotent.js";
|
|
15
|
-
import { job } from "./job.js";
|
|
16
|
-
import { workflow } from "./workflow.js";
|
|
17
21
|
import { flattenStepInputs, isParallelGroup, parallel } from "./parallel.js";
|
|
22
|
+
import { invokeSource } from "./invoke.js";
|
|
18
23
|
import { pr } from "./triggers/pr.js";
|
|
19
24
|
import { push } from "./triggers/push.js";
|
|
20
25
|
import { tag } from "./triggers/tag.js";
|
|
@@ -42,7 +47,7 @@ import { defineDispatchInputs } from "./triggers/dispatch-inputs.js";
|
|
|
42
47
|
import "./triggers/index.js";
|
|
43
48
|
import { afterStep, beforeStep, cleanup, onCancel, onFailure, onSuccess } from "./hooks/index.js";
|
|
44
49
|
import { onlyOnFanoutIndex, onlyOnFirstHost, onlyOnLastHost, rule, skip } from "./rules/rule.js";
|
|
45
|
-
import {
|
|
50
|
+
import { createRuleContext } from "./rules/context.js";
|
|
46
51
|
import { evaluateRules } from "./rules/evaluator.js";
|
|
47
52
|
import { isEventType } from "./events/event-payloads.js";
|
|
48
53
|
import "./rules/index.js";
|
|
@@ -58,5 +63,6 @@ import "./matrix/index.js";
|
|
|
58
63
|
import { defineEvent, isEventDefinition } from "./events/define-event.js";
|
|
59
64
|
import "./events/index.js";
|
|
60
65
|
import { agentVersionConverge } from "./fleet/agent-version-converge.js";
|
|
66
|
+
import { SCALER_EVENT_NAMES, ScaleDownReason, ScalerScaleDownPayload, ScalerScaleUpPayload } from "@kici-dev/engine";
|
|
61
67
|
import { z } from "zod";
|
|
62
|
-
export { ARTIFACT_NAME_MAX_LENGTH, ArtifactNameSchema, CacheSpecSchema, ChangedFilesUnavailableError, DYNAMIC_GROUP_TAG, DYNAMIC_JOB_GROUP_TAG, DYNAMIC_JOB_NEEDS_TAG, SecretNotFoundError, WaitForTimeoutError, afterStep, agentVersionConverge, applyIncludeExclude, beforeStep, buildKiciApi, buildNeedsContext, checkStep, cleanup, comment, create, createJobOutputProxy, createRuleContext, createSnapshotOutputProxy, createStepOutputProxy, createStepSecrets, defineDispatchInputs, defineEvent, del as delete, dispatch, dynamicGroup, dynamicJob, evaluateRules, expandMatrix, fixture, flattenStepInputs, fork, genericWebhook, getDynamicJobGroup, getDynamicJobNeeds, getJobOutputsMap, getStepOutputsMap, getStepRefMap, idempotent, idempotentStep, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isEventDefinition, isEventType, isHostJobOutputs, isMatrixJobOutputs, isParallelGroup, isStaticArray, isStaticObject, job, jobComplete, kiciEvent, lifecycle, normalizeApproval, normalizeCacheSpecs, onCancel, onFailure, onSuccess, onlyOnFanoutIndex, onlyOnFirstHost, onlyOnLastHost, parallel, pr, provenanceSubjectIsPath, push, release, resolveJobOutputs, resolveStepOutputs, restartHost, review, reviewComment, rule, schedule, setJobOutputsMap, setStepOutputsMap, setStepRefMap, skip, star, status, step, tag, validateDag, waitFor, waitForHostAlive, waitForStep, watch, webhook, workflow, workflowComplete, workflowRun, workflowsFailedBatch, z };
|
|
68
|
+
export { ARTIFACT_NAME_MAX_LENGTH, ArtifactNameSchema, CacheSpecSchema, ChangedFilesUnavailableError, DYNAMIC_GROUP_TAG, DYNAMIC_JOB_GROUP_TAG, DYNAMIC_JOB_NEEDS_TAG, SCALER_EVENT_NAMES, ScaleDownReason, ScalerScaleDownPayload, ScalerScaleUpPayload, SecretNotFoundError, WaitForTimeoutError, afterStep, agentVersionConverge, applyIncludeExclude, assertSecretName, beforeStep, buildAgentCloudInit, buildKiciApi, buildNeedsContext, checkStep, cleanup, comment, create, createFilterContext, createJobOutputProxy, createRuleContext, createSnapshotOutputProxy, createStepOutputProxy, createStepSecrets, defineDispatchInputs, defineEvent, del as delete, dispatch, dynamicGroup, dynamicJob, evaluateRules, expandMatrix, fixture, flattenStepInputs, fork, genericWebhook, getDynamicJobGroup, getDynamicJobNeeds, getJobOutputsMap, getStepOutputsMap, getStepRefMap, idempotent, idempotentStep, invokeSource, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isEventDefinition, isEventType, isHostJobOutputs, isMatrixJobOutputs, isParallelGroup, isStaticArray, isStaticObject, job, jobComplete, kiciEvent, lifecycle, normalizeApproval, normalizeCacheSpecs, onCancel, onFailure, onSuccess, onlyOnFanoutIndex, onlyOnFirstHost, onlyOnLastHost, parallel, pr, provenanceSubjectIsPath, push, release, resolveJobOutputs, resolveStepOutputs, restartHost, review, reviewComment, rule, schedule, setJobOutputsMap, setStepOutputsMap, setStepRefMap, skip, star, status, step, tag, validateDag, waitFor, waitForHostAlive, waitForStep, watch, webhook, workflow, workflowComplete, workflowRun, workflowsFailedBatch, z };
|
package/dist/invoke.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Config produced by {@link invokeSource}: emit a kici event at the source repo and gate on the runs it triggers. */
|
|
2
|
+
export interface InvokeConfig {
|
|
3
|
+
readonly _tag: 'InvokeSource';
|
|
4
|
+
/** The kici event name to emit. Subscribers opt in with `kiciEvent({ name })`. */
|
|
5
|
+
readonly event: string;
|
|
6
|
+
/** Target scope. `'source'` targets exactly `ctx.sourceRepo` (the only v1 scope). */
|
|
7
|
+
readonly scope: 'source';
|
|
8
|
+
/** Optional event payload delivered to subscribers. */
|
|
9
|
+
readonly payload?: Readonly<Record<string, unknown>>;
|
|
10
|
+
/**
|
|
11
|
+
* When true, a zero-subscriber emit succeeds immediately (the repo may opt out).
|
|
12
|
+
* When false/unset (the default), a zero-subscriber emit FAILS the gate — a repo
|
|
13
|
+
* that never wired up its tests must not silently pass the org gate.
|
|
14
|
+
*/
|
|
15
|
+
readonly optional?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Invoke the source repo's opt-in workflows and gate on them.
|
|
19
|
+
*
|
|
20
|
+
* Targets exactly `ctx.sourceRepo`, so a global workflow can hand control back to
|
|
21
|
+
* the repo whose event triggered it without any org-wide fan-out. Subscribers opt
|
|
22
|
+
* in with `kiciEvent({ name })`. Required by default: if no workflow subscribes,
|
|
23
|
+
* the gate fails unless `optional: true` is set.
|
|
24
|
+
*/
|
|
25
|
+
export declare function invokeSource(event: string, opts?: {
|
|
26
|
+
payload?: Record<string, unknown>;
|
|
27
|
+
optional?: boolean;
|
|
28
|
+
}): InvokeConfig;
|
|
29
|
+
//# sourceMappingURL=invoke.d.ts.map
|
package/dist/invoke.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import "./rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { reservedEventNamePrefix } from "@kici-dev/engine";
|
|
3
|
+
//#region src/invoke.ts
|
|
4
|
+
/**
|
|
5
|
+
* Invoke the source repo's opt-in workflows and gate on them.
|
|
6
|
+
*
|
|
7
|
+
* Targets exactly `ctx.sourceRepo`, so a global workflow can hand control back to
|
|
8
|
+
* the repo whose event triggered it without any org-wide fan-out. Subscribers opt
|
|
9
|
+
* in with `kiciEvent({ name })`. Required by default: if no workflow subscribes,
|
|
10
|
+
* the gate fails unless `optional: true` is set.
|
|
11
|
+
*/
|
|
12
|
+
function invokeSource(event, opts) {
|
|
13
|
+
if (typeof event !== "string" || event.trim().length === 0) throw new Error("invokeSource: event name must be a non-empty string");
|
|
14
|
+
const reservedPrefix = reservedEventNamePrefix(event);
|
|
15
|
+
if (reservedPrefix) throw new Error(`invokeSource: event name prefix "${reservedPrefix}" is reserved for KiCI internal events (got "${event}")`);
|
|
16
|
+
return Object.freeze({
|
|
17
|
+
_tag: "InvokeSource",
|
|
18
|
+
event,
|
|
19
|
+
scope: "source",
|
|
20
|
+
...opts?.payload !== void 0 && { payload: Object.freeze({ ...opts.payload }) },
|
|
21
|
+
...opts?.optional === true && { optional: true }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { invokeSource };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=invoke.js.map
|
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import "./rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { isHostJobOutputs, isMatrixJobOutputs } from "./context.js";
|
|
3
3
|
import { dynamicGroup } from "./dynamic-group.js";
|
|
4
|
-
import { step } from "./step.js";
|
|
5
|
-
import { job } from "./job.js";
|
|
6
4
|
import { workflow } from "./workflow.js";
|
|
5
|
+
import { job } from "./job.js";
|
|
6
|
+
import { step } from "./step.js";
|
|
7
7
|
import { describe, expectTypeOf, it } from "vitest";
|
|
8
8
|
//#region src/job-outputs.test-d.ts
|
|
9
9
|
describe("Job<TOutputs> — merged-steps inference", () => {
|
|
10
10
|
it("infers a nested output map keyed by step name for a multi-step job", () => {
|
|
11
|
+
const build = step("build", { run: async () => ({ version: "1.0" }) });
|
|
12
|
+
const test_ = step("test", { run: async () => ({ passed: true }) });
|
|
11
13
|
const j = job("ci", {
|
|
12
14
|
runsOn: "kici:os:linux",
|
|
13
|
-
steps: [
|
|
15
|
+
steps: [build, test_]
|
|
14
16
|
});
|
|
15
17
|
expectTypeOf(j.result.build.version).toEqualTypeOf();
|
|
16
18
|
expectTypeOf(j.result.test.passed).toEqualTypeOf();
|
|
17
19
|
});
|
|
18
20
|
it("rejects reading an unknown output field of a declared step", () => {
|
|
21
|
+
const build = step("build", { run: async () => ({ version: "1.0" }) });
|
|
19
22
|
job("ci", {
|
|
20
23
|
runsOn: "kici:os:linux",
|
|
21
|
-
steps: [
|
|
24
|
+
steps: [build]
|
|
22
25
|
}).result.build.nope;
|
|
23
26
|
});
|
|
24
27
|
it("rejects reading an undeclared step name", () => {
|
|
28
|
+
const build = step("build", { run: async () => ({ version: "1.0" }) });
|
|
25
29
|
job("ci", {
|
|
26
30
|
runsOn: "kici:os:linux",
|
|
27
|
-
steps: [
|
|
31
|
+
steps: [build]
|
|
28
32
|
}).result.other;
|
|
29
33
|
});
|
|
30
34
|
it("infers a flat output shape for the run: shorthand", () => {
|
|
@@ -36,22 +40,26 @@ describe("Job<TOutputs> — merged-steps inference", () => {
|
|
|
36
40
|
flat.result.nope;
|
|
37
41
|
});
|
|
38
42
|
it("honours an explicit output-type override for a dynamically-shaped job", () => {
|
|
39
|
-
|
|
43
|
+
const dyn = job("dyn", {
|
|
40
44
|
runsOn: "kici:os:linux",
|
|
41
45
|
steps: [step(async () => ({}))]
|
|
42
|
-
})
|
|
46
|
+
});
|
|
47
|
+
expectTypeOf(dyn.result.n).toEqualTypeOf();
|
|
43
48
|
});
|
|
44
49
|
it("omits id-less steps and falls back to the loose shape", () => {
|
|
45
|
-
|
|
50
|
+
const j = job("idless", {
|
|
46
51
|
runsOn: "kici:os:linux",
|
|
47
52
|
steps: [step(async () => ({ x: 1 }))]
|
|
48
|
-
})
|
|
53
|
+
});
|
|
54
|
+
expectTypeOf(j.result.anything).toEqualTypeOf();
|
|
49
55
|
});
|
|
50
56
|
it("keeps a typed job assignable to the bare Job type", () => {
|
|
51
|
-
|
|
57
|
+
const build = step("build", { run: async () => ({ version: "1.0" }) });
|
|
58
|
+
const anyJob = job("ci", {
|
|
52
59
|
runsOn: "kici:os:linux",
|
|
53
|
-
steps: [
|
|
54
|
-
})
|
|
60
|
+
steps: [build]
|
|
61
|
+
});
|
|
62
|
+
expectTypeOf(anyJob).toEqualTypeOf();
|
|
55
63
|
});
|
|
56
64
|
});
|
|
57
65
|
describe("Job<void> — void run-shorthand assignability", () => {
|
|
@@ -75,10 +83,11 @@ describe("Job<void> — void run-shorthand assignability", () => {
|
|
|
75
83
|
workflow("wf", { jobs: [setup] });
|
|
76
84
|
});
|
|
77
85
|
it("resolves result to never for a void run-shorthand job", () => {
|
|
78
|
-
|
|
86
|
+
const setup = job("setup", {
|
|
79
87
|
runsOn: "kici:os:linux",
|
|
80
88
|
run: async () => {}
|
|
81
|
-
})
|
|
89
|
+
});
|
|
90
|
+
expectTypeOf(setup.result).toEqualTypeOf();
|
|
82
91
|
});
|
|
83
92
|
it("leaves a value-returning run job untouched (regression guard)", () => {
|
|
84
93
|
const flat = job("flat", {
|
|
@@ -90,9 +99,11 @@ describe("Job<void> — void run-shorthand assignability", () => {
|
|
|
90
99
|
});
|
|
91
100
|
});
|
|
92
101
|
describe("typed ctx.needs from job references", () => {
|
|
102
|
+
const build = step("build", { run: async () => ({ version: "1.0" }) });
|
|
103
|
+
const test_ = step("test", { run: async () => ({ passed: true }) });
|
|
93
104
|
const ci = job("ci", {
|
|
94
105
|
runsOn: "kici:os:linux",
|
|
95
|
-
steps: [
|
|
106
|
+
steps: [build, test_]
|
|
96
107
|
});
|
|
97
108
|
it("threads a referenced job’s inferred outputs into ctx.needs", () => {
|
|
98
109
|
job("deploy", {
|
|
@@ -135,13 +146,15 @@ describe("typed ctx.needs from job references", () => {
|
|
|
135
146
|
});
|
|
136
147
|
});
|
|
137
148
|
describe("envelope-generic jobOutputs for typed refs", () => {
|
|
149
|
+
const build = step("build", { run: async () => ({ version: "1.0" }) });
|
|
138
150
|
const ci = job("ci", {
|
|
139
151
|
runsOn: "kici:os:linux",
|
|
140
|
-
steps: [
|
|
152
|
+
steps: [build]
|
|
141
153
|
});
|
|
142
154
|
const ctx = null;
|
|
143
155
|
it("types a job-ref jobOutputs as the plain | matrix | host union of the inferred shape", () => {
|
|
144
|
-
|
|
156
|
+
const out = ctx.jobOutputs(ci);
|
|
157
|
+
expectTypeOf(out).toEqualTypeOf();
|
|
145
158
|
});
|
|
146
159
|
it("narrows to the typed matrix envelope via isMatrixJobOutputs", () => {
|
|
147
160
|
const out = ctx.jobOutputs(ci);
|
|
@@ -155,7 +168,8 @@ describe("envelope-generic jobOutputs for typed refs", () => {
|
|
|
155
168
|
if (isHostJobOutputs(out)) expectTypeOf(out.byHost).toEqualTypeOf();
|
|
156
169
|
});
|
|
157
170
|
it("keeps a string-name ref on the loose union", () => {
|
|
158
|
-
|
|
171
|
+
const out = ctx.jobOutputs({ name: "ci" });
|
|
172
|
+
expectTypeOf(out).toEqualTypeOf();
|
|
159
173
|
});
|
|
160
174
|
});
|
|
161
175
|
//#endregion
|
package/dist/job.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { createJobOutputProxy } from "./outputs.js";
|
|
3
|
+
import { assertSecretName } from "./git-types.js";
|
|
3
4
|
import { isKnownCapability } from "@kici-dev/engine";
|
|
4
5
|
import { randomUUID } from "node:crypto";
|
|
5
6
|
//#region src/job.ts
|
|
@@ -38,6 +39,7 @@ function validateSandbox(sandbox, jobName) {
|
|
|
38
39
|
function job(nameOrOptions, maybeOptions) {
|
|
39
40
|
const name = typeof nameOrOptions === "string" ? nameOrOptions : randomUUID();
|
|
40
41
|
const options = typeof nameOrOptions === "string" ? maybeOptions : nameOrOptions;
|
|
42
|
+
if (options.invoke && ((options.steps?.length ?? 0) > 0 || "run" in options)) throw new Error(`job('${name}'): 'invoke' is mutually exclusive with 'steps'/'run'`);
|
|
41
43
|
let steps = options.steps ?? [];
|
|
42
44
|
if (options.run) {
|
|
43
45
|
if (options.steps && options.steps.length > 0) throw new Error("job() cannot have both \"run\" and \"steps\" -- use one or the other");
|
|
@@ -49,7 +51,10 @@ function job(nameOrOptions, maybeOptions) {
|
|
|
49
51
|
const concurrencyGroup = options.concurrencyGroup ?? (typeof firstContext === "string" ? firstContext : void 0);
|
|
50
52
|
if (options.context !== void 0 && options.contexts !== void 0) throw new Error(`job('${name}'): context and contexts are mutually exclusive — use one`);
|
|
51
53
|
if (options.runsOn !== void 0 && options.runsOnAll !== void 0) throw new Error(`job('${name}'): runsOn and runsOnAll are mutually exclusive`);
|
|
52
|
-
if (options.
|
|
54
|
+
if (options.container !== void 0) validateContainerImageSource(name, options.container);
|
|
55
|
+
if (options.container && typeof options.container === "object" && options.container.auth) validateContainerAuth(name, options.container.auth);
|
|
56
|
+
if (options.gitCredentials) validateGitCredentials(name, options.gitCredentials);
|
|
57
|
+
if (options.invoke === void 0 && options.runsOn === void 0 && options.runsOnAll === void 0) throw new Error(`job('${name}'): one of runsOn or runsOnAll is required`);
|
|
53
58
|
if (options.onUnreachable !== void 0 && options.runsOnAll === void 0) console.warn(`[kici] job('${name}'): onUnreachable is ignored without runsOnAll`);
|
|
54
59
|
if (options.includeUninitialized !== void 0 && options.runsOnAll === void 0) console.warn(`[kici] job('${name}'): includeUninitialized is ignored without runsOnAll`);
|
|
55
60
|
return {
|
|
@@ -61,6 +66,7 @@ function job(nameOrOptions, maybeOptions) {
|
|
|
61
66
|
...options.includeUninitialized !== void 0 && { includeUninitialized: options.includeUninitialized },
|
|
62
67
|
...options.maxParallel !== void 0 && { maxParallel: options.maxParallel },
|
|
63
68
|
...options.failFast !== void 0 && { failFast: options.failFast },
|
|
69
|
+
...options.invoke !== void 0 && { invoke: options.invoke },
|
|
64
70
|
steps,
|
|
65
71
|
needs: options.needs,
|
|
66
72
|
rules: options.rules,
|
|
@@ -69,6 +75,7 @@ function job(nameOrOptions, maybeOptions) {
|
|
|
69
75
|
include: options.include,
|
|
70
76
|
exclude: options.exclude,
|
|
71
77
|
checkout: options.checkout,
|
|
78
|
+
...options.gitCredentials && { gitCredentials: options.gitCredentials },
|
|
72
79
|
container: options.container,
|
|
73
80
|
...options.sandbox !== void 0 && { sandbox: options.sandbox },
|
|
74
81
|
context: options.context,
|
|
@@ -90,6 +97,91 @@ function job(nameOrOptions, maybeOptions) {
|
|
|
90
97
|
result: createJobOutputProxy(name)
|
|
91
98
|
};
|
|
92
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Validate a job's named git credentials at definition time.
|
|
102
|
+
*
|
|
103
|
+
* Every `*Secret` field is a qualified `<context>:<secret-name>` reference into
|
|
104
|
+
* the secrets backend — the same form `workflow()` enforces for
|
|
105
|
+
* `registries[].tokenSecret`. Catching a pasted credential here matters because
|
|
106
|
+
* the alternative is committing it to a git repository and discovering it later
|
|
107
|
+
* as a confusing auth failure.
|
|
108
|
+
*/
|
|
109
|
+
/**
|
|
110
|
+
* Validate a job's container registry auth at definition time.
|
|
111
|
+
*
|
|
112
|
+
* Same contract as `gitCredentials`: every `*Secret` field is a qualified
|
|
113
|
+
* `<context>:<secret-name>` reference, and a pasted credential is refused
|
|
114
|
+
* rather than committed to a git repository. The `*Value` half is material
|
|
115
|
+
* supplied at run time and is deliberately not checked for shape.
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Is `p` a repo-relative path that stays inside the repository?
|
|
119
|
+
*
|
|
120
|
+
* Rejects an absolute path and any path whose `..` segments walk out of the
|
|
121
|
+
* tree. Deliberately string-only: the workflow is authored on one machine and
|
|
122
|
+
* the path is resolved on another, so there is no filesystem here to consult.
|
|
123
|
+
*/
|
|
124
|
+
function isInsideRepo(p) {
|
|
125
|
+
if (p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p)) return false;
|
|
126
|
+
let depth = 0;
|
|
127
|
+
for (const part of p.split(/[\\/]+/)) {
|
|
128
|
+
if (part === "" || part === ".") continue;
|
|
129
|
+
if (part === "..") {
|
|
130
|
+
depth -= 1;
|
|
131
|
+
if (depth < 0) return false;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
depth += 1;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* A container names exactly one image source: a finalized `image`, or a
|
|
140
|
+
* `dockerfile` the agent builds before the job runs.
|
|
141
|
+
*
|
|
142
|
+
* Refused at definition time rather than at dispatch, because the author is the
|
|
143
|
+
* only one who can fix it and the workflow file is where they are looking. The
|
|
144
|
+
* agent re-checks the paths against the real workdir — a lock file is repo
|
|
145
|
+
* content and is not trusted — but that failure reaches a run log, not an
|
|
146
|
+
* editor.
|
|
147
|
+
*/
|
|
148
|
+
function validateContainerImageSource(name, container) {
|
|
149
|
+
if (typeof container === "string") return;
|
|
150
|
+
const hasImage = typeof container.image === "string" && container.image.length > 0;
|
|
151
|
+
const hasDockerfile = typeof container.dockerfile === "string" && container.dockerfile.length > 0;
|
|
152
|
+
if (hasImage === hasDockerfile) throw new Error(`job('${name}'): exactly one of container.image or container.dockerfile is required`);
|
|
153
|
+
if (!hasDockerfile) {
|
|
154
|
+
for (const field of [
|
|
155
|
+
"context",
|
|
156
|
+
"target",
|
|
157
|
+
"args"
|
|
158
|
+
]) if (container[field] !== void 0) throw new Error(`job('${name}'): container.${field} applies only to container.dockerfile`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!isInsideRepo(container.dockerfile)) throw new Error(`job('${name}'): container.dockerfile must stay inside the repository (got: ${container.dockerfile})`);
|
|
162
|
+
if (container.context !== void 0 && !isInsideRepo(container.context)) throw new Error(`job('${name}'): container.context must stay inside the repository (got: ${container.context})`);
|
|
163
|
+
if (container.auth && typeof container.auth.registry !== "string") throw new Error(`job('${name}'): container.auth.registry is required with container.dockerfile — the base image is named inside the Dockerfile, so the registry cannot be derived`);
|
|
164
|
+
}
|
|
165
|
+
function validateContainerAuth(name, auth) {
|
|
166
|
+
const bag = auth;
|
|
167
|
+
for (const [field, value] of Object.entries(bag)) {
|
|
168
|
+
if (!field.endsWith("Secret") || typeof value !== "string") continue;
|
|
169
|
+
assertSecretName(value, field, "container registry credential");
|
|
170
|
+
const idx = value.indexOf(":");
|
|
171
|
+
if (idx <= 0 || idx >= value.length - 1 || value.slice(idx + 1).includes(":")) throw new Error(`job('${name}'): container.auth.${field} must use qualified <context>:<secret-name> syntax (got: ${value})`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function validateGitCredentials(name, credentials) {
|
|
175
|
+
for (const [alias, ref] of Object.entries(credentials)) {
|
|
176
|
+
const bag = ref;
|
|
177
|
+
for (const [field, value] of Object.entries(bag)) {
|
|
178
|
+
if (!field.endsWith("Secret") || typeof value !== "string") continue;
|
|
179
|
+
assertSecretName(value, `${alias}.${field}`);
|
|
180
|
+
const idx = value.indexOf(":");
|
|
181
|
+
if (idx <= 0 || idx >= value.length - 1 || value.slice(idx + 1).includes(":")) throw new Error(`job('${name}'): gitCredentials.${alias}.${field} must use qualified <context>:<secret-name> syntax (got: ${value})`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
93
185
|
//#endregion
|
|
94
186
|
export { job };
|
|
95
187
|
|
package/dist/needs-context.d.ts
CHANGED
|
@@ -14,6 +14,23 @@ export interface UpstreamSnapshot {
|
|
|
14
14
|
jobs: Record<string, Record<string, unknown>>;
|
|
15
15
|
groups: Record<string, string[]>;
|
|
16
16
|
statuses?: Record<string, ExecutionJobStatus>;
|
|
17
|
+
/**
|
|
18
|
+
* Maps an invoke-gate job name to its ordered per-run results, one entry per
|
|
19
|
+
* run the gate triggered. Present only when the upstream is an invoke gate.
|
|
20
|
+
*/
|
|
21
|
+
invokeResults?: Record<string, InvokeResult[]>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* One invoked-run result exposed on an invoke-gate upstream's
|
|
25
|
+
* `ctx.needs[gate].result` array — one entry per run the gate triggered.
|
|
26
|
+
* `outputs` carries the invoked run's non-secret declared outputs.
|
|
27
|
+
*/
|
|
28
|
+
export interface InvokeResult {
|
|
29
|
+
readonly repo: string;
|
|
30
|
+
readonly workflow: string;
|
|
31
|
+
readonly runId: string;
|
|
32
|
+
readonly status: string;
|
|
33
|
+
readonly outputs: Readonly<Record<string, unknown>>;
|
|
17
34
|
}
|
|
18
35
|
/**
|
|
19
36
|
* A single-job need entry: the upstream's outputs proxy plus its terminal
|
|
@@ -31,8 +48,20 @@ export interface GroupNeedEntry<T = Record<string, unknown>> {
|
|
|
31
48
|
result: OutputProxy<T>;
|
|
32
49
|
status: ExecutionJobStatus;
|
|
33
50
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
51
|
+
/**
|
|
52
|
+
* An invoke-gate need entry: `ctx.needs['<gate>'].result` is an ordered array of
|
|
53
|
+
* {@link InvokeResult}, one per run the gate triggered.
|
|
54
|
+
*/
|
|
55
|
+
export interface InvokeNeedEntry {
|
|
56
|
+
result: readonly InvokeResult[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A single-job need exposes `{ result, status }`; a group need exposes an
|
|
60
|
+
* ordered array of `{ name, result, status }`; an invoke-gate need exposes
|
|
61
|
+
* `{ result }` — an ordered array of {@link InvokeResult} on `.result`, one per
|
|
62
|
+
* triggered run.
|
|
63
|
+
*/
|
|
64
|
+
export type NeedEntry<T = Record<string, unknown>> = SingleNeedEntry<T> | GroupNeedEntry[] | InvokeNeedEntry;
|
|
36
65
|
/** The resolved `ctx.needs` map keyed by job name or group name. */
|
|
37
66
|
export type NeedsContext = Record<string, NeedEntry>;
|
|
38
67
|
/**
|
package/dist/needs-context.js
CHANGED
|
@@ -34,7 +34,9 @@ function buildNeedsContext(snapshot, declaredNeeds) {
|
|
|
34
34
|
const out = {};
|
|
35
35
|
for (const need of declaredNeeds) {
|
|
36
36
|
const { kind, key } = needKey(need);
|
|
37
|
-
|
|
37
|
+
const invokeResults = snapshot.invokeResults?.[key];
|
|
38
|
+
if (kind === "job" && invokeResults !== void 0) out[key] = { result: invokeResults };
|
|
39
|
+
else if (kind === "group") out[key] = (snapshot.groups[key] ?? []).map((name) => ({
|
|
38
40
|
name,
|
|
39
41
|
result: createSnapshotOutputProxy(name, snapshot.jobs[name]),
|
|
40
42
|
status: statusFor(snapshot, name)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ChangedFilesStatus } from '@kici-dev/engine';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a rule or a workflow `filter` reads `ctx.changedFiles` but the
|
|
4
|
+
* diff is not available (`changedFilesStatus !== 'fetched'`). `evaluateRules`
|
|
5
|
+
* re-throws this rather than folding it into a `passed=false` skip, so the job
|
|
6
|
+
* fails loudly instead of silently mis-evaluating a path-based gate; the global
|
|
7
|
+
* eval round reports the candidate indeterminate for the same reason.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ChangedFilesUnavailableError extends Error {
|
|
10
|
+
readonly changedFilesStatus: ChangedFilesStatus;
|
|
11
|
+
readonly eventType?: string;
|
|
12
|
+
constructor(status: ChangedFilesStatus, eventType?: string);
|
|
13
|
+
}
|
|
14
|
+
/** Read an event's `type` discriminant, when it carries a string one. */
|
|
15
|
+
export declare function eventTypeOf(event: unknown): string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Install the throwing `changedFiles` accessor shared by `RuleContext` and
|
|
18
|
+
* `FilterContext`.
|
|
19
|
+
*
|
|
20
|
+
* Both contexts contract `changedFiles` to **throw** when the diff is
|
|
21
|
+
* unavailable rather than read as an empty list: a path-based gate that
|
|
22
|
+
* silently sees no changes suppresses work invisibly, and for a `filter` it
|
|
23
|
+
* suppresses every job the workflow declares. On the organization-wide path
|
|
24
|
+
* that leaves no run row at all; on the same-repo path it leaves a `success`
|
|
25
|
+
* run carrying only the workflow's `__init__*` evaluation jobs.
|
|
26
|
+
*
|
|
27
|
+
* The behaviour lives here, in one `Object.defineProperty`, precisely because a
|
|
28
|
+
* plain `{ changedFiles: [] }` property satisfies both context types with no
|
|
29
|
+
* error anywhere: a second construction site could violate the contract and
|
|
30
|
+
* still typecheck. Every builder of either context calls this.
|
|
31
|
+
*/
|
|
32
|
+
export declare function defineChangedFilesGetter(target: object, input: {
|
|
33
|
+
files: string[];
|
|
34
|
+
status: ChangedFilesStatus;
|
|
35
|
+
eventType?: string;
|
|
36
|
+
}): void;
|
|
37
|
+
//# sourceMappingURL=changed-files.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/rules/changed-files.ts
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when a rule or a workflow `filter` reads `ctx.changedFiles` but the
|
|
5
|
+
* diff is not available (`changedFilesStatus !== 'fetched'`). `evaluateRules`
|
|
6
|
+
* re-throws this rather than folding it into a `passed=false` skip, so the job
|
|
7
|
+
* fails loudly instead of silently mis-evaluating a path-based gate; the global
|
|
8
|
+
* eval round reports the candidate indeterminate for the same reason.
|
|
9
|
+
*/
|
|
10
|
+
var ChangedFilesUnavailableError = class extends Error {
|
|
11
|
+
changedFilesStatus;
|
|
12
|
+
eventType;
|
|
13
|
+
constructor(status, eventType) {
|
|
14
|
+
super(`ctx.changedFiles is not available (status: ${status}` + (eventType ? `, event: ${eventType}` : "") + "). Changed files are only defined for push / pull_request events with a computable diff. Guard with ctx.changedFilesStatus before accessing, e.g. `if (ctx.changedFilesStatus !== 'fetched') return true`.");
|
|
15
|
+
this.name = "ChangedFilesUnavailableError";
|
|
16
|
+
this.changedFilesStatus = status;
|
|
17
|
+
this.eventType = eventType;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/** Read an event's `type` discriminant, when it carries a string one. */
|
|
21
|
+
function eventTypeOf(event) {
|
|
22
|
+
const type = event?.type;
|
|
23
|
+
return typeof type === "string" ? type : void 0;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Install the throwing `changedFiles` accessor shared by `RuleContext` and
|
|
27
|
+
* `FilterContext`.
|
|
28
|
+
*
|
|
29
|
+
* Both contexts contract `changedFiles` to **throw** when the diff is
|
|
30
|
+
* unavailable rather than read as an empty list: a path-based gate that
|
|
31
|
+
* silently sees no changes suppresses work invisibly, and for a `filter` it
|
|
32
|
+
* suppresses every job the workflow declares. On the organization-wide path
|
|
33
|
+
* that leaves no run row at all; on the same-repo path it leaves a `success`
|
|
34
|
+
* run carrying only the workflow's `__init__*` evaluation jobs.
|
|
35
|
+
*
|
|
36
|
+
* The behaviour lives here, in one `Object.defineProperty`, precisely because a
|
|
37
|
+
* plain `{ changedFiles: [] }` property satisfies both context types with no
|
|
38
|
+
* error anywhere: a second construction site could violate the contract and
|
|
39
|
+
* still typecheck. Every builder of either context calls this.
|
|
40
|
+
*/
|
|
41
|
+
function defineChangedFilesGetter(target, input) {
|
|
42
|
+
Object.defineProperty(target, "changedFiles", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
configurable: true,
|
|
45
|
+
get() {
|
|
46
|
+
if (input.status !== "fetched") throw new ChangedFilesUnavailableError(input.status, input.eventType);
|
|
47
|
+
return input.files;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { ChangedFilesUnavailableError, defineChangedFilesGetter, eventTypeOf };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=changed-files.js.map
|
package/dist/rules/context.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import type { ChangedFilesStatus } from '@kici-dev/engine';
|
|
2
2
|
import type { EventPayload } from '../events/event-payloads.js';
|
|
3
3
|
import type { FanoutPosition } from '../fanout-context.js';
|
|
4
|
+
import type { RepoInfo } from '../context.js';
|
|
4
5
|
import type { RuleContext } from './types.js';
|
|
5
|
-
|
|
6
|
-
* Thrown when a rule reads `ctx.changedFiles` but the diff is not available
|
|
7
|
-
* (`changedFilesStatus !== 'fetched'`). `evaluateRules` re-throws this rather
|
|
8
|
-
* than folding it into a `passed=false` skip, so the job fails loudly instead
|
|
9
|
-
* of silently mis-evaluating a path-based gate.
|
|
10
|
-
*/
|
|
11
|
-
export declare class ChangedFilesUnavailableError extends Error {
|
|
12
|
-
readonly changedFilesStatus: ChangedFilesStatus;
|
|
13
|
-
readonly eventType?: string;
|
|
14
|
-
constructor(status: ChangedFilesStatus, eventType?: string);
|
|
15
|
-
}
|
|
6
|
+
export { ChangedFilesUnavailableError } from './changed-files.js';
|
|
16
7
|
/** Input for {@link createRuleContext}. */
|
|
17
8
|
export interface CreateRuleContextInput {
|
|
18
9
|
event: EventPayload | Record<string, unknown>;
|
|
@@ -22,6 +13,16 @@ export interface CreateRuleContextInput {
|
|
|
22
13
|
env?: Record<string, string | undefined>;
|
|
23
14
|
dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>;
|
|
24
15
|
fanout?: FanoutPosition;
|
|
16
|
+
/**
|
|
17
|
+
* The repo whose event triggered this run. Supplied for a global workflow.
|
|
18
|
+
*
|
|
19
|
+
* `path` is an absolute path into the evaluating machine's work directory and
|
|
20
|
+
* is NOT stable across evaluations — read the tree through it, never compare
|
|
21
|
+
* it. `ref` / `sha` are optional on `RepoInfo`; do not assume either is set.
|
|
22
|
+
*/
|
|
23
|
+
sourceRepo?: RepoInfo;
|
|
24
|
+
/** The repo that registered the workflow. Identical to `sourceRepo` outside a global workflow. */
|
|
25
|
+
workflowRepo?: RepoInfo;
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
28
|
* Build a RuleContext. `changedFiles` is exposed as a getter: it returns the
|