@kici-dev/orchestrator 0.4.0 → 0.5.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/__test-helpers__/mock-db.d.ts +4 -0
- package/dist/agent/agent-version.d.ts +34 -0
- package/dist/agent/dispatcher.d.ts +70 -0
- package/dist/app.d.ts +23 -1
- package/dist/cache/global-eval-round-cache.d.ts +88 -0
- package/dist/cache/index.d.ts +3 -0
- package/dist/cache/pending-global-evals.d.ts +42 -0
- package/dist/cache/pending-inits.d.ts +10 -0
- package/dist/cli/commands/cluster-settings.d.ts +41 -3
- package/dist/cli/commands/runs.d.ts +1 -0
- package/dist/cli.js +1205 -620
- package/dist/cluster/cluster-settings-reader.d.ts +53 -1
- package/dist/config.d.ts +27 -0
- package/dist/content-requirements-cache.d.ts +55 -0
- package/dist/db/migrations/109_cluster_settings_cache_knobs.d.ts +4 -0
- package/dist/db/migrations/110_cluster_settings_global_eval_knobs.d.ts +4 -0
- package/dist/db/migrations/111_cluster_settings_global_eval_wait.d.ts +4 -0
- package/dist/db/migrations/112_execution_runs_workflow_repo.d.ts +4 -0
- package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +30 -0
- package/dist/db/migrations/114_ingest_queue_claim.d.ts +4 -0
- package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +9 -0
- package/dist/db/types.d.ts +60 -2
- package/dist/metrics/agent-metrics-aggregator.d.ts +2 -2
- package/dist/metrics/prometheus.d.ts +59 -0
- package/dist/orchestrator-core.d.ts +12 -1
- package/dist/pipeline/content-filter.d.ts +71 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +247 -8
- package/dist/pipeline/global-eval-round.d.ts +293 -0
- package/dist/pipeline/job-contexts.d.ts +16 -17
- package/dist/pipeline/process-webhook.d.ts +7 -0
- package/dist/pipeline/processor.d.ts +56 -2
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +6 -0
- package/dist/pipeline/test-pipeline.d.ts +12 -0
- package/dist/pipeline/webhook-payload-store.d.ts +20 -0
- package/dist/provenance/backfill-run.d.ts +10 -1
- package/dist/provider-registry.d.ts +38 -3
- package/dist/providers/github/check-status-poster.d.ts +22 -3
- package/dist/providers/github/commit-message.d.ts +20 -0
- package/dist/providers/github/file-contents.d.ts +40 -0
- package/dist/providers/github/index.d.ts +2 -0
- package/dist/providers/universal-git/config.d.ts +2 -0
- package/dist/providers/universal-git/normalizer.d.ts +10 -0
- package/dist/queue/cleanup.d.ts +7 -1
- package/dist/queue/job-queue.d.ts +69 -6
- package/dist/queue/terminalize-unroutable.d.ts +13 -0
- package/dist/registration/registration-run-match.d.ts +47 -0
- package/dist/reporting/check-run-reporter.d.ts +52 -1
- package/dist/reporting/execution-tracker.d.ts +117 -7
- package/dist/reporting/log-chunk-sink.d.ts +8 -5
- package/dist/routes/admin-org-settings.d.ts +5 -0
- package/dist/routes/admin.d.ts +6 -0
- package/dist/scaler/manager.d.ts +10 -8
- package/dist/security/global-workflow-policy.d.ts +52 -12
- package/dist/server.js +27553 -23779
- package/dist/standalone.js +5840 -2207
- package/dist/webhook/ingest-accept.d.ts +70 -0
- package/dist/webhook/ingest-overflow-buffer.d.ts +35 -4
- package/dist/webhook/ingest-overflow-replayer.d.ts +50 -6
- package/dist/ws/agent-handler.d.ts +3 -0
- package/dist/ws/dashboard-global-workflows-handler.d.ts +30 -9
- package/dist/ws/execution-status-frame.d.ts +32 -0
- package/dist/ws/platform-client.d.ts +14 -0
- package/dist/ws/test-relay-handlers.d.ts +35 -10
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +57 -52
- package/dist/pipeline/inline-eval.d.ts +0 -44
|
@@ -30,10 +30,17 @@ import { type ProcessingDeps } from './processor.js';
|
|
|
30
30
|
* ingress route return `{ duplicate: true }` to GitHub's Recent Deliveries
|
|
31
31
|
* panel; `skipped` covers unknown provider / unknown event / no-repo paths;
|
|
32
32
|
* `processed` means the pipeline matched and dispatched (or recorded a run).
|
|
33
|
+
*
|
|
34
|
+
* `queued` means the delivery is durably stored and its pipeline will run after
|
|
35
|
+
* the response — the direct-ingress accept path's success outcome. It maps to
|
|
36
|
+
* the same 202 `processed` always did, because from the sender's side both mean
|
|
37
|
+
* "accepted, nothing more to do"; the distinction exists so the accept path can
|
|
38
|
+
* be asserted on directly rather than through the HTTP status.
|
|
33
39
|
*/
|
|
34
40
|
export declare const WebhookIngestOutcome: z.ZodEnum<{
|
|
35
41
|
duplicate: "duplicate";
|
|
36
42
|
processed: "processed";
|
|
43
|
+
queued: "queued";
|
|
37
44
|
shed: "shed";
|
|
38
45
|
skipped: "skipped";
|
|
39
46
|
}>;
|
|
@@ -18,6 +18,7 @@ import type { WebhookInfo } from '../webhook/handler.js';
|
|
|
18
18
|
import type { DedupCache } from '../webhook/dedup.js';
|
|
19
19
|
import type { ProviderRegistry, ProviderBundle } from '../provider-registry.js';
|
|
20
20
|
import type { LockFileCache } from '../lockfile-cache.js';
|
|
21
|
+
import type { ContentRequirementsCache } from '../content-requirements-cache.js';
|
|
21
22
|
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
22
23
|
import type { PlatformClient } from '../ws/platform-client.js';
|
|
23
24
|
import type { QueuedJobInput } from '../queue/job-queue.js';
|
|
@@ -27,6 +28,8 @@ import type { DepCache } from '../cache/index.js';
|
|
|
27
28
|
import type { PendingBuildTracker } from '../cache/index.js';
|
|
28
29
|
import type { PendingInitTracker } from '../cache/pending-inits.js';
|
|
29
30
|
import type { PendingDynamicTracker } from '../cache/pending-dynamics.js';
|
|
31
|
+
import type { PendingGlobalEvalTracker } from '../cache/pending-global-evals.js';
|
|
32
|
+
import type { GlobalEvalRoundCache } from '../cache/global-eval-round-cache.js';
|
|
30
33
|
import type { CheckRunReporter } from '../reporting/check-run-reporter.js';
|
|
31
34
|
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
32
35
|
import type { AgentRegistry } from '../agent/registry.js';
|
|
@@ -35,6 +38,7 @@ import type { RunCoordinator } from '../cluster/coordinator.js';
|
|
|
35
38
|
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
36
39
|
import type { TeamMembershipLookup } from '../approvals/team-membership-lookup.js';
|
|
37
40
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
41
|
+
import type { LogWriter } from '../reporting/log-writer.js';
|
|
38
42
|
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
39
43
|
import type { ContributorCache } from '../security/contributor-cache.js';
|
|
40
44
|
import type { AccessLogWriter } from '../audit/access-log.js';
|
|
@@ -228,8 +232,12 @@ export declare function buildTriggerEvent(event: string, action: string | null |
|
|
|
228
232
|
*/
|
|
229
233
|
export declare function extractInboundRepoIdentifier(payload: unknown): string | null;
|
|
230
234
|
/**
|
|
231
|
-
* Extract the first line of the commit message from a webhook payload
|
|
232
|
-
* Handles push (head_commit.message)
|
|
235
|
+
* Extract the first line of the commit message from a webhook payload, for run
|
|
236
|
+
* display. Handles push (head_commit.message), PR (pull_request.title) and
|
|
237
|
+
* issue_comment (issue.title) events.
|
|
238
|
+
*
|
|
239
|
+
* The Tier-0 `commitMessage` trigger filter deliberately reads a DIFFERENT text
|
|
240
|
+
* (the full message, and PR title + body) — see `githubFilterText`.
|
|
233
241
|
*/
|
|
234
242
|
export declare function extractCommitMessage(event: string, payload: unknown): string | undefined;
|
|
235
243
|
/**
|
|
@@ -267,7 +275,31 @@ export declare function summarizeApprovalClauses(clauses: ReadonlyArray<{
|
|
|
267
275
|
export interface ProcessingDeps {
|
|
268
276
|
dedup: DedupCache;
|
|
269
277
|
providerRegistry: ProviderRegistry;
|
|
278
|
+
/**
|
|
279
|
+
* Re-register the provider bundle for a generic routing key from server
|
|
280
|
+
* truth, returning true when a bundle is now present.
|
|
281
|
+
*
|
|
282
|
+
* The registry is an in-memory CACHE of `generic_webhook_sources`, populated
|
|
283
|
+
* by three independent paths (startup enumeration, the admin write handler,
|
|
284
|
+
* and the LISTEN/NOTIFY drain). None of them can guarantee the entry is
|
|
285
|
+
* present for a delivery that arrives at an arbitrary moment, and a miss is
|
|
286
|
+
* not benign: `getByRoutingKey` used to substitute an unrelated `generic:`
|
|
287
|
+
* bundle whose normalizer reports "this payload has no repository", so the
|
|
288
|
+
* delivery was discarded with nothing above `debug` to say why.
|
|
289
|
+
*
|
|
290
|
+
* Optional — hand-built test deps and wirings with no generic-source manager
|
|
291
|
+
* keep the previous behaviour (a miss stays a miss, reported loudly).
|
|
292
|
+
*/
|
|
293
|
+
ensureProviderBundle?: (routingKey: string) => Promise<boolean>;
|
|
270
294
|
lockFileCache: LockFileCache;
|
|
295
|
+
/**
|
|
296
|
+
* Cache for the Tier-1 `requires` static content filter (source-file bytes at
|
|
297
|
+
* a ref, keyed by (repo, sha, path)). Optional so hand-built test deps and
|
|
298
|
+
* independent deployments that never use `requires` keep working; when absent,
|
|
299
|
+
* a candidate carrying `requires` is dropped fail-visible rather than
|
|
300
|
+
* dispatched unfiltered (see content-filter.ts).
|
|
301
|
+
*/
|
|
302
|
+
contentRequirementsCache?: ContentRequirementsCache;
|
|
271
303
|
dispatcher: Dispatcher;
|
|
272
304
|
/** Null/undefined in Independent mode. send() buffers when disconnected. */
|
|
273
305
|
platformClient?: PlatformClient;
|
|
@@ -285,6 +317,21 @@ export interface ProcessingDeps {
|
|
|
285
317
|
pendingInits?: PendingInitTracker;
|
|
286
318
|
/** Pending dynamic tracker -- waits for agents to evaluate DynamicJobFn and return generated LockJob[]. */
|
|
287
319
|
pendingDynamics?: PendingDynamicTracker;
|
|
320
|
+
/** Pending global-eval tracker -- waits for the pre-run round that decides which global workflows apply. */
|
|
321
|
+
pendingGlobalEvals?: PendingGlobalEvalTracker;
|
|
322
|
+
/** Round-result cache for the pre-run global eval round. Optional -- if not set, every round re-runs. */
|
|
323
|
+
globalEvalCache?: GlobalEvalRoundCache;
|
|
324
|
+
/** Cluster default for the whole-round budget handed to the eval agent (ms).
|
|
325
|
+
* The live per-cluster override is `cluster_settings.global_eval_round_timeout_ms`. */
|
|
326
|
+
globalEvalRoundTimeoutMs?: number;
|
|
327
|
+
/** Cluster default for the per-candidate budget handed to the eval agent (ms).
|
|
328
|
+
* The live per-cluster override is `cluster_settings.global_eval_candidate_timeout_ms`. */
|
|
329
|
+
globalEvalCandidateTimeoutMs?: number;
|
|
330
|
+
/** Cluster default for the orchestrator's own ceiling on awaiting a round (ms).
|
|
331
|
+
* Unlike the two budgets above, this one is enforced here rather than by the
|
|
332
|
+
* agent, so it also bounds a round no agent ever picked up. The live
|
|
333
|
+
* per-cluster override is `cluster_settings.global_eval_wait_timeout_ms`. */
|
|
334
|
+
globalEvalWaitTimeoutMs?: number;
|
|
288
335
|
/** Commit status reporter for setting pending/success/failure/error on commits. Optional. */
|
|
289
336
|
checkRunReporter?: CheckRunReporter;
|
|
290
337
|
/** Execution tracker for DB persistence. Optional -- if not set, execution tracking is skipped. */
|
|
@@ -321,6 +368,13 @@ export interface ProcessingDeps {
|
|
|
321
368
|
secretKey?: string;
|
|
322
369
|
/** Log storage backend for persisting webhook payloads. Optional -- if not set, payload storage is skipped. */
|
|
323
370
|
logStorage?: LogStorage;
|
|
371
|
+
/**
|
|
372
|
+
* Durable step-log writer. Optional -- when present, the deferred-init path
|
|
373
|
+
* uses it to surface a post-init env warning on the run's log stream so a
|
|
374
|
+
* blocking `kici run remote` test run prints it (the accept response has
|
|
375
|
+
* already been returned by the time the init round resolves).
|
|
376
|
+
*/
|
|
377
|
+
logWriter?: Pick<LogWriter, 'appendChunk' | 'drain'>;
|
|
324
378
|
/** Context store for looking up deployment contexts. Optional -- if not set, context features are inactive. */
|
|
325
379
|
contextStore?: ContextStore;
|
|
326
380
|
/** Variable store for resolving context variables. Optional -- if not set, context vars are not merged. */
|
|
@@ -19,6 +19,12 @@ export interface DispatchedJobEntry {
|
|
|
19
19
|
jobName: string;
|
|
20
20
|
matrixValues?: Record<string, unknown>;
|
|
21
21
|
runsOnLabels?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* The unexpanded job name a materialized child came from. Persisted to
|
|
24
|
+
* `execution_jobs.base_job_name`, which is the key the rolling-wave scheduler
|
|
25
|
+
* groups a wave's children by — a NULL there makes the wave gate bail.
|
|
26
|
+
*/
|
|
27
|
+
baseJobName?: string;
|
|
22
28
|
}
|
|
23
29
|
export interface RejectedJobEntry {
|
|
24
30
|
jobId: string;
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* - direct workflow execution (bypass trigger matching) is supported.
|
|
23
23
|
*/
|
|
24
24
|
import { type ProcessingDeps } from './processor.js';
|
|
25
|
+
import { type ActorPrincipal } from '@kici-dev/engine';
|
|
25
26
|
import type { CheckMode, HostTargetSelector } from '@kici-dev/engine';
|
|
26
27
|
/**
|
|
27
28
|
* Input for a test trigger request.
|
|
@@ -58,6 +59,17 @@ export interface TestTriggerInput {
|
|
|
58
59
|
};
|
|
59
60
|
/** Request trace ID from the HTTP request. */
|
|
60
61
|
requestId: string;
|
|
62
|
+
/**
|
|
63
|
+
* The principal that initiated this run, relayed by the Platform on
|
|
64
|
+
* `test.relay.trigger` (where the wire schema has always required it).
|
|
65
|
+
*
|
|
66
|
+
* Required rather than optional: the relay handler is the only production
|
|
67
|
+
* caller, so there is no path that legitimately lacks an actor, and an
|
|
68
|
+
* optional field would let a future caller silently drop attribution — which
|
|
69
|
+
* is exactly how `execution_runs.triggered_by` stayed NULL for every remote
|
|
70
|
+
* test run while the column claimed to hold the initiator.
|
|
71
|
+
*/
|
|
72
|
+
actor: ActorPrincipal;
|
|
61
73
|
/** JSON-stringified lock file content for local repos with no remote. */
|
|
62
74
|
inlineLockFile?: string;
|
|
63
75
|
/** When true, repo has no remote -- skip provider lookup, skip clone. */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LogStorage } from '../reporting/log-storage.js';
|
|
2
|
+
/**
|
|
3
|
+
* Object-storage key holding the webhook payload for a run. Shared by the
|
|
4
|
+
* writers and by the re-run path that copies a payload forward, so the layout
|
|
5
|
+
* is stated once.
|
|
6
|
+
*/
|
|
7
|
+
export declare function webhookPayloadPath(runId: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Store a run's triggering webhook payload, best-effort.
|
|
10
|
+
*
|
|
11
|
+
* A failure is logged and swallowed: the payload is a debugging aid, and losing
|
|
12
|
+
* it must never cost the run that was about to execute. A no-op when the
|
|
13
|
+
* orchestrator has no object storage configured.
|
|
14
|
+
*/
|
|
15
|
+
export declare function storeWebhookPayload(args: {
|
|
16
|
+
logStorage: LogStorage | undefined;
|
|
17
|
+
runId: string;
|
|
18
|
+
payload: unknown;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=webhook-payload-store.d.ts.map
|
|
@@ -6,6 +6,12 @@ export interface BackfillRunRow {
|
|
|
6
6
|
status: string;
|
|
7
7
|
routing_key: string | null;
|
|
8
8
|
repo_identifier: string | null;
|
|
9
|
+
/**
|
|
10
|
+
* The repository that DEFINES the workflow, recorded only when it differs
|
|
11
|
+
* from `repo_identifier`. A cross-repository global run backfilled without it
|
|
12
|
+
* lands in the Platform mirror as an ordinary per-repository run.
|
|
13
|
+
*/
|
|
14
|
+
workflow_repo_identifier: string | null;
|
|
9
15
|
provider: string | null;
|
|
10
16
|
local_working_tree: boolean | null;
|
|
11
17
|
sha: string | null;
|
|
@@ -33,7 +39,10 @@ export interface BackfillRunDeps {
|
|
|
33
39
|
}
|
|
34
40
|
/**
|
|
35
41
|
* Send one `execution.status` (terminal) followed by one `job.status.forward`
|
|
36
|
-
* per job, populating
|
|
42
|
+
* per job, populating the run and job fields the Platform's `handler.ts` upserts
|
|
43
|
+
* that the local rows can answer for (the trigger metadata a live run carries in
|
|
44
|
+
* memory — trigger event, commit message, re-run lineage — has no column here
|
|
45
|
+
* and is left to the live path).
|
|
37
46
|
* Ordered execution.status FIRST so the run row exists before the job rows
|
|
38
47
|
* reference it. A no-op (throws) when the run is not found locally — the caller
|
|
39
48
|
* leaves the pending row `deferred` with a clear error.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* webhook normalization, lock file fetching, changed files retrieval,
|
|
13
13
|
* clone token creation, and URL building.
|
|
14
14
|
*/
|
|
15
|
-
import type { WebhookNormalizer, LockFileFetcher, ChangedFilesFetcher, CloneTokenProvider, RepoUrlBuilder, ContributorResolver, CheckStatusPoster, ProviderType } from '@kici-dev/engine';
|
|
15
|
+
import type { WebhookNormalizer, LockFileFetcher, FileContentsFetcher, ChangedFilesFetcher, CloneTokenProvider, RepoUrlBuilder, ContributorResolver, CheckStatusPoster, ProviderType } from '@kici-dev/engine';
|
|
16
16
|
/**
|
|
17
17
|
* Complete set of provider capabilities.
|
|
18
18
|
*
|
|
@@ -24,6 +24,23 @@ import type { WebhookNormalizer, LockFileFetcher, ChangedFilesFetcher, CloneToke
|
|
|
24
24
|
export interface ProviderBundle {
|
|
25
25
|
normalizer: WebhookNormalizer;
|
|
26
26
|
lockFileFetcher?: LockFileFetcher;
|
|
27
|
+
/**
|
|
28
|
+
* Fetches arbitrary file contents at a ref (content-match triggers). Unlike
|
|
29
|
+
* the other fetchers, a GitHub instance is scoped to one installation, so the
|
|
30
|
+
* per-delivery installation id must be known to construct it -- it is wired
|
|
31
|
+
* where that id is in scope, not in the source-level bundle build. Providers
|
|
32
|
+
* that can prebuild one (no per-delivery credential) may set this directly;
|
|
33
|
+
* providers scoped per installation supply {@link fileContentsFetcherFactory}
|
|
34
|
+
* instead.
|
|
35
|
+
*/
|
|
36
|
+
fileContentsFetcher?: FileContentsFetcher;
|
|
37
|
+
/**
|
|
38
|
+
* Builds a per-delivery {@link FileContentsFetcher} from the event
|
|
39
|
+
* credentials (e.g. a GitHub installation id). Returns undefined when the
|
|
40
|
+
* credentials do not carry what the provider needs. The webhook pipeline
|
|
41
|
+
* calls this once per delivery, where the credentials are already resolved.
|
|
42
|
+
*/
|
|
43
|
+
fileContentsFetcherFactory?: (credentials: Record<string, unknown>) => FileContentsFetcher | undefined;
|
|
27
44
|
changedFilesFetcher?: ChangedFilesFetcher;
|
|
28
45
|
cloneTokenProvider?: CloneTokenProvider;
|
|
29
46
|
repoUrlBuilder?: RepoUrlBuilder;
|
|
@@ -79,12 +96,30 @@ export declare class ProviderRegistry {
|
|
|
79
96
|
* prefix. For multi-app, use getByRoutingKey() instead.
|
|
80
97
|
*/
|
|
81
98
|
get(type: ProviderType): ProviderBundle | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Whether a bundle is registered at EXACTLY this routing key.
|
|
101
|
+
*
|
|
102
|
+
* `getByRoutingKey` cannot answer this: it falls back to a provider-type
|
|
103
|
+
* lookup, so it returns a bundle for a key it has never seen. A caller that
|
|
104
|
+
* needs to know whether the source's OWN bundle is present — rather than
|
|
105
|
+
* whether some bundle can be produced — has to ask here.
|
|
106
|
+
*/
|
|
107
|
+
hasExact(routingKey: string): boolean;
|
|
82
108
|
/**
|
|
83
109
|
* Get the provider bundle by routing key.
|
|
84
110
|
* Routing keys have the format "{provider}:{id}" (e.g., "github:12345").
|
|
85
111
|
*
|
|
86
|
-
* Falls back to get(providerType) if exact key is not found,
|
|
87
|
-
*
|
|
112
|
+
* Falls back to get(providerType) if the exact key is not found, for
|
|
113
|
+
* backward compatibility with single-app registration.
|
|
114
|
+
*
|
|
115
|
+
* The fallback is deliberately narrower for a `generic:` key. Such a key is
|
|
116
|
+
* fully qualified (`generic:{orgId}:{sourceId}`), so the type-prefix scan in
|
|
117
|
+
* `get()` cannot be a "the single configured app" shortcut the way it is for
|
|
118
|
+
* `github:` — it returns whichever `generic:`-prefixed bundle happens to sit
|
|
119
|
+
* first in insertion order, which may belong to a different source, or to a
|
|
120
|
+
* different ORGANIZATION. Only the shared default bundle (`generic:default`,
|
|
121
|
+
* the one that genuinely stands in for every plain generic source) is an
|
|
122
|
+
* acceptable stand-in, so that is the only fallback offered here.
|
|
88
123
|
*/
|
|
89
124
|
getByRoutingKey(routingKey: string): ProviderBundle | undefined;
|
|
90
125
|
/**
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* - Security holds (pending): "KiCI Security" — "Held for approval"
|
|
6
6
|
* - Workflow modifications (neutral): "KiCI: Workflow changes"
|
|
7
7
|
* - Org globals skipped by the trust policy (neutral): "KiCI: Organization workflows"
|
|
8
|
+
* - Org global evaluation failed (failure): "KiCI: Organization workflow evaluation"
|
|
8
9
|
* - Approved runs (success) / Rejected/expired runs (failure)
|
|
9
10
|
*
|
|
10
|
-
* `postWorkflowModificationCheck
|
|
11
|
-
* interface-backed (the `CheckStatusPoster`
|
|
12
|
-
* post on their own check names so
|
|
11
|
+
* `postWorkflowModificationCheck`, `postGlobalWorkflowsSkippedCheck`, and
|
|
12
|
+
* `postGlobalEvalFailedCheck` are interface-backed (the `CheckStatusPoster`
|
|
13
|
+
* contract in `@kici-dev/engine`) and post on their own check names so none of
|
|
14
|
+
* them overwrites the security-hold check.
|
|
13
15
|
*
|
|
14
16
|
* Reuses the Octokit infrastructure from auth.ts via createInstallationOctokit.
|
|
15
17
|
* Uses a fixed check name per category so that subsequent updates (approve/reject)
|
|
@@ -47,5 +49,22 @@ export declare class GitHubCheckStatusPoster implements CheckStatusPoster {
|
|
|
47
49
|
* while the run is still held. Always posted as neutral/completed.
|
|
48
50
|
*/
|
|
49
51
|
postGlobalWorkflowsSkippedCheck(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Post the check recording that the pre-run evaluation of the organization's
|
|
54
|
+
* global workflows failed, so none of the workflows it was deciding on ran.
|
|
55
|
+
*
|
|
56
|
+
* Its own check name ("KiCI: Organization workflow evaluation") for the same
|
|
57
|
+
* reason as the notice above: the security-hold check is a single named run
|
|
58
|
+
* per commit which a hold posts as pending and approve / reject later
|
|
59
|
+
* complete, so writing this through `postCheckStatus` would resolve a
|
|
60
|
+
* still-held run's check and unblock a branch protection rule that requires
|
|
61
|
+
* it.
|
|
62
|
+
*
|
|
63
|
+
* Posted as a failure, not a neutral: work the organization asked for did not
|
|
64
|
+
* run, and the reader has to be able to tell that from a clean commit. The
|
|
65
|
+
* name is new, so no existing branch protection rule requires it — a rule
|
|
66
|
+
* only requires check names it was configured with.
|
|
67
|
+
*/
|
|
68
|
+
postGlobalEvalFailedCheck(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
50
69
|
}
|
|
51
70
|
//# sourceMappingURL=check-status-poster.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the commit message lives in a GitHub webhook payload, and the two
|
|
3
|
+
* different readings of it.
|
|
4
|
+
*
|
|
5
|
+
* They differ deliberately. `githubFilterText` feeds the Tier-0 `commitMessage`
|
|
6
|
+
* trigger filter, so it must be the WHOLE message — truncating to the subject
|
|
7
|
+
* line would make a `[skip ci]` marker written in the body invisible to a filter
|
|
8
|
+
* that names it. `githubDisplayMessage` feeds run-display metadata, where a
|
|
9
|
+
* one-line summary is the point.
|
|
10
|
+
*/
|
|
11
|
+
/** Full text a `commitMessage` trigger filter is tested against; undefined when the payload carries none. */
|
|
12
|
+
export declare function githubFilterText(event: string, payload: unknown): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* One-line message for run display: the subject (first line) of a push's
|
|
15
|
+
* head-commit message, the PR title, or the issue title. This is the reading
|
|
16
|
+
* run-display metadata uses, distinct from the full text `githubFilterText`
|
|
17
|
+
* feeds the Tier-0 filter.
|
|
18
|
+
*/
|
|
19
|
+
export declare function githubDisplayMessage(event: string, payload: unknown): string | undefined;
|
|
20
|
+
//# sourceMappingURL=commit-message.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub file contents fetcher.
|
|
3
|
+
*
|
|
4
|
+
* Implements the FileContentsFetcher interface from @kici-dev/engine for GitHub.
|
|
5
|
+
* Fetches an arbitrary file's contents from a GitHub repository at a specific
|
|
6
|
+
* ref via the Contents API, so the orchestrator can evaluate content-match
|
|
7
|
+
* triggers without cloning the repo.
|
|
8
|
+
*/
|
|
9
|
+
import type { FileContentsFetcher } from '@kici-dev/engine';
|
|
10
|
+
import { type GitHubAppConfig } from './auth.js';
|
|
11
|
+
/**
|
|
12
|
+
* GitHub-specific implementation of FileContentsFetcher.
|
|
13
|
+
*
|
|
14
|
+
* The installation id is baked in at construction because the
|
|
15
|
+
* FileContentsFetcher interface is credential-less at call time -- a fetcher
|
|
16
|
+
* instance is scoped to one GitHub App installation.
|
|
17
|
+
*/
|
|
18
|
+
export declare class GitHubFileContentsFetcher implements FileContentsFetcher {
|
|
19
|
+
private readonly config;
|
|
20
|
+
private readonly installationId;
|
|
21
|
+
readonly provider: 'github';
|
|
22
|
+
constructor(config: GitHubAppConfig, installationId: number);
|
|
23
|
+
/**
|
|
24
|
+
* Fetch a file's contents from a GitHub repository at a specific ref.
|
|
25
|
+
*
|
|
26
|
+
* @param owner - Repository owner (e.g. "my-org")
|
|
27
|
+
* @param repo - Repository name (e.g. "my-app")
|
|
28
|
+
* @param path - Repo-relative file path (e.g. ".kici/workflows/ci.ts")
|
|
29
|
+
* @param ref - Git ref (branch, tag, or SHA)
|
|
30
|
+
* @returns `{ present: false }` on 404 / directory; `{ present: true, bytes }`
|
|
31
|
+
* when inline content is returned; `{ present: true }` (no bytes)
|
|
32
|
+
* when the file exists but GitHub omits inline content (files over
|
|
33
|
+
* 1 MiB).
|
|
34
|
+
*/
|
|
35
|
+
getFileContents(owner: string, repo: string, path: string, ref: string): Promise<{
|
|
36
|
+
present: boolean;
|
|
37
|
+
bytes?: string;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=file-contents.d.ts.map
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Each class implements a corresponding interface from @kici-dev/engine:
|
|
5
5
|
* - GitHubWebhookNormalizer -> WebhookNormalizer
|
|
6
6
|
* - GitHubLockFileFetcher -> LockFileFetcher
|
|
7
|
+
* - GitHubFileContentsFetcher -> FileContentsFetcher
|
|
7
8
|
* - GitHubChangedFilesFetcher -> ChangedFilesFetcher
|
|
8
9
|
* - GitHubCloneTokenProvider -> CloneTokenProvider
|
|
9
10
|
* - GitHubRepoUrlBuilder -> RepoUrlBuilder
|
|
@@ -11,6 +12,7 @@
|
|
|
11
12
|
*/
|
|
12
13
|
export { GitHubWebhookNormalizer } from './normalizer.js';
|
|
13
14
|
export { GitHubLockFileFetcher } from './lock-file.js';
|
|
15
|
+
export { GitHubFileContentsFetcher } from './file-contents.js';
|
|
14
16
|
export { GitHubChangedFilesFetcher } from './changed-files.js';
|
|
15
17
|
export { GitHubCloneTokenProvider, createInstallationOctokit } from './auth.js';
|
|
16
18
|
export type { GitHubAppConfig, GitHubCredentials } from './auth.js';
|
|
@@ -72,6 +72,7 @@ export declare const UniversalGitPayloadPathsSchema: z.ZodObject<{
|
|
|
72
72
|
commitsAdded: z.ZodString;
|
|
73
73
|
commitsModified: z.ZodString;
|
|
74
74
|
commitsRemoved: z.ZodString;
|
|
75
|
+
commitMessage: z.ZodOptional<z.ZodString>;
|
|
75
76
|
}, z.core.$strip>;
|
|
76
77
|
export type UniversalGitPayloadPaths = z.infer<typeof UniversalGitPayloadPathsSchema>;
|
|
77
78
|
/**
|
|
@@ -129,6 +130,7 @@ export declare const UniversalGitConfigSchema: z.ZodObject<{
|
|
|
129
130
|
commitsAdded: z.ZodString;
|
|
130
131
|
commitsModified: z.ZodString;
|
|
131
132
|
commitsRemoved: z.ZodString;
|
|
133
|
+
commitMessage: z.ZodOptional<z.ZodString>;
|
|
132
134
|
}, z.core.$strip>>;
|
|
133
135
|
eventMapping: z.ZodOptional<z.ZodObject<{
|
|
134
136
|
push: z.ZodArray<z.ZodString>;
|
|
@@ -84,6 +84,16 @@ export declare class UniversalGitWebhookNormalizer implements WebhookNormalizer
|
|
|
84
84
|
* is safe to ship ahead of the interface change in Phase 3.
|
|
85
85
|
*/
|
|
86
86
|
extractDefaultBranch(payload: unknown): string | null;
|
|
87
|
+
/**
|
|
88
|
+
* Text a `commitMessage` trigger filter is tested against.
|
|
89
|
+
*
|
|
90
|
+
* Push/tag reads the configured JSONPath, because forges genuinely differ on
|
|
91
|
+
* where the head commit lives. The PR read is STRUCTURAL rather than
|
|
92
|
+
* configured, matching how this normalizer already resolves base/head refs —
|
|
93
|
+
* `pull_request` on Gitea-family forges, `object_attributes` on GitLab, whose
|
|
94
|
+
* body field is spelled `description`.
|
|
95
|
+
*/
|
|
96
|
+
private extractCommitMessage;
|
|
87
97
|
/** Classify the raw event header against the source's eventMapping. */
|
|
88
98
|
private classifyEvent;
|
|
89
99
|
}
|
package/dist/queue/cleanup.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { CheckRunTrackingStore } from '../reporting/check-run-tracking-stor
|
|
|
6
6
|
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
7
7
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
8
8
|
import { JobQueue } from './job-queue.js';
|
|
9
|
-
import { type CanRouteLabels } from './terminalize-unroutable.js';
|
|
9
|
+
import { type CanRouteLabels, type TerminalizeDeps } from './terminalize-unroutable.js';
|
|
10
10
|
/**
|
|
11
11
|
* Optional cleanup dependencies + retention knobs. Present in platform/hybrid
|
|
12
12
|
* mode (the only wiring site, `orchestrator-core`, always supplies them); the
|
|
@@ -67,6 +67,12 @@ export interface CleanupExtras {
|
|
|
67
67
|
* safety: the job is terminal either way and the run fails either way.
|
|
68
68
|
*/
|
|
69
69
|
canRouteLabels?: CanRouteLabels;
|
|
70
|
+
/**
|
|
71
|
+
* The Tier-2 global-eval tracker, forwarded to
|
|
72
|
+
* {@link terminalizeUnroutableJob} so a round job this sweep settles also
|
|
73
|
+
* settles the webhook request awaiting its verdict.
|
|
74
|
+
*/
|
|
75
|
+
pendingGlobalEvals?: TerminalizeDeps['pendingGlobalEvals'];
|
|
70
76
|
}
|
|
71
77
|
/**
|
|
72
78
|
* Run a single cleanup pass: remove expired dedup_cache entries and
|
|
@@ -239,18 +239,59 @@ export declare class JobQueue {
|
|
|
239
239
|
* @returns The matching job, or null if none found.
|
|
240
240
|
*/
|
|
241
241
|
dequeueForLabels(agentLabels: string[], agentMandatoryLabels?: string[], agentId?: string): Promise<QueuedJob | null>;
|
|
242
|
+
/**
|
|
243
|
+
* The column writes that constitute a claim. Identical to what
|
|
244
|
+
* {@link markDispatched} sets, so a claim and the caller's follow-up
|
|
245
|
+
* markDispatched are the same transition applied twice rather than two
|
|
246
|
+
* different half-transitions — and a row is never observable as Dispatched
|
|
247
|
+
* with no owner.
|
|
248
|
+
*
|
|
249
|
+
* `agentId` is optional only because `dequeueForLabels` accepts it optionally;
|
|
250
|
+
* every production caller supplies it.
|
|
251
|
+
*/
|
|
252
|
+
private claimTransition;
|
|
253
|
+
/**
|
|
254
|
+
* Conditionally claim one row by id: flip Pending -> Dispatched, returning
|
|
255
|
+
* whether this caller won. The `status = Pending` guard is the arbiter — a
|
|
256
|
+
* loser updates zero rows and must treat that as "someone else took it",
|
|
257
|
+
* never as an error.
|
|
258
|
+
*
|
|
259
|
+
* Used by every claim path that has to run a JS post-filter before claiming
|
|
260
|
+
* (the regex matchers), since that filter runs after the SELECT's
|
|
261
|
+
* per-statement lock window has already closed.
|
|
262
|
+
*/
|
|
263
|
+
private claimRowById;
|
|
242
264
|
/**
|
|
243
265
|
* Build the shared drain WHERE chain (status / expiry / exact-label @> /
|
|
244
266
|
* exclude-label / pin / mandatory-label gate) common to both drain passes.
|
|
245
267
|
* The pattern columns are NOT filtered here — each pass adds its own
|
|
246
268
|
* pattern-free / pattern-bearing guard on top.
|
|
269
|
+
*
|
|
270
|
+
* No projection is attached: the pattern-free pass selects `id` alone (it
|
|
271
|
+
* embeds this as the sub-select of its claiming UPDATE), while the pattern
|
|
272
|
+
* pass selects every column so it can run the JS matcher post-filter.
|
|
247
273
|
*/
|
|
248
274
|
private drainBaseQuery;
|
|
249
275
|
/**
|
|
250
276
|
* Fast path: claim the oldest pending pattern-free row. The
|
|
251
277
|
* `runs_on_patterns = '[]' AND exclude_patterns = '[]'` guard restricts this
|
|
252
|
-
* pass to rows that need no JS post-filter,
|
|
253
|
-
*
|
|
278
|
+
* pass to rows that need no JS post-filter, which is what lets the whole
|
|
279
|
+
* claim be ONE statement.
|
|
280
|
+
*
|
|
281
|
+
* Selecting a row and transitioning it in two statements is not a claim.
|
|
282
|
+
* Outside an explicit transaction the `FOR UPDATE` lock lives only for the
|
|
283
|
+
* duration of its own SELECT, so a second agent arriving between the SELECT
|
|
284
|
+
* and the UPDATE reads the row still Pending, skips nothing, and dispatches
|
|
285
|
+
* the same job — which is one job executing twice on two agents, side effects
|
|
286
|
+
* and all.
|
|
287
|
+
*
|
|
288
|
+
* So the sub-select is embedded in the claiming UPDATE: its `FOR UPDATE SKIP
|
|
289
|
+
* LOCKED` row lock is now taken inside the UPDATE's own transaction and held
|
|
290
|
+
* until commit. That buys both halves at once — exactly one claimant can win
|
|
291
|
+
* a given row, and a concurrent claimant SKIPs the locked row and takes the
|
|
292
|
+
* *next* one instead of coming back empty-handed, which a select-then-claim
|
|
293
|
+
* retry loop would not preserve. The redundant outer `status = Pending` is
|
|
294
|
+
* belt-and-braces on the arbiter.
|
|
254
295
|
*/
|
|
255
296
|
private claimPatternFree;
|
|
256
297
|
/**
|
|
@@ -263,6 +304,9 @@ export declare class JobQueue {
|
|
|
263
304
|
* Pending` makes exactly one of them win. The claim transitions the row to
|
|
264
305
|
* Dispatched, matching the value the caller-side markDispatched would set
|
|
265
306
|
* (which then re-sets it idempotently).
|
|
307
|
+
*
|
|
308
|
+
* Losing the claim continues to the next candidate rather than returning
|
|
309
|
+
* null, so a lost race costs this agent a candidate and not a whole drain.
|
|
266
310
|
*/
|
|
267
311
|
private claimWithPatterns;
|
|
268
312
|
/**
|
|
@@ -276,6 +320,13 @@ export declare class JobQueue {
|
|
|
276
320
|
* `runsOn`/`exclude` patterns no longer match the agent's current labels must
|
|
277
321
|
* not be claimed. The single matching authority is the engine's
|
|
278
322
|
* `matcherSatisfiedBy` (never a Postgres `~`).
|
|
323
|
+
*
|
|
324
|
+
* The claim is the conditional UPDATE, not the SELECT: the JS post-filter has
|
|
325
|
+
* to run first (claiming and then releasing a pattern-rejected row would
|
|
326
|
+
* strand it as Dispatched), which puts the filter outside the SELECT's
|
|
327
|
+
* per-statement lock window. Losing that claim returns null, and
|
|
328
|
+
* `onAgentAvailable` then falls through to the generic label drain — which
|
|
329
|
+
* also matches jobs pinned to this agent — so a lost race is not a stall.
|
|
279
330
|
*/
|
|
280
331
|
dequeueByPinnedAgent(agentId: string, agentLabels?: string[]): Promise<QueuedJob | null>;
|
|
281
332
|
/**
|
|
@@ -292,10 +343,22 @@ export declare class JobQueue {
|
|
|
292
343
|
* spawned the agent and was reassigned to a different queued job).
|
|
293
344
|
*
|
|
294
345
|
* Returns null if the job is gone, no longer pending, expired, its label
|
|
295
|
-
* requirements are no longer satisfied by the agent,
|
|
296
|
-
*
|
|
297
|
-
|
|
298
|
-
|
|
346
|
+
* requirements are no longer satisfied by the agent, the agent's gate is not
|
|
347
|
+
* satisfied by the job's `runsOn`, or another claimant won the row first.
|
|
348
|
+
*
|
|
349
|
+
* That last case is the one this shares with every other claim path: the
|
|
350
|
+
* eager bound claim and the generic drain can target the same row moments
|
|
351
|
+
* apart, and a SELECT that returns the row still Pending lets both dispatch
|
|
352
|
+
* it. The conditional UPDATE below is the arbiter, and it runs after the JS
|
|
353
|
+
* post-filter so a pattern-rejected row is never claimed and stranded. A
|
|
354
|
+
* loser returns null, and `dispatchBoundJob` then falls back to the generic
|
|
355
|
+
* `onAgentAvailable` drain exactly as it does for an already-gone job.
|
|
356
|
+
*
|
|
357
|
+
* @param claimingAgentId Recorded as the row's durable owner as part of the
|
|
358
|
+
* claim. Optional so existing 3-arg callers keep working; the caller's
|
|
359
|
+
* markDispatched sets the same column immediately afterwards either way.
|
|
360
|
+
*/
|
|
361
|
+
dequeueById(jobId: string, agentLabels: string[], agentMandatoryLabels?: string[], claimingAgentId?: string): Promise<QueuedJob | null>;
|
|
299
362
|
/**
|
|
300
363
|
* Insert a job directly with status='dispatched' (bypasses the queue).
|
|
301
364
|
* Used when an agent is immediately available and the job doesn't need to wait.
|
|
@@ -3,6 +3,7 @@ import { ExecutionJobStatus, type LabelMatcher } from '@kici-dev/engine';
|
|
|
3
3
|
import type { Database } from '../db/types.js';
|
|
4
4
|
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
5
5
|
import type { CheckRunReporter } from '../reporting/check-run-reporter.js';
|
|
6
|
+
import type { PendingGlobalEvalTracker } from '../cache/pending-global-evals.js';
|
|
6
7
|
import type { ExpiredJobInfo } from './job-queue.js';
|
|
7
8
|
/**
|
|
8
9
|
* Whether ANYTHING could ever run a job with these selectors — a registered
|
|
@@ -23,6 +24,18 @@ export interface TerminalizeDeps {
|
|
|
23
24
|
executionTracker: ExecutionTracker;
|
|
24
25
|
checkRunReporter?: Pick<CheckRunReporter, 'updateJobStatus'>;
|
|
25
26
|
canRouteLabels?: CanRouteLabels;
|
|
27
|
+
/**
|
|
28
|
+
* The Tier-2 global-eval tracker, so a round job settled here also settles
|
|
29
|
+
* the webhook request awaiting its verdict.
|
|
30
|
+
*
|
|
31
|
+
* A round job is the one queue entry with an in-process awaiter and no
|
|
32
|
+
* `execution_runs` row — the round decides whether any run exists at all —
|
|
33
|
+
* so the rest of this function skips it entirely and the awaiter would
|
|
34
|
+
* otherwise wait out its full ceiling for a job the queue has already
|
|
35
|
+
* declared dead. With the shipped defaults that is a 120s definitive
|
|
36
|
+
* fast-fail followed by a 240s wait, twice.
|
|
37
|
+
*/
|
|
38
|
+
pendingGlobalEvals?: Pick<PendingGlobalEvalTracker, 'reject'>;
|
|
26
39
|
}
|
|
27
40
|
export declare function unroutableMessage(job: JobRoutingFacts): string;
|
|
28
41
|
/**
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ExpressionBuilder, ExpressionWrapper, SqlBool } from 'kysely';
|
|
2
|
+
import type { Database } from '../db/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Match `execution_runs` rows to the registrations that define their workflow.
|
|
5
|
+
*
|
|
6
|
+
* A registration always names the repository the workflow is DEFINED in. A run
|
|
7
|
+
* names two repositories: `repo_identifier` is the one it acted on and whose
|
|
8
|
+
* code its jobs checked out, and `workflow_repo_identifier` is the one that
|
|
9
|
+
* defines the workflow — recorded only when the two differ, which is exactly an
|
|
10
|
+
* organization-wide workflow dispatched against another repository.
|
|
11
|
+
*
|
|
12
|
+
* So the repository a registration must be matched on is
|
|
13
|
+
* `workflow_repo_identifier ?? repo_identifier`. Matching on `repo_identifier`
|
|
14
|
+
* alone is wrong in both directions at once: a global registration matches none
|
|
15
|
+
* of its own runs, and a same-named registration in the acted-on repository
|
|
16
|
+
* matches all of them.
|
|
17
|
+
*
|
|
18
|
+
* Both consumers — the "last triggered" enrichment and the delete path's
|
|
19
|
+
* in-flight cancellation — share this one predicate deliberately. Fixing one
|
|
20
|
+
* and not the other would leave the dashboard showing a global workflow as
|
|
21
|
+
* triggered while deleting it still cancelled nothing.
|
|
22
|
+
*
|
|
23
|
+
* A NULL marker is evidence of a per-repository run only because every
|
|
24
|
+
* recording site states which repository defines the workflow it is recording,
|
|
25
|
+
* and the sites narrow that to NULL exactly when it is the repository the run
|
|
26
|
+
* acted on. The predicate never infers the answer, and the recording sites do
|
|
27
|
+
* not leave it to a default: `recordInitFailureRun`, `recordRunHeld` and
|
|
28
|
+
* `recordGlobalEvalRoundFailureRun` take the defining repository as a required
|
|
29
|
+
* argument, and `WorkflowDispatchContext` carries it as a required field, so a
|
|
30
|
+
* dispatch path that does not state it does not compile. The one run-start
|
|
31
|
+
* recorder that takes it as a trailing positional, `onExecutionStarted`, is
|
|
32
|
+
* supplied by the global dispatch path — the only path where the two
|
|
33
|
+
* repositories differ.
|
|
34
|
+
*/
|
|
35
|
+
export declare function runsDefinedByRepos(eb: ExpressionBuilder<Database, 'execution_runs'>, repoIdentifiers: readonly string[]): ExpressionWrapper<Database, 'execution_runs', SqlBool>;
|
|
36
|
+
/**
|
|
37
|
+
* The repository that defines the workflow a run executed — the read-side
|
|
38
|
+
* counterpart of {@link runsDefinedByRepos}, applied to a row the query
|
|
39
|
+
* returned. Selecting `workflow_repo_identifier` alongside `repo_identifier`
|
|
40
|
+
* and folding here keeps the grouping key the same expression the predicate
|
|
41
|
+
* filters on.
|
|
42
|
+
*/
|
|
43
|
+
export declare function definingRepoOfRun(row: {
|
|
44
|
+
repo_identifier: string;
|
|
45
|
+
workflow_repo_identifier: string | null;
|
|
46
|
+
}): string;
|
|
47
|
+
//# sourceMappingURL=registration-run-match.d.ts.map
|