@pipeline-builder/pipeline-data 4.3.11 → 4.3.13
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/lib/api/managed-toolchain.d.ts +195 -0
- package/lib/api/managed-toolchain.js +443 -0
- package/lib/api/public-directory.d.ts +4 -15
- package/lib/api/public-directory.js +1 -31
- package/lib/api/reporting/ingest.js +52 -12
- package/lib/api/reporting/stakeholder/compose.d.ts +3 -2
- package/lib/api/reporting/stakeholder/compose.js +1 -1
- package/lib/api/reporting/stakeholder/period.d.ts +5 -4
- package/lib/api/reporting/stakeholder/period.js +1 -1
- package/lib/api/reporting/stakeholder/sections.d.ts +4 -4
- package/lib/api/reporting/stakeholder/sections.js +1 -1
- package/lib/api/reporting/stakeholder/store.d.ts +3 -2
- package/lib/api/reporting/stakeholder/store.js +1 -1
- package/lib/api/reporting/toolchain-runs.d.ts +156 -0
- package/lib/api/reporting/toolchain-runs.js +267 -0
- package/lib/api/reporting/types.d.ts +20 -3
- package/lib/api/reporting/types.js +1 -1
- package/lib/api/support-cases.d.ts +50 -24
- package/lib/api/support-cases.js +96 -37
- package/lib/api/version-references.d.ts +2 -24
- package/lib/api/version-references.js +46 -7
- package/lib/core/query-filters.d.ts +6 -6
- package/lib/core/query-filters.js +1 -1
- package/lib/database/drizzle-schema.d.ts +698 -41
- package/lib/database/drizzle-schema.js +11 -1
- package/lib/database/index.d.ts +1 -1
- package/lib/database/index.js +2 -2
- package/lib/database/schema/alert.js +4 -4
- package/lib/database/schema/compliance.d.ts +29 -40
- package/lib/database/schema/compliance.js +10 -14
- package/lib/database/schema/ecosystem.d.ts +72 -0
- package/lib/database/schema/ecosystem.js +18 -2
- package/lib/database/schema/pipeline.d.ts +19 -0
- package/lib/database/schema/pipeline.js +17 -1
- package/lib/database/schema/reporting-stakeholder.d.ts +2 -1
- package/lib/database/schema/reporting-stakeholder.js +2 -27
- package/lib/database/schema/toolchain.d.ts +643 -0
- package/lib/database/schema/toolchain.js +102 -0
- package/lib/index.d.ts +4 -2
- package/lib/index.js +7 -3
- package/lib/testing/exports.json +39 -2
- package/package.json +2 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { type SqlExecutor } from '../../database/pg-result.js';
|
|
2
|
+
/** The two toolchain actions a release is judged on. */
|
|
3
|
+
export type ToolchainAction = 'synth' | 'self-mutation';
|
|
4
|
+
/** Terminal toolchain outcomes; anything else (STARTED, CANCELED, ABANDONED…) says nothing about the toolchain. */
|
|
5
|
+
export type ToolchainOutcome = 'succeeded' | 'failed';
|
|
6
|
+
/**
|
|
7
|
+
* CDK Pipelines' self-mutation action: a fixed stage and action name
|
|
8
|
+
* (`aws-cdk-lib/pipelines/lib/codepipeline/codepipeline.js`).
|
|
9
|
+
*/
|
|
10
|
+
export declare const SELF_MUTATION_STAGE = "UpdatePipeline";
|
|
11
|
+
export declare const SELF_MUTATION_ACTION = "SelfMutate";
|
|
12
|
+
/**
|
|
13
|
+
* The synth step's `stepKey` in the step manifest (`pluginStepLabel(null, …)`).
|
|
14
|
+
* The synth ACTION's name is a generated id, so it is identified by the
|
|
15
|
+
* manifest row carrying this key — the names the built pipeline really used.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SYNTH_STEP_KEY = "synth";
|
|
18
|
+
/** A manifest row, as {@link deployedToolchainOf} reads it. */
|
|
19
|
+
export interface ToolchainManifestRow {
|
|
20
|
+
pipelineId: string;
|
|
21
|
+
orgId: string;
|
|
22
|
+
stageName: string;
|
|
23
|
+
actionName: string;
|
|
24
|
+
pluginPublisher: string | null;
|
|
25
|
+
pluginName: string | null;
|
|
26
|
+
pluginVersion: string | null;
|
|
27
|
+
stepKey?: string | null;
|
|
28
|
+
}
|
|
29
|
+
/** One pipeline's deployed toolchain, from its manifest. */
|
|
30
|
+
export interface DeployedToolchain {
|
|
31
|
+
orgId: string;
|
|
32
|
+
/** The release its `build-image` entry records. */
|
|
33
|
+
releaseVersion: string;
|
|
34
|
+
/** The synth action as the built pipeline names it; null when the manifest has no synth row. */
|
|
35
|
+
synth: {
|
|
36
|
+
stageName: string;
|
|
37
|
+
actionName: string;
|
|
38
|
+
} | null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Each pipeline's deployed toolchain, from its manifest rows. A pipeline
|
|
42
|
+
* whose manifest records no `build-image` entry (deployed before releases, or
|
|
43
|
+
* a placeholder bootstrap) has none, and its actions are not stamped. Only an
|
|
44
|
+
* OWN-ORG row (no publisher) counts: a managed name can never be a listing.
|
|
45
|
+
*/
|
|
46
|
+
export declare function deployedToolchainOf(rows: readonly ToolchainManifestRow[]): Map<string, DeployedToolchain>;
|
|
47
|
+
/**
|
|
48
|
+
* Which toolchain action (if any) an ACTION event is, given its pipeline's
|
|
49
|
+
* deployed toolchain. Names are compared post-scrub, as the manifest stores them.
|
|
50
|
+
*/
|
|
51
|
+
export declare function toolchainActionOf(event: {
|
|
52
|
+
eventType: string;
|
|
53
|
+
stageName?: string | null;
|
|
54
|
+
actionName?: string | null;
|
|
55
|
+
}, deployed: DeployedToolchain | undefined): ToolchainAction | null;
|
|
56
|
+
/** The terminal outcome of a status, or null for a non-terminal / non-judging one. */
|
|
57
|
+
export declare function toolchainOutcomeOf(status: string): ToolchainOutcome | null;
|
|
58
|
+
/** The gate's thresholds (set on the console, Phase 5). */
|
|
59
|
+
export interface CanaryGateParams {
|
|
60
|
+
/** N: counted synth runs required. */
|
|
61
|
+
minRuns: number;
|
|
62
|
+
/** M: distinct orgs required. */
|
|
63
|
+
minOrgs: number;
|
|
64
|
+
/** T: the release's failure rate may exceed the default's by at most this many percentage points. */
|
|
65
|
+
maxFailurePointsAboveDefault: number;
|
|
66
|
+
}
|
|
67
|
+
export declare const DEFAULT_CANARY_GATE: Readonly<CanaryGateParams>;
|
|
68
|
+
/** One org's terminal synth outcomes on the candidate, NEWEST first. */
|
|
69
|
+
export interface OrgRuns {
|
|
70
|
+
orgId: string;
|
|
71
|
+
outcomes: readonly ToolchainOutcome[];
|
|
72
|
+
}
|
|
73
|
+
export interface CanaryGateInput {
|
|
74
|
+
releaseVersion: string;
|
|
75
|
+
candidate: readonly OrgRuns[];
|
|
76
|
+
/** The current platform default's synth runs over the same window (uncapped). */
|
|
77
|
+
baseline: {
|
|
78
|
+
releaseVersion: string | null;
|
|
79
|
+
runs: number;
|
|
80
|
+
failures: number;
|
|
81
|
+
};
|
|
82
|
+
params?: Partial<CanaryGateParams>;
|
|
83
|
+
}
|
|
84
|
+
export type CanaryGateShortfall = 'runs' | 'orgs' | 'failure-rate';
|
|
85
|
+
/** The gate's verdict and every number behind it — what the console shows. */
|
|
86
|
+
export interface CanaryGateResult {
|
|
87
|
+
passed: boolean;
|
|
88
|
+
releaseVersion: string;
|
|
89
|
+
params: CanaryGateParams;
|
|
90
|
+
/** ceil(N / M): the most runs any one org contributes. */
|
|
91
|
+
perOrgCap: number;
|
|
92
|
+
/** Runs counted toward N, after the per-org cap. */
|
|
93
|
+
countedRuns: number;
|
|
94
|
+
/** Every terminal synth run on the release, before the cap. */
|
|
95
|
+
totalRuns: number;
|
|
96
|
+
/** Distinct orgs with at least one counted run. */
|
|
97
|
+
orgs: number;
|
|
98
|
+
/** Failures among the counted runs. */
|
|
99
|
+
failures: number;
|
|
100
|
+
/** Percent (0–100) over the counted runs; null with none. */
|
|
101
|
+
failureRate: number | null;
|
|
102
|
+
baselineReleaseVersion: string | null;
|
|
103
|
+
baselineRuns: number;
|
|
104
|
+
/** Percent (0–100); 0 when the default has no runs in the window (the strict reading). */
|
|
105
|
+
baselineFailureRate: number;
|
|
106
|
+
/** failureRate − baselineFailureRate, in points; null with no counted runs. */
|
|
107
|
+
pointsAboveDefault: number | null;
|
|
108
|
+
/** Which thresholds were not met (empty when passed). */
|
|
109
|
+
shortfalls: CanaryGateShortfall[];
|
|
110
|
+
}
|
|
111
|
+
/** The per-org contribution cap: ceil(N / M). */
|
|
112
|
+
export declare function canaryPerOrgCap(params: Pick<CanaryGateParams, 'minRuns' | 'minOrgs'>): number;
|
|
113
|
+
/**
|
|
114
|
+
* The canary gate: at least N counted synth runs across at least M orgs, and a
|
|
115
|
+
* failure rate within T points of the current default's. Each org counts its
|
|
116
|
+
* most recent `ceil(N / M)` runs only — toward N AND toward the failure rate,
|
|
117
|
+
* so one org can neither satisfy N alone nor dominate the rate. Being BETTER
|
|
118
|
+
* than the default never fails the gate.
|
|
119
|
+
*/
|
|
120
|
+
export declare function evaluateCanaryGate(input: CanaryGateInput): CanaryGateResult;
|
|
121
|
+
/**
|
|
122
|
+
* Evaluate the canary gate for `releaseVersion` against `baselineReleaseVersion`
|
|
123
|
+
* (the current platform default; null when none) from the stamped rows.
|
|
124
|
+
*
|
|
125
|
+
* Window: since the release was registered (`toolchain_release.created_at`),
|
|
126
|
+
* for both the candidate and the default, so the two rates are contemporaneous.
|
|
127
|
+
* Only terminal (SUCCEEDED / FAILED) SYNTH runs count; self-mutation is not
|
|
128
|
+
* part of the gate (§6 counts synth runs).
|
|
129
|
+
*
|
|
130
|
+
* Reads every org's rows: run it ELEVATED (the plugin service's toolchain tx
|
|
131
|
+
* is). The candidate read is bounded at `ceil(N/M)` rows per org.
|
|
132
|
+
*/
|
|
133
|
+
export declare function readCanaryGate(db: SqlExecutor, releaseVersion: string, baselineReleaseVersion: string | null, params?: Partial<CanaryGateParams>): Promise<CanaryGateResult>;
|
|
134
|
+
export interface AffectedOrg {
|
|
135
|
+
orgId: string;
|
|
136
|
+
/** Pipelines in this org on the release whose latest synth failed. */
|
|
137
|
+
pipelines: number;
|
|
138
|
+
pipelineIds: string[];
|
|
139
|
+
}
|
|
140
|
+
export interface AffectedPipelines {
|
|
141
|
+
releaseVersion: string;
|
|
142
|
+
totalPipelines: number;
|
|
143
|
+
orgs: AffectedOrg[];
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Pipelines whose DEPLOYED release is `releaseVersion` (their manifest's
|
|
147
|
+
* `build-image` entry) and whose LATEST terminal synth run failed, grouped by
|
|
148
|
+
* org — the console's Affected view and the "Notify affected owners" target
|
|
149
|
+
* set. A pipeline whose latest synth succeeded has healed and is not listed.
|
|
150
|
+
*
|
|
151
|
+
* Elevated read: it spans every org. Callers show the system-org operator
|
|
152
|
+
* counts, and notify each org about its own pipelines only.
|
|
153
|
+
*/
|
|
154
|
+
export declare function readAffectedPipelines(db: SqlExecutor, releaseVersion: string): Promise<AffectedPipelines>;
|
|
155
|
+
/** The current platform default (elevated read), or null. */
|
|
156
|
+
export declare function readPlatformDefaultRelease(db: SqlExecutor): Promise<string | null>;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Toolchain RUN DATA (docs/plans/bootstrap-image-versioning.md §6, §7, §9):
|
|
5
|
+
* which managed toolchain release each `synth` / `self-mutation` action ran on,
|
|
6
|
+
* and the two queries built over those rows — the canary gate and the Affected
|
|
7
|
+
* view.
|
|
8
|
+
*
|
|
9
|
+
* WHERE THE RELEASE COMES FROM. pipeline-events is a Lambda in the CUSTOMER's
|
|
10
|
+
* account; everything it posts is customer-controlled, and the tags it reads
|
|
11
|
+
* are cached for 15 minutes. So the release is never read from the payload or
|
|
12
|
+
* a tag: the ingest joins the event to the PLATFORM's deployed step manifest
|
|
13
|
+
* for that pipeline (`pipeline_step_manifests`, written by the registry POST
|
|
14
|
+
* after a deploy) and takes the release from its `build-image` entry — the
|
|
15
|
+
* `pipeline-bootstrap` row, whose version IS the release (both halves share
|
|
16
|
+
* it). The manifest is replaced on every deploy, so it is the release the
|
|
17
|
+
* pipeline was deployed with when the event arrives.
|
|
18
|
+
*
|
|
19
|
+
* THE GATE IS ADVISORY. Outcomes are customer-reported (the events Lambda and
|
|
20
|
+
* its key live in the customer's account), so a canary org could forge them.
|
|
21
|
+
* Each org therefore counts for at most `ceil(N / M)` runs, so one org cannot
|
|
22
|
+
* satisfy the gate alone; the release comes from the platform's manifest; and
|
|
23
|
+
* the gate only informs a human — an override records a reason.
|
|
24
|
+
*/
|
|
25
|
+
import { BUILD_IMAGE_STEP_KEY, MANAGED_TOOLCHAIN, TOOLCHAIN_RELEASE_VERSION_PATTERN } from '@pipeline-builder/api-core';
|
|
26
|
+
import { sql } from 'drizzle-orm';
|
|
27
|
+
import { executeRows } from '../../database/pg-result.js';
|
|
28
|
+
/**
|
|
29
|
+
* CDK Pipelines' self-mutation action: a fixed stage and action name
|
|
30
|
+
* (`aws-cdk-lib/pipelines/lib/codepipeline/codepipeline.js`).
|
|
31
|
+
*/
|
|
32
|
+
export const SELF_MUTATION_STAGE = 'UpdatePipeline';
|
|
33
|
+
export const SELF_MUTATION_ACTION = 'SelfMutate';
|
|
34
|
+
/**
|
|
35
|
+
* The synth step's `stepKey` in the step manifest (`pluginStepLabel(null, …)`).
|
|
36
|
+
* The synth ACTION's name is a generated id, so it is identified by the
|
|
37
|
+
* manifest row carrying this key — the names the built pipeline really used.
|
|
38
|
+
*/
|
|
39
|
+
export const SYNTH_STEP_KEY = 'synth';
|
|
40
|
+
/**
|
|
41
|
+
* The `build-image` entry's plugin: the release's bootstrap half. The entry is
|
|
42
|
+
* the manifest's non-step row under `step_key` {@link BUILD_IMAGE_STEP_KEY}
|
|
43
|
+
* (api-core `BUILD_IMAGE_MANIFEST_*`), and its `plugin_version` IS the release.
|
|
44
|
+
*/
|
|
45
|
+
const BUILD_IMAGE_PLUGIN = MANAGED_TOOLCHAIN.bootstrap;
|
|
46
|
+
/**
|
|
47
|
+
* Each pipeline's deployed toolchain, from its manifest rows. A pipeline
|
|
48
|
+
* whose manifest records no `build-image` entry (deployed before releases, or
|
|
49
|
+
* a placeholder bootstrap) has none, and its actions are not stamped. Only an
|
|
50
|
+
* OWN-ORG row (no publisher) counts: a managed name can never be a listing.
|
|
51
|
+
*/
|
|
52
|
+
export function deployedToolchainOf(rows) {
|
|
53
|
+
const release = new Map();
|
|
54
|
+
const synth = new Map();
|
|
55
|
+
for (const r of rows) {
|
|
56
|
+
if (r.stepKey === BUILD_IMAGE_STEP_KEY && r.pluginName === BUILD_IMAGE_PLUGIN && r.pluginPublisher === null
|
|
57
|
+
&& typeof r.pluginVersion === 'string' && TOOLCHAIN_RELEASE_VERSION_PATTERN.test(r.pluginVersion)) {
|
|
58
|
+
release.set(r.pipelineId, { orgId: r.orgId, releaseVersion: r.pluginVersion });
|
|
59
|
+
}
|
|
60
|
+
if (r.stepKey === SYNTH_STEP_KEY)
|
|
61
|
+
synth.set(r.pipelineId, { stageName: r.stageName, actionName: r.actionName });
|
|
62
|
+
}
|
|
63
|
+
const out = new Map();
|
|
64
|
+
for (const [pipelineId, r] of release)
|
|
65
|
+
out.set(pipelineId, { ...r, synth: synth.get(pipelineId) ?? null });
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Which toolchain action (if any) an ACTION event is, given its pipeline's
|
|
70
|
+
* deployed toolchain. Names are compared post-scrub, as the manifest stores them.
|
|
71
|
+
*/
|
|
72
|
+
export function toolchainActionOf(event, deployed) {
|
|
73
|
+
if (event.eventType !== 'ACTION' || !deployed || !event.stageName || !event.actionName)
|
|
74
|
+
return null;
|
|
75
|
+
if (event.stageName === SELF_MUTATION_STAGE && event.actionName === SELF_MUTATION_ACTION)
|
|
76
|
+
return 'self-mutation';
|
|
77
|
+
if (deployed.synth && event.stageName === deployed.synth.stageName && event.actionName === deployed.synth.actionName)
|
|
78
|
+
return 'synth';
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/** The terminal outcome of a status, or null for a non-terminal / non-judging one. */
|
|
82
|
+
export function toolchainOutcomeOf(status) {
|
|
83
|
+
if (status === 'SUCCEEDED')
|
|
84
|
+
return 'succeeded';
|
|
85
|
+
if (status === 'FAILED')
|
|
86
|
+
return 'failed';
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/** SQL: a stamped row's action (stamped rows are synth or self-mutation only). */
|
|
90
|
+
const IS_SELF_MUTATION = sql `(e.stage_name = ${SELF_MUTATION_STAGE} AND e.action_name = ${SELF_MUTATION_ACTION})`;
|
|
91
|
+
export const DEFAULT_CANARY_GATE = Object.freeze({
|
|
92
|
+
minRuns: 20,
|
|
93
|
+
minOrgs: 2,
|
|
94
|
+
maxFailurePointsAboveDefault: 5,
|
|
95
|
+
});
|
|
96
|
+
/** Round to 2 dp, so the numbers the console shows are stable. */
|
|
97
|
+
function r2(n) {
|
|
98
|
+
return Math.round(n * 100) / 100;
|
|
99
|
+
}
|
|
100
|
+
function resolveParams(p) {
|
|
101
|
+
const merged = { ...DEFAULT_CANARY_GATE, ...(p ?? {}) };
|
|
102
|
+
const minRuns = Math.max(1, Math.floor(merged.minRuns));
|
|
103
|
+
const minOrgs = Math.max(1, Math.floor(merged.minOrgs));
|
|
104
|
+
const maxFailurePointsAboveDefault = Math.max(0, merged.maxFailurePointsAboveDefault);
|
|
105
|
+
if (![minRuns, minOrgs, maxFailurePointsAboveDefault].every(Number.isFinite))
|
|
106
|
+
return { ...DEFAULT_CANARY_GATE };
|
|
107
|
+
return { minRuns, minOrgs, maxFailurePointsAboveDefault };
|
|
108
|
+
}
|
|
109
|
+
/** The per-org contribution cap: ceil(N / M). */
|
|
110
|
+
export function canaryPerOrgCap(params) {
|
|
111
|
+
return Math.ceil(params.minRuns / params.minOrgs);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The canary gate: at least N counted synth runs across at least M orgs, and a
|
|
115
|
+
* failure rate within T points of the current default's. Each org counts its
|
|
116
|
+
* most recent `ceil(N / M)` runs only — toward N AND toward the failure rate,
|
|
117
|
+
* so one org can neither satisfy N alone nor dominate the rate. Being BETTER
|
|
118
|
+
* than the default never fails the gate.
|
|
119
|
+
*/
|
|
120
|
+
export function evaluateCanaryGate(input) {
|
|
121
|
+
const params = resolveParams(input.params);
|
|
122
|
+
const perOrgCap = canaryPerOrgCap(params);
|
|
123
|
+
let countedRuns = 0;
|
|
124
|
+
let failures = 0;
|
|
125
|
+
let totalRuns = 0;
|
|
126
|
+
let orgs = 0;
|
|
127
|
+
for (const org of input.candidate) {
|
|
128
|
+
totalRuns += org.outcomes.length;
|
|
129
|
+
const counted = org.outcomes.slice(0, perOrgCap);
|
|
130
|
+
if (counted.length === 0)
|
|
131
|
+
continue;
|
|
132
|
+
orgs++;
|
|
133
|
+
countedRuns += counted.length;
|
|
134
|
+
failures += counted.filter((o) => o === 'failed').length;
|
|
135
|
+
}
|
|
136
|
+
const failureRate = countedRuns > 0 ? r2((failures / countedRuns) * 100) : null;
|
|
137
|
+
const baselineFailureRate = input.baseline.runs > 0 ? r2((input.baseline.failures / input.baseline.runs) * 100) : 0;
|
|
138
|
+
const pointsAboveDefault = failureRate === null ? null : r2(failureRate - baselineFailureRate);
|
|
139
|
+
const shortfalls = [];
|
|
140
|
+
if (countedRuns < params.minRuns)
|
|
141
|
+
shortfalls.push('runs');
|
|
142
|
+
if (orgs < params.minOrgs)
|
|
143
|
+
shortfalls.push('orgs');
|
|
144
|
+
if (pointsAboveDefault === null || pointsAboveDefault > params.maxFailurePointsAboveDefault)
|
|
145
|
+
shortfalls.push('failure-rate');
|
|
146
|
+
return {
|
|
147
|
+
passed: shortfalls.length === 0,
|
|
148
|
+
releaseVersion: input.releaseVersion,
|
|
149
|
+
params,
|
|
150
|
+
perOrgCap,
|
|
151
|
+
countedRuns,
|
|
152
|
+
totalRuns,
|
|
153
|
+
orgs,
|
|
154
|
+
failures,
|
|
155
|
+
failureRate,
|
|
156
|
+
baselineReleaseVersion: input.baseline.releaseVersion,
|
|
157
|
+
baselineRuns: input.baseline.runs,
|
|
158
|
+
baselineFailureRate,
|
|
159
|
+
pointsAboveDefault,
|
|
160
|
+
shortfalls,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// -----------------------------------------------------------------------------
|
|
164
|
+
// Canary gate — the read
|
|
165
|
+
// -----------------------------------------------------------------------------
|
|
166
|
+
/**
|
|
167
|
+
* Evaluate the canary gate for `releaseVersion` against `baselineReleaseVersion`
|
|
168
|
+
* (the current platform default; null when none) from the stamped rows.
|
|
169
|
+
*
|
|
170
|
+
* Window: since the release was registered (`toolchain_release.created_at`),
|
|
171
|
+
* for both the candidate and the default, so the two rates are contemporaneous.
|
|
172
|
+
* Only terminal (SUCCEEDED / FAILED) SYNTH runs count; self-mutation is not
|
|
173
|
+
* part of the gate (§6 counts synth runs).
|
|
174
|
+
*
|
|
175
|
+
* Reads every org's rows: run it ELEVATED (the plugin service's toolchain tx
|
|
176
|
+
* is). The candidate read is bounded at `ceil(N/M)` rows per org.
|
|
177
|
+
*/
|
|
178
|
+
export async function readCanaryGate(db, releaseVersion, baselineReleaseVersion, params) {
|
|
179
|
+
const resolved = resolveParams(params);
|
|
180
|
+
const cap = canaryPerOrgCap(resolved);
|
|
181
|
+
const since = sql `(SELECT created_at FROM toolchain_release WHERE release_version = ${releaseVersion})`;
|
|
182
|
+
const candidate = await executeRows(db, sql `
|
|
183
|
+
SELECT org_id, status, total FROM (
|
|
184
|
+
SELECT e.org_id, e.status,
|
|
185
|
+
row_number() OVER (PARTITION BY e.org_id ORDER BY coalesce(e.completed_at, e.created_at) DESC, e.id) AS rn,
|
|
186
|
+
count(*) OVER (PARTITION BY e.org_id) AS total
|
|
187
|
+
FROM pipeline_events e
|
|
188
|
+
WHERE e.toolchain_release = ${releaseVersion}
|
|
189
|
+
AND e.event_type = 'ACTION' AND e.status IN ('SUCCEEDED', 'FAILED')
|
|
190
|
+
AND NOT ${IS_SELF_MUTATION}
|
|
191
|
+
AND e.created_at >= coalesce(${since}, '-infinity'::timestamptz)
|
|
192
|
+
) x
|
|
193
|
+
WHERE rn <= ${cap}
|
|
194
|
+
ORDER BY org_id, rn
|
|
195
|
+
`);
|
|
196
|
+
const byOrg = new Map();
|
|
197
|
+
for (const row of candidate) {
|
|
198
|
+
const entry = byOrg.get(row.org_id) ?? { outcomes: [], total: Number(row.total) };
|
|
199
|
+
entry.outcomes.push(row.status === 'FAILED' ? 'failed' : 'succeeded');
|
|
200
|
+
byOrg.set(row.org_id, entry);
|
|
201
|
+
}
|
|
202
|
+
let baseline = { releaseVersion: baselineReleaseVersion, runs: 0, failures: 0 };
|
|
203
|
+
if (baselineReleaseVersion !== null && baselineReleaseVersion !== releaseVersion) {
|
|
204
|
+
const [b] = await executeRows(db, sql `
|
|
205
|
+
SELECT count(*) AS runs, count(*) FILTER (WHERE e.status = 'FAILED') AS failures
|
|
206
|
+
FROM pipeline_events e
|
|
207
|
+
WHERE e.toolchain_release = ${baselineReleaseVersion}
|
|
208
|
+
AND e.event_type = 'ACTION' AND e.status IN ('SUCCEEDED', 'FAILED')
|
|
209
|
+
AND NOT ${IS_SELF_MUTATION}
|
|
210
|
+
AND e.created_at >= coalesce(${since}, '-infinity'::timestamptz)
|
|
211
|
+
`);
|
|
212
|
+
baseline = { releaseVersion: baselineReleaseVersion, runs: Number(b?.runs ?? 0), failures: Number(b?.failures ?? 0) };
|
|
213
|
+
}
|
|
214
|
+
const result = evaluateCanaryGate({
|
|
215
|
+
releaseVersion,
|
|
216
|
+
candidate: [...byOrg.entries()].map(([orgId, e]) => ({ orgId, outcomes: e.outcomes })),
|
|
217
|
+
baseline,
|
|
218
|
+
params: resolved,
|
|
219
|
+
});
|
|
220
|
+
// The read is already capped per org, so the pre-cap total is the window count.
|
|
221
|
+
return { ...result, totalRuns: [...byOrg.values()].reduce((n, e) => n + e.total, 0) };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Pipelines whose DEPLOYED release is `releaseVersion` (their manifest's
|
|
225
|
+
* `build-image` entry) and whose LATEST terminal synth run failed, grouped by
|
|
226
|
+
* org — the console's Affected view and the "Notify affected owners" target
|
|
227
|
+
* set. A pipeline whose latest synth succeeded has healed and is not listed.
|
|
228
|
+
*
|
|
229
|
+
* Elevated read: it spans every org. Callers show the system-org operator
|
|
230
|
+
* counts, and notify each org about its own pipelines only.
|
|
231
|
+
*/
|
|
232
|
+
export async function readAffectedPipelines(db, releaseVersion) {
|
|
233
|
+
const rows = await executeRows(db, sql `
|
|
234
|
+
SELECT m.org_id, m.pipeline_id::text AS pipeline_id
|
|
235
|
+
FROM pipeline_step_manifests m
|
|
236
|
+
JOIN LATERAL (
|
|
237
|
+
SELECT e.status
|
|
238
|
+
FROM pipeline_events e
|
|
239
|
+
WHERE e.pipeline_id = m.pipeline_id
|
|
240
|
+
AND e.toolchain_release IS NOT NULL
|
|
241
|
+
AND e.event_type = 'ACTION' AND e.status IN ('SUCCEEDED', 'FAILED')
|
|
242
|
+
AND NOT ${IS_SELF_MUTATION}
|
|
243
|
+
ORDER BY coalesce(e.completed_at, e.created_at) DESC, e.created_at DESC
|
|
244
|
+
LIMIT 1
|
|
245
|
+
) latest ON latest.status = 'FAILED'
|
|
246
|
+
WHERE m.step_key = ${BUILD_IMAGE_STEP_KEY} AND m.plugin_name = ${BUILD_IMAGE_PLUGIN} AND m.plugin_publisher IS NULL
|
|
247
|
+
AND m.plugin_version = ${releaseVersion}
|
|
248
|
+
ORDER BY m.org_id, m.pipeline_id
|
|
249
|
+
`);
|
|
250
|
+
const byOrg = new Map();
|
|
251
|
+
for (const r of rows) {
|
|
252
|
+
const ids = byOrg.get(r.org_id) ?? [];
|
|
253
|
+
if (!ids.includes(r.pipeline_id))
|
|
254
|
+
ids.push(r.pipeline_id);
|
|
255
|
+
byOrg.set(r.org_id, ids);
|
|
256
|
+
}
|
|
257
|
+
const orgs = [...byOrg.entries()].map(([orgId, pipelineIds]) => ({ orgId, pipelines: pipelineIds.length, pipelineIds }));
|
|
258
|
+
return { releaseVersion, totalPipelines: orgs.reduce((n, o) => n + o.pipelines, 0), orgs };
|
|
259
|
+
}
|
|
260
|
+
/** The current platform default (elevated read), or null. */
|
|
261
|
+
export async function readPlatformDefaultRelease(db) {
|
|
262
|
+
const [row] = await executeRows(db, sql `
|
|
263
|
+
SELECT release_version FROM toolchain_platform_default WHERE singleton = true
|
|
264
|
+
`);
|
|
265
|
+
return row?.release_version ?? null;
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbGNoYWluLXJ1bnMuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9hcGkvcmVwb3J0aW5nL3Rvb2xjaGFpbi1ydW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXFCRztBQUVILE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxpQkFBaUIsRUFBRSxpQ0FBaUMsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3hILE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxFQUFFLFdBQVcsRUFBb0IsTUFBTSw2QkFBNkIsQ0FBQztBQVk1RTs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxnQkFBZ0IsQ0FBQztBQUNwRCxNQUFNLENBQUMsTUFBTSxvQkFBb0IsR0FBRyxZQUFZLENBQUM7QUFFakQ7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxPQUFPLENBQUM7QUFFdEM7Ozs7R0FJRztBQUNILE1BQU0sa0JBQWtCLEdBQUcsaUJBQWlCLENBQUMsU0FBUyxDQUFDO0FBdUJ2RDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxJQUFxQztJQUN2RSxNQUFNLE9BQU8sR0FBRyxJQUFJLEdBQUcsRUFBcUQsQ0FBQztJQUM3RSxNQUFNLEtBQUssR0FBRyxJQUFJLEdBQUcsRUFBcUQsQ0FBQztJQUMzRSxLQUFLLE1BQU0sQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxDQUFDLE9BQU8sS0FBSyxvQkFBb0IsSUFBSSxDQUFDLENBQUMsVUFBVSxLQUFLLGtCQUFrQixJQUFJLENBQUMsQ0FBQyxlQUFlLEtBQUssSUFBSTtlQUN0RyxPQUFPLENBQUMsQ0FBQyxhQUFhLEtBQUssUUFBUSxJQUFJLGlDQUFpQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFD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QVcsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN6SCxPQUFPLEVBQUUsY0FBYyxFQUFFLGNBQWMsRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDN0YsQ0FBQztBQUVELDZEQUE2RDtBQUM3RCxNQUFNLENBQUMsS0FBSyxVQUFVLDBCQUEwQixDQUFDLEVBQWU7SUFDOUQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLE1BQU0sV0FBVyxDQUE4QixFQUFFLEVBQUUsR0FBRyxDQUFBOztHQUVuRSxDQUFDLENBQUM7SUFDSCxPQUFPLEdBQUcsRUFBRSxlQUFlLElBQUksSUFBSSxDQUFDO0FBQ3RDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBUb29sY2hhaW4gUlVOIERBVEEgKGRvY3MvcGxhbnMvYm9vdHN0cmFwLWltYWdlLXZlcnNpb25pbmcubWQgwqc2LCDCpzcsIMKnOSk6XG4gKiB3aGljaCBtYW5hZ2VkIHRvb2xjaGFpbiByZWxlYXNlIGVhY2ggYHN5bnRoYCAvIGBzZWxmLW11dGF0aW9uYCBhY3Rpb24gcmFuIG9uLFxuICogYW5kIHRoZSB0d28gcXVlcmllcyBidWlsdCBvdmVyIHRob3NlIHJvd3Mg4oCUIHRoZSBjYW5hcnkgZ2F0ZSBhbmQgdGhlIEFmZmVjdGVkXG4gKiB2aWV3LlxuICpcbiAqIFdIRVJFIFRIRSBSRUxFQVNFIENPTUVTIEZST00uIHBpcGVsaW5lLWV2ZW50cyBpcyBhIExhbWJkYSBpbiB0aGUgQ1VTVE9NRVInc1xuICogYWNjb3VudDsgZXZlcnl0aGluZyBpdCBwb3N0cyBpcyBjdXN0b21lci1jb250cm9sbGVkLCBhbmQgdGhlIHRhZ3MgaXQgcmVhZHNcbiAqIGFyZSBjYWNoZWQgZm9yIDE1IG1pbnV0ZXMuIFNvIHRoZSByZWxlYXNlIGlzIG5ldmVyIHJlYWQgZnJvbSB0aGUgcGF5bG9hZCBvclxuICogYSB0YWc6IHRoZSBpbmdlc3Qgam9pbnMgdGhlIGV2ZW50IHRvIHRoZSBQTEFURk9STSdzIGRlcGxveWVkIHN0ZXAgbWFuaWZlc3RcbiAqIGZvciB0aGF0IHBpcGVsaW5lIChgcGlwZWxpbmVfc3RlcF9tYW5pZmVzdHNgLCB3cml0dGVuIGJ5IHRoZSByZWdpc3RyeSBQT1NUXG4gKiBhZnRlciBhIGRlcGxveSkgYW5kIHRha2VzIHRoZSByZWxlYXNlIGZyb20gaXRzIGBidWlsZC1pbWFnZWAgZW50cnkg4oCUIHRoZVxuICogYHBpcGVsaW5lLWJvb3RzdHJhcGAgcm93LCB3aG9zZSB2ZXJzaW9uIElTIHRoZSByZWxlYXNlIChib3RoIGhhbHZlcyBzaGFyZVxuICogaXQpLiBUaGUgbWFuaWZlc3QgaXMgcmVwbGFjZWQgb24gZXZlcnkgZGVwbG95LCBzbyBpdCBpcyB0aGUgcmVsZWFzZSB0aGVcbiAqIHBpcGVsaW5lIHdhcyBkZXBsb3llZCB3aXRoIHdoZW4gdGhlIGV2ZW50IGFycml2ZXMuXG4gKlxuICogVEhFIEdBVEUgSVMgQURWSVNPUlkuIE91dGNvbWVzIGFyZSBjdXN0b21lci1yZXBvcnRlZCAodGhlIGV2ZW50cyBMYW1iZGEgYW5kXG4gKiBpdHMga2V5IGxpdmUgaW4gdGhlIGN1c3RvbWVyJ3MgYWNjb3VudCksIHNvIGEgY2FuYXJ5IG9yZyBjb3VsZCBmb3JnZSB0aGVtLlxuICogRWFjaCBvcmcgdGhlcmVmb3JlIGNvdW50cyBmb3IgYXQgbW9zdCBgY2VpbChOIC8gTSlgIHJ1bnMsIHNvIG9uZSBvcmcgY2Fubm90XG4gKiBzYXRpc2Z5IHRoZSBnYXRlIGFsb25lOyB0aGUgcmVsZWFzZSBjb21lcyBmcm9tIHRoZSBwbGF0Zm9ybSdzIG1hbmlmZXN0OyBhbmRcbiAqIHRoZSBnYXRlIG9ubHkgaW5mb3JtcyBhIGh1bWFuIOKAlCBhbiBvdmVycmlkZSByZWNvcmRzIGEgcmVhc29uLlxuICovXG5cbmltcG9ydCB7IEJVSUxEX0lNQUdFX1NURVBfS0VZLCBNQU5BR0VEX1RPT0xDSEFJTiwgVE9PTENIQUlOX1JFTEVBU0VfVkVSU0lPTl9QQVRURVJOIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHsgc3FsIH0gZnJvbSAnZHJpenpsZS1vcm0nO1xuaW1wb3J0IHsgZXhlY3V0ZVJvd3MsIHR5cGUgU3FsRXhlY3V0b3IgfSBmcm9tICcuLi8uLi9kYXRhYmFzZS9wZy1yZXN1bHQuanMnO1xuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gV2hpY2ggYWN0aW9ucyBhcmUgdG9vbGNoYWluIHJ1bnNcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8qKiBUaGUgdHdvIHRvb2xjaGFpbiBhY3Rpb25zIGEgcmVsZWFzZSBpcyBqdWRnZWQgb24uICovXG5leHBvcnQgdHlwZSBUb29sY2hhaW5BY3Rpb24gPSAnc3ludGgnIHwgJ3NlbGYtbXV0YXRpb24nO1xuXG4vKiogVGVybWluYWwgdG9vbGNoYWluIG91dGNvbWVzOyBhbnl0aGluZyBlbHNlIChTVEFSVEVELCBDQU5DRUxFRCwgQUJBTkRPTkVE4oCmKSBzYXlzIG5vdGhpbmcgYWJvdXQgdGhlIHRvb2xjaGFpbi4gKi9cbmV4cG9ydCB0eXBlIFRvb2xjaGFpbk91dGNvbWUgPSAnc3VjY2VlZGVkJyB8ICdmYWlsZWQnO1xuXG4vKipcbiAqIENESyBQaXBlbGluZXMnIHNlbGYtbXV0YXRpb24gYWN0aW9uOiBhIGZpeGVkIHN0YWdlIGFuZCBhY3Rpb24gbmFtZVxuICogKGBhd3MtY2RrLWxpYi9waXBlbGluZXMvbGliL2NvZGVwaXBlbGluZS9jb2RlcGlwZWxpbmUuanNgKS5cbiAqL1xuZXhwb3J0IGNvbnN0IFNFTEZfTVVUQVRJT05fU1RBR0UgPSAnVXBkYXRlUGlwZWxpbmUnO1xuZXhwb3J0IGNvbnN0IFNFTEZfTVVUQVRJT05fQUNUSU9OID0gJ1NlbGZNdXRhdGUnO1xuXG4vKipcbiAqIFRoZSBzeW50aCBzdGVwJ3MgYHN0ZXBLZXlgIGluIHRoZSBzdGVwIG1hbmlmZXN0IChgcGx1Z2luU3RlcExhYmVsKG51bGwsIOKApilgKS5cbiAqIFRoZSBzeW50aCBBQ1RJT04ncyBuYW1lIGlzIGEgZ2VuZXJhdGVkIGlkLCBzbyBpdCBpcyBpZGVudGlmaWVkIGJ5IHRoZVxuICogbWFuaWZlc3Qgcm93IGNhcnJ5aW5nIHRoaXMga2V5IOKAlCB0aGUgbmFtZXMgdGhlIGJ1aWx0IHBpcGVsaW5lIHJlYWxseSB1c2VkLlxuICovXG5leHBvcnQgY29uc3QgU1lOVEhfU1RFUF9LRVkgPSAnc3ludGgnO1xuXG4vKipcbiAqIFRoZSBgYnVpbGQtaW1hZ2VgIGVudHJ5J3MgcGx1Z2luOiB0aGUgcmVsZWFzZSdzIGJvb3RzdHJhcCBoYWxmLiBUaGUgZW50cnkgaXNcbiAqIHRoZSBtYW5pZmVzdCdzIG5vbi1zdGVwIHJvdyB1bmRlciBgc3RlcF9rZXlgIHtAbGluayBCVUlMRF9JTUFHRV9TVEVQX0tFWX1cbiAqIChhcGktY29yZSBgQlVJTERfSU1BR0VfTUFOSUZFU1RfKmApLCBhbmQgaXRzIGBwbHVnaW5fdmVyc2lvbmAgSVMgdGhlIHJlbGVhc2UuXG4gKi9cbmNvbnN0IEJVSUxEX0lNQUdFX1BMVUdJTiA9IE1BTkFHRURfVE9PTENIQUlOLmJvb3RzdHJhcDtcblxuLyoqIEEgbWFuaWZlc3Qgcm93LCBhcyB7QGxpbmsgZGVwbG95ZWRUb29sY2hhaW5PZn0gcmVhZHMgaXQuICovXG5leHBvcnQgaW50ZXJmYWNlIFRvb2xjaGFpbk1hbmlmZXN0Um93IHtcbiAgcGlwZWxpbmVJZDogc3RyaW5nO1xuICBvcmdJZDogc3RyaW5nO1xuICBzdGFnZU5hbWU6IHN0cmluZztcbiAgYWN0aW9uTmFtZTogc3RyaW5nO1xuICBwbHVnaW5QdWJsaXNoZXI6IHN0cmluZyB8IG51bGw7XG4gIHBsdWdpbk5hbWU6IHN0cmluZyB8IG51bGw7XG4gIHBsdWdpblZlcnNpb246IHN0cmluZyB8IG51bGw7XG4gIHN0ZXBLZXk/OiBzdHJpbmcgfCBudWxsO1xufVxuXG4vKiogT25lIHBpcGVsaW5lJ3MgZGVwbG95ZWQgdG9vbGNoYWluLCBmcm9tIGl0cyBtYW5pZmVzdC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRGVwbG95ZWRUb29sY2hhaW4ge1xuICBvcmdJZDogc3RyaW5nO1xuICAvKiogVGhlIHJlbGVhc2UgaXRzIGBidWlsZC1pbWFnZWAgZW50cnkgcmVjb3Jkcy4gKi9cbiAgcmVsZWFzZVZlcnNpb246IHN0cmluZztcbiAgLyoqIFRoZSBzeW50aCBhY3Rpb24gYXMgdGhlIGJ1aWx0IHBpcGVsaW5lIG5hbWVzIGl0OyBudWxsIHdoZW4gdGhlIG1hbmlmZXN0IGhhcyBubyBzeW50aCByb3cuICovXG4gIHN5bnRoOiB7IHN0YWdlTmFtZTogc3RyaW5nOyBhY3Rpb25OYW1lOiBzdHJpbmcgfSB8IG51bGw7XG59XG5cbi8qKlxuICogRWFjaCBwaXBlbGluZSdzIGRlcGxveWVkIHRvb2xjaGFpbiwgZnJvbSBpdHMgbWFuaWZlc3Qgcm93cy4gQSBwaXBlbGluZVxuICogd2hvc2UgbWFuaWZlc3QgcmVjb3JkcyBubyBgYnVpbGQtaW1hZ2VgIGVudHJ5IChkZXBsb3llZCBiZWZvcmUgcmVsZWFzZXMsIG9yXG4gKiBhIHBsYWNlaG9sZGVyIGJvb3RzdHJhcCkgaGFzIG5vbmUsIGFuZCBpdHMgYWN0aW9ucyBhcmUgbm90IHN0YW1wZWQuIE9ubHkgYW5cbiAqIE9XTi1PUkcgcm93IChubyBwdWJsaXNoZXIpIGNvdW50czogYSBtYW5hZ2VkIG5hbWUgY2FuIG5ldmVyIGJlIGEgbGlzdGluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlcGxveWVkVG9vbGNoYWluT2Yocm93czogcmVhZG9ubHkgVG9vbGNoYWluTWFuaWZlc3RSb3dbXSk6IE1hcDxzdHJpbmcsIERlcGxveWVkVG9vbGNoYWluPiB7XG4gIGNvbnN0IHJlbGVhc2UgPSBuZXcgTWFwPHN0cmluZywgeyBvcmdJZDogc3RyaW5nOyByZWxlYXNlVmVyc2lvbjogc3RyaW5nIH0+KCk7XG4gIGNvbnN0IHN5bnRoID0gbmV3IE1hcDxzdHJpbmcsIHsgc3RhZ2VOYW1lOiBzdHJpbmc7IGFjdGlvbk5hbWU6IHN0cmluZyB9PigpO1xuICBmb3IgKGNvbnN0IHIgb2Ygcm93cykge1xuICAgIGlmIChyLnN0ZXBLZXkgPT09IEJVSUxEX0lNQUdFX1NURVBfS0VZICYmIHIucGx1Z2luTmFtZSA9PT0gQlVJTERfSU1BR0VfUExVR0lOICYmIHIucGx1Z2luUHVibGlzaGVyID09PSBudWxsXG4gICAgICAmJiB0eXBlb2Ygci5wbHVnaW5WZXJzaW9uID09PSAnc3RyaW5nJyAmJiBUT09MQ0hBSU5fUkVMRUFTRV9WRVJTSU9OX1BBVFRFUk4udGVzdChyLnBsdWdpblZlcnNpb24pKSB7XG4gICAgICByZWxlYXNlLnNldChyLnBpcGVsaW5lSWQsIHsgb3JnSWQ6IHIub3JnSWQsIHJlbGVhc2VWZXJzaW9uOiByLnBsdWdpblZlcnNpb24gfSk7XG4gICAgfVxuICAgIGlmIChyLnN0ZXBLZXkgPT09IFNZTlRIX1NURVBfS0VZKSBzeW50aC5zZXQoci5waXBlbGluZUlkLCB7IHN0YWdlTmFtZTogci5zdGFnZU5hbWUsIGFjdGlvbk5hbWU6IHIuYWN0aW9uTmFtZSB9KTtcbiAgfVxuICBjb25zdCBvdXQgPSBuZXcgTWFwPHN0cmluZywgRGVwbG95ZWRUb29sY2hhaW4+KCk7XG4gIGZvciAoY29uc3QgW3BpcGVsaW5lSWQsIHJdIG9mIHJlbGVhc2UpIG91dC5zZXQocGlwZWxpbmVJZCwgeyAuLi5yLCBzeW50aDogc3ludGguZ2V0KHBpcGVsaW5lSWQpID8/IG51bGwgfSk7XG4gIHJldHVybiBvdXQ7XG59XG5cbi8qKlxuICogV2hpY2ggdG9vbGNoYWluIGFjdGlvbiAoaWYgYW55KSBhbiBBQ1RJT04gZXZlbnQgaXMsIGdpdmVuIGl0cyBwaXBlbGluZSdzXG4gKiBkZXBsb3llZCB0b29sY2hhaW4uIE5hbWVzIGFyZSBjb21wYXJlZCBwb3N0LXNjcnViLCBhcyB0aGUgbWFuaWZlc3Qgc3RvcmVzIHRoZW0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0b29sY2hhaW5BY3Rpb25PZihcbiAgZXZlbnQ6IHsgZXZlbnRUeXBlOiBzdHJpbmc7IHN0YWdlTmFtZT86IHN0cmluZyB8IG51bGw7IGFjdGlvbk5hbWU/OiBzdHJpbmcgfCBudWxsIH0sXG4gIGRlcGxveWVkOiBEZXBsb3llZFRvb2xjaGFpbiB8IHVuZGVmaW5lZCxcbik6IFRvb2xjaGFpbkFjdGlvbiB8IG51bGwge1xuICBpZiAoZXZlbnQuZXZlbnRUeXBlICE9PSAnQUNUSU9OJyB8fCAhZGVwbG95ZWQgfHwgIWV2ZW50LnN0YWdlTmFtZSB8fCAhZXZlbnQuYWN0aW9uTmFtZSkgcmV0dXJuIG51bGw7XG4gIGlmIChldmVudC5zdGFnZU5hbWUgPT09IFNFTEZfTVVUQVRJT05fU1RBR0UgJiYgZXZlbnQuYWN0aW9uTmFtZSA9PT0gU0VMRl9NVVRBVElPTl9BQ1RJT04pIHJldHVybiAnc2VsZi1tdXRhdGlvbic7XG4gIGlmIChkZXBsb3llZC5zeW50aCAmJiBldmVudC5zdGFnZU5hbWUgPT09IGRlcGxveWVkLnN5bnRoLnN0YWdlTmFtZSAmJiBldmVudC5hY3Rpb25OYW1lID09PSBkZXBsb3llZC5zeW50aC5hY3Rpb25OYW1lKSByZXR1cm4gJ3N5bnRoJztcbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKiBUaGUgdGVybWluYWwgb3V0Y29tZSBvZiBhIHN0YXR1cywgb3IgbnVsbCBmb3IgYSBub24tdGVybWluYWwgLyBub24tanVkZ2luZyBvbmUuICovXG5leHBvcnQgZnVuY3Rpb24gdG9vbGNoYWluT3V0Y29tZU9mKHN0YXR1czogc3RyaW5nKTogVG9vbGNoYWluT3V0Y29tZSB8IG51bGwge1xuICBpZiAoc3RhdHVzID09PSAnU1VDQ0VFREVEJykgcmV0dXJuICdzdWNjZWVkZWQnO1xuICBpZiAoc3RhdHVzID09PSAnRkFJTEVEJykgcmV0dXJuICdmYWlsZWQnO1xuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqIFNRTDogYSBzdGFtcGVkIHJvdydzIGFjdGlvbiAoc3RhbXBlZCByb3dzIGFyZSBzeW50aCBvciBzZWxmLW11dGF0aW9uIG9ubHkpLiAqL1xuY29uc3QgSVNfU0VMRl9NVVRBVElPTiA9IHNxbGAoZS5zdGFnZV9uYW1lID0gJHtTRUxGX01VVEFUSU9OX1NUQUdFfSBBTkQgZS5hY3Rpb25fbmFtZSA9ICR7U0VMRl9NVVRBVElPTl9BQ1RJT059KWA7XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBDYW5hcnkgZ2F0ZSAowqc2KSDigJQgcHVyZSBjb21wdXRhdGlvblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLyoqIFRoZSBnYXRlJ3MgdGhyZXNob2xkcyAoc2V0IG9uIHRoZSBjb25zb2xlLCBQaGFzZSA1KS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ2FuYXJ5R2F0ZVBhcmFtcyB7XG4gIC8qKiBOOiBjb3VudGVkIHN5bnRoIHJ1bnMgcmVxdWlyZWQuICovXG4gIG1pblJ1bnM6IG51bWJlcjtcbiAgLyoqIE06IGRpc3RpbmN0IG9yZ3MgcmVxdWlyZWQuICovXG4gIG1pbk9yZ3M6IG51bWJlcjtcbiAgLyoqIFQ6IHRoZSByZWxlYXNlJ3MgZmFpbHVyZSByYXRlIG1heSBleGNlZWQgdGhlIGRlZmF1bHQncyBieSBhdCBtb3N0IHRoaXMgbWFueSBwZXJjZW50YWdlIHBvaW50cy4gKi9cbiAgbWF4RmFpbHVyZVBvaW50c0Fib3ZlRGVmYXVsdDogbnVtYmVyO1xufVxuXG5leHBvcnQgY29uc3QgREVGQVVMVF9DQU5BUllfR0FURTogUmVhZG9ubHk8Q2FuYXJ5R2F0ZVBhcmFtcz4gPSBPYmplY3QuZnJlZXplKHtcbiAgbWluUnVuczogMjAsXG4gIG1pbk9yZ3M6IDIsXG4gIG1heEZhaWx1cmVQb2ludHNBYm92ZURlZmF1bHQ6IDUsXG59KTtcblxuLyoqIE9uZSBvcmcncyB0ZXJtaW5hbCBzeW50aCBvdXRjb21lcyBvbiB0aGUgY2FuZGlkYXRlLCBORVdFU1QgZmlyc3QuICovXG5leHBvcnQgaW50ZXJmYWNlIE9yZ1J1bnMge1xuICBvcmdJZDogc3RyaW5nO1xuICBvdXRjb21lczogcmVhZG9ubHkgVG9vbGNoYWluT3V0Y29tZVtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENhbmFyeUdhdGVJbnB1dCB7XG4gIHJlbGVhc2VWZXJzaW9uOiBzdHJpbmc7XG4gIGNhbmRpZGF0ZTogcmVhZG9ubHkgT3JnUnVuc1tdO1xuICAvKiogVGhlIGN1cnJlbnQgcGxhdGZvcm0gZGVmYXVsdCdzIHN5bnRoIHJ1bnMgb3ZlciB0aGUgc2FtZSB3aW5kb3cgKHVuY2FwcGVkKS4gKi9cbiAgYmFzZWxpbmU6IHsgcmVsZWFzZVZlcnNpb246IHN0cmluZyB8IG51bGw7IHJ1bnM6IG51bWJlcjsgZmFpbHVyZXM6IG51bWJlciB9O1xuICBwYXJhbXM/OiBQYXJ0aWFsPENhbmFyeUdhdGVQYXJhbXM+O1xufVxuXG5leHBvcnQgdHlwZSBDYW5hcnlHYXRlU2hvcnRmYWxsID0gJ3J1bnMnIHwgJ29yZ3MnIHwgJ2ZhaWx1cmUtcmF0ZSc7XG5cbi8qKiBUaGUgZ2F0ZSdzIHZlcmRpY3QgYW5kIGV2ZXJ5IG51bWJlciBiZWhpbmQgaXQg4oCUIHdoYXQgdGhlIGNvbnNvbGUgc2hvd3MuICovXG5leHBvcnQgaW50ZXJmYWNlIENhbmFyeUdhdGVSZXN1bHQge1xuICBwYXNzZWQ6IGJvb2xlYW47XG4gIHJlbGVhc2VWZXJzaW9uOiBzdHJpbmc7XG4gIHBhcmFtczogQ2FuYXJ5R2F0ZVBhcmFtcztcbiAgLyoqIGNlaWwoTiAvIE0pOiB0aGUgbW9zdCBydW5zIGFueSBvbmUgb3JnIGNvbnRyaWJ1dGVzLiAqL1xuICBwZXJPcmdDYXA6IG51bWJlcjtcbiAgLyoqIFJ1bnMgY291bnRlZCB0b3dhcmQgTiwgYWZ0ZXIgdGhlIHBlci1vcmcgY2FwLiAqL1xuICBjb3VudGVkUnVuczogbnVtYmVyO1xuICAvKiogRXZlcnkgdGVybWluYWwgc3ludGggcnVuIG9uIHRoZSByZWxlYXNlLCBiZWZvcmUgdGhlIGNhcC4gKi9cbiAgdG90YWxSdW5zOiBudW1iZXI7XG4gIC8qKiBEaXN0aW5jdCBvcmdzIHdpdGggYXQgbGVhc3Qgb25lIGNvdW50ZWQgcnVuLiAqL1xuICBvcmdzOiBudW1iZXI7XG4gIC8qKiBGYWlsdXJlcyBhbW9uZyB0aGUgY291bnRlZCBydW5zLiAqL1xuICBmYWlsdXJlczogbnVtYmVyO1xuICAvKiogUGVyY2VudCAoMOKAkzEwMCkgb3ZlciB0aGUgY291bnRlZCBydW5zOyBudWxsIHdpdGggbm9uZS4gKi9cbiAgZmFpbHVyZVJhdGU6IG51bWJlciB8IG51bGw7XG4gIGJhc2VsaW5lUmVsZWFzZVZlcnNpb246IHN0cmluZyB8IG51bGw7XG4gIGJhc2VsaW5lUnVuczogbnVtYmVyO1xuICAvKiogUGVyY2VudCAoMOKAkzEwMCk7IDAgd2hlbiB0aGUgZGVmYXVsdCBoYXMgbm8gcnVucyBpbiB0aGUgd2luZG93ICh0aGUgc3RyaWN0IHJlYWRpbmcpLiAqL1xuICBiYXNlbGluZUZhaWx1cmVSYXRlOiBudW1iZXI7XG4gIC8qKiBmYWlsdXJlUmF0ZSDiiJIgYmFzZWxpbmVGYWlsdXJlUmF0ZSwgaW4gcG9pbnRzOyBudWxsIHdpdGggbm8gY291bnRlZCBydW5zLiAqL1xuICBwb2ludHNBYm92ZURlZmF1bHQ6IG51bWJlciB8IG51bGw7XG4gIC8qKiBXaGljaCB0aHJlc2hvbGRzIHdlcmUgbm90IG1ldCAoZW1wdHkgd2hlbiBwYXNzZWQpLiAqL1xuICBzaG9ydGZhbGxzOiBDYW5hcnlHYXRlU2hvcnRmYWxsW107XG59XG5cbi8qKiBSb3VuZCB0byAyIGRwLCBzbyB0aGUgbnVtYmVycyB0aGUgY29uc29sZSBzaG93cyBhcmUgc3RhYmxlLiAqL1xuZnVuY3Rpb24gcjIobjogbnVtYmVyKTogbnVtYmVyIHtcbiAgcmV0dXJuIE1hdGgucm91bmQobiAqIDEwMCkgLyAxMDA7XG59XG5cbmZ1bmN0aW9uIHJlc29sdmVQYXJhbXMocDogUGFydGlhbDxDYW5hcnlHYXRlUGFyYW1zPiB8IHVuZGVmaW5lZCk6IENhbmFyeUdhdGVQYXJhbXMge1xuICBjb25zdCBtZXJnZWQgPSB7IC4uLkRFRkFVTFRfQ0FOQVJZX0dBVEUsIC4uLihwID8/IHt9KSB9O1xuICBjb25zdCBtaW5SdW5zID0gTWF0aC5tYXgoMSwgTWF0aC5mbG9vcihtZXJnZWQubWluUnVucykpO1xuICBjb25zdCBtaW5PcmdzID0gTWF0aC5tYXgoMSwgTWF0aC5mbG9vcihtZXJnZWQubWluT3JncykpO1xuICBjb25zdCBtYXhGYWlsdXJlUG9pbnRzQWJvdmVEZWZhdWx0ID0gTWF0aC5tYXgoMCwgbWVyZ2VkLm1heEZhaWx1cmVQb2ludHNBYm92ZURlZmF1bHQpO1xuICBpZiAoIVttaW5SdW5zLCBtaW5PcmdzLCBtYXhGYWlsdXJlUG9pbnRzQWJvdmVEZWZhdWx0XS5ldmVyeShOdW1iZXIuaXNGaW5pdGUpKSByZXR1cm4geyAuLi5ERUZBVUxUX0NBTkFSWV9HQVRFIH07XG4gIHJldHVybiB7IG1pblJ1bnMsIG1pbk9yZ3MsIG1heEZhaWx1cmVQb2ludHNBYm92ZURlZmF1bHQgfTtcbn1cblxuLyoqIFRoZSBwZXItb3JnIGNvbnRyaWJ1dGlvbiBjYXA6IGNlaWwoTiAvIE0pLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbmFyeVBlck9yZ0NhcChwYXJhbXM6IFBpY2s8Q2FuYXJ5R2F0ZVBhcmFtcywgJ21pblJ1bnMnIHwgJ21pbk9yZ3MnPik6IG51bWJlciB7XG4gIHJldHVybiBNYXRoLmNlaWwocGFyYW1zLm1pblJ1bnMgLyBwYXJhbXMubWluT3Jncyk7XG59XG5cbi8qKlxuICogVGhlIGNhbmFyeSBnYXRlOiBhdCBsZWFzdCBOIGNvdW50ZWQgc3ludGggcnVucyBhY3Jvc3MgYXQgbGVhc3QgTSBvcmdzLCBhbmQgYVxuICogZmFpbHVyZSByYXRlIHdpdGhpbiBUIHBvaW50cyBvZiB0aGUgY3VycmVudCBkZWZhdWx0J3MuIEVhY2ggb3JnIGNvdW50cyBpdHNcbiAqIG1vc3QgcmVjZW50IGBjZWlsKE4gLyBNKWAgcnVucyBvbmx5IOKAlCB0b3dhcmQgTiBBTkQgdG93YXJkIHRoZSBmYWlsdXJlIHJhdGUsXG4gKiBzbyBvbmUgb3JnIGNhbiBuZWl0aGVyIHNhdGlzZnkgTiBhbG9uZSBub3IgZG9taW5hdGUgdGhlIHJhdGUuIEJlaW5nIEJFVFRFUlxuICogdGhhbiB0aGUgZGVmYXVsdCBuZXZlciBmYWlscyB0aGUgZ2F0ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV2YWx1YXRlQ2FuYXJ5R2F0ZShpbnB1dDogQ2FuYXJ5R2F0ZUlucHV0KTogQ2FuYXJ5R2F0ZVJlc3VsdCB7XG4gIGNvbnN0IHBhcmFtcyA9IHJlc29sdmVQYXJhbXMoaW5wdXQucGFyYW1zKTtcbiAgY29uc3QgcGVyT3JnQ2FwID0gY2FuYXJ5UGVyT3JnQ2FwKHBhcmFtcyk7XG4gIGxldCBjb3VudGVkUnVucyA9IDA7XG4gIGxldCBmYWlsdXJlcyA9IDA7XG4gIGxldCB0b3RhbFJ1bnMgPSAwO1xuICBsZXQgb3JncyA9IDA7XG4gIGZvciAoY29uc3Qgb3JnIG9mIGlucHV0LmNhbmRpZGF0ZSkge1xuICAgIHRvdGFsUnVucyArPSBvcmcub3V0Y29tZXMubGVuZ3RoO1xuICAgIGNvbnN0IGNvdW50ZWQgPSBvcmcub3V0Y29tZXMuc2xpY2UoMCwgcGVyT3JnQ2FwKTtcbiAgICBpZiAoY291bnRlZC5sZW5ndGggPT09IDApIGNvbnRpbnVlO1xuICAgIG9yZ3MrKztcbiAgICBjb3VudGVkUnVucyArPSBjb3VudGVkLmxlbmd0aDtcbiAgICBmYWlsdXJlcyArPSBjb3VudGVkLmZpbHRlcigobykgPT4gbyA9PT0gJ2ZhaWxlZCcpLmxlbmd0aDtcbiAgfVxuICBjb25zdCBmYWlsdXJlUmF0ZSA9IGNvdW50ZWRSdW5zID4gMCA/IHIyKChmYWlsdXJlcyAvIGNvdW50ZWRSdW5zKSAqIDEwMCkgOiBudWxsO1xuICBjb25zdCBiYXNlbGluZUZhaWx1cmVSYXRlID0gaW5wdXQuYmFzZWxpbmUucnVucyA+IDAgPyByMigoaW5wdXQuYmFzZWxpbmUuZmFpbHVyZXMgLyBpbnB1dC5iYXNlbGluZS5ydW5zKSAqIDEwMCkgOiAwO1xuICBjb25zdCBwb2ludHNBYm92ZURlZmF1bHQgPSBmYWlsdXJlUmF0ZSA9PT0gbnVsbCA/IG51bGwgOiByMihmYWlsdXJlUmF0ZSAtIGJhc2VsaW5lRmFpbHVyZVJhdGUpO1xuXG4gIGNvbnN0IHNob3J0ZmFsbHM6IENhbmFyeUdhdGVTaG9ydGZhbGxbXSA9IFtdO1xuICBpZiAoY291bnRlZFJ1bnMgPCBwYXJhbXMubWluUnVucykgc2hvcnRmYWxscy5wdXNoKCdydW5zJyk7XG4gIGlmIChvcmdzIDwgcGFyYW1zLm1pbk9yZ3MpIHNob3J0ZmFsbHMucHVzaCgnb3JncycpO1xuICBpZiAocG9pbnRzQWJvdmVEZWZhdWx0ID09PSBudWxsIHx8IHBvaW50c0Fib3ZlRGVmYXVsdCA+IHBhcmFtcy5tYXhGYWlsdXJlUG9pbnRzQWJvdmVEZWZhdWx0KSBzaG9ydGZhbGxzLnB1c2goJ2ZhaWx1cmUtcmF0ZScpO1xuXG4gIHJldHVybiB7XG4gICAgcGFzc2VkOiBzaG9ydGZhbGxzLmxlbmd0aCA9PT0gMCxcbiAgICByZWxlYXNlVmVyc2lvbjogaW5wdXQucmVsZWFzZVZlcnNpb24sXG4gICAgcGFyYW1zLFxuICAgIHBlck9yZ0NhcCxcbiAgICBjb3VudGVkUnVucyxcbiAgICB0b3RhbFJ1bnMsXG4gICAgb3JncyxcbiAgICBmYWlsdXJlcyxcbiAgICBmYWlsdXJlUmF0ZSxcbiAgICBiYXNlbGluZVJlbGVhc2VWZXJzaW9uOiBpbnB1dC5iYXNlbGluZS5yZWxlYXNlVmVyc2lvbixcbiAgICBiYXNlbGluZVJ1bnM6IGlucHV0LmJhc2VsaW5lLnJ1bnMsXG4gICAgYmFzZWxpbmVGYWlsdXJlUmF0ZSxcbiAgICBwb2ludHNBYm92ZURlZmF1bHQsXG4gICAgc2hvcnRmYWxscyxcbiAgfTtcbn1cblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIENhbmFyeSBnYXRlIOKAlCB0aGUgcmVhZFxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLyoqXG4gKiBFdmFsdWF0ZSB0aGUgY2FuYXJ5IGdhdGUgZm9yIGByZWxlYXNlVmVyc2lvbmAgYWdhaW5zdCBgYmFzZWxpbmVSZWxlYXNlVmVyc2lvbmBcbiAqICh0aGUgY3VycmVudCBwbGF0Zm9ybSBkZWZhdWx0OyBudWxsIHdoZW4gbm9uZSkgZnJvbSB0aGUgc3RhbXBlZCByb3dzLlxuICpcbiAqIFdpbmRvdzogc2luY2UgdGhlIHJlbGVhc2Ugd2FzIHJlZ2lzdGVyZWQgKGB0b29sY2hhaW5fcmVsZWFzZS5jcmVhdGVkX2F0YCksXG4gKiBmb3IgYm90aCB0aGUgY2FuZGlkYXRlIGFuZCB0aGUgZGVmYXVsdCwgc28gdGhlIHR3byByYXRlcyBhcmUgY29udGVtcG9yYW5lb3VzLlxuICogT25seSB0ZXJtaW5hbCAoU1VDQ0VFREVEIC8gRkFJTEVEKSBTWU5USCBydW5zIGNvdW50OyBzZWxmLW11dGF0aW9uIGlzIG5vdFxuICogcGFydCBvZiB0aGUgZ2F0ZSAowqc2IGNvdW50cyBzeW50aCBydW5zKS5cbiAqXG4gKiBSZWFkcyBldmVyeSBvcmcncyByb3dzOiBydW4gaXQgRUxFVkFURUQgKHRoZSBwbHVnaW4gc2VydmljZSdzIHRvb2xjaGFpbiB0eFxuICogaXMpLiBUaGUgY2FuZGlkYXRlIHJlYWQgaXMgYm91bmRlZCBhdCBgY2VpbChOL00pYCByb3dzIHBlciBvcmcuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkQ2FuYXJ5R2F0ZShcbiAgZGI6IFNxbEV4ZWN1dG9yLFxuICByZWxlYXNlVmVyc2lvbjogc3RyaW5nLFxuICBiYXNlbGluZVJlbGVhc2VWZXJzaW9uOiBzdHJpbmcgfCBudWxsLFxuICBwYXJhbXM/OiBQYXJ0aWFsPENhbmFyeUdhdGVQYXJhbXM+LFxuKTogUHJvbWlzZTxDYW5hcnlHYXRlUmVzdWx0PiB7XG4gIGNvbnN0IHJlc29sdmVkID0gcmVzb2x2ZVBhcmFtcyhwYXJhbXMpO1xuICBjb25zdCBjYXAgPSBjYW5hcnlQZXJPcmdDYXAocmVzb2x2ZWQpO1xuICBjb25zdCBzaW5jZSA9IHNxbGAoU0VMRUNUIGNyZWF0ZWRfYXQgRlJPTSB0b29sY2hhaW5fcmVsZWFzZSBXSEVSRSByZWxlYXNlX3ZlcnNpb24gPSAke3JlbGVhc2VWZXJzaW9ufSlgO1xuICBjb25zdCBjYW5kaWRhdGUgPSBhd2FpdCBleGVjdXRlUm93czx7IG9yZ19pZDogc3RyaW5nOyBzdGF0dXM6IHN0cmluZzsgdG90YWw6IHN0cmluZyB8IG51bWJlciB9PihkYiwgc3FsYFxuICAgIFNFTEVDVCBvcmdfaWQsIHN0YXR1cywgdG90YWwgRlJPTSAoXG4gICAgICBTRUxFQ1QgZS5vcmdfaWQsIGUuc3RhdHVzLFxuICAgICAgICAgICAgIHJvd19udW1iZXIoKSBPVkVSIChQQVJUSVRJT04gQlkgZS5vcmdfaWQgT1JERVIgQlkgY29hbGVzY2UoZS5jb21wbGV0ZWRfYXQsIGUuY3JlYXRlZF9hdCkgREVTQywgZS5pZCkgQVMgcm4sXG4gICAgICAgICAgICAgY291bnQoKikgT1ZFUiAoUEFSVElUSU9OIEJZIGUub3JnX2lkKSBBUyB0b3RhbFxuICAgICAgICBGUk9NIHBpcGVsaW5lX2V2ZW50cyBlXG4gICAgICAgV0hFUkUgZS50b29sY2hhaW5fcmVsZWFzZSA9ICR7cmVsZWFzZVZlcnNpb259XG4gICAgICAgICBBTkQgZS5ldmVudF90eXBlID0gJ0FDVElPTicgQU5EIGUuc3RhdHVzIElOICgnU1VDQ0VFREVEJywgJ0ZBSUxFRCcpXG4gICAgICAgICBBTkQgTk9UICR7SVNfU0VMRl9NVVRBVElPTn1cbiAgICAgICAgIEFORCBlLmNyZWF0ZWRfYXQgPj0gY29hbGVzY2UoJHtzaW5jZX0sICctaW5maW5pdHknOjp0aW1lc3RhbXB0eilcbiAgICApIHhcbiAgICBXSEVSRSBybiA8PSAke2NhcH1cbiAgICBPUkRFUiBCWSBvcmdfaWQsIHJuXG4gIGApO1xuICBjb25zdCBieU9yZyA9IG5ldyBNYXA8c3RyaW5nLCB7IG91dGNvbWVzOiBUb29sY2hhaW5PdXRjb21lW107IHRvdGFsOiBudW1iZXIgfT4oKTtcbiAgZm9yIChjb25zdCByb3cgb2YgY2FuZGlkYXRlKSB7XG4gICAgY29uc3QgZW50cnkgPSBieU9yZy5nZXQocm93Lm9yZ19pZCkgPz8geyBvdXRjb21lczogW10sIHRvdGFsOiBOdW1iZXIocm93LnRvdGFsKSB9O1xuICAgIGVudHJ5Lm91dGNvbWVzLnB1c2gocm93LnN0YXR1cyA9PT0gJ0ZBSUxFRCcgPyAnZmFpbGVkJyA6ICdzdWNjZWVkZWQnKTtcbiAgICBieU9yZy5zZXQocm93Lm9yZ19pZCwgZW50cnkpO1xuICB9XG5cbiAgbGV0IGJhc2VsaW5lID0geyByZWxlYXNlVmVyc2lvbjogYmFzZWxpbmVSZWxlYXNlVmVyc2lvbiwgcnVuczogMCwgZmFpbHVyZXM6IDAgfTtcbiAgaWYgKGJhc2VsaW5lUmVsZWFzZVZlcnNpb24gIT09IG51bGwgJiYgYmFzZWxpbmVSZWxlYXNlVmVyc2lvbiAhPT0gcmVsZWFzZVZlcnNpb24pIHtcbiAgICBjb25zdCBbYl0gPSBhd2FpdCBleGVjdXRlUm93czx7IHJ1bnM6IHN0cmluZyB8IG51bWJlcjsgZmFpbHVyZXM6IHN0cmluZyB8IG51bWJlciB9PihkYiwgc3FsYFxuICAgICAgU0VMRUNUIGNvdW50KCopIEFTIHJ1bnMsIGNvdW50KCopIEZJTFRFUiAoV0hFUkUgZS5zdGF0dXMgPSAnRkFJTEVEJykgQVMgZmFpbHVyZXNcbiAgICAgICAgRlJPTSBwaXBlbGluZV9ldmVudHMgZVxuICAgICAgIFdIRVJFIGUudG9vbGNoYWluX3JlbGVhc2UgPSAke2Jhc2VsaW5lUmVsZWFzZVZlcnNpb259XG4gICAgICAgICBBTkQgZS5ldmVudF90eXBlID0gJ0FDVElPTicgQU5EIGUuc3RhdHVzIElOICgnU1VDQ0VFREVEJywgJ0ZBSUxFRCcpXG4gICAgICAgICBBTkQgTk9UICR7SVNfU0VMRl9NVVRBVElPTn1cbiAgICAgICAgIEFORCBlLmNyZWF0ZWRfYXQgPj0gY29hbGVzY2UoJHtzaW5jZX0sICctaW5maW5pdHknOjp0aW1lc3RhbXB0eilcbiAgICBgKTtcbiAgICBiYXNlbGluZSA9IHsgcmVsZWFzZVZlcnNpb246IGJhc2VsaW5lUmVsZWFzZVZlcnNpb24sIHJ1bnM6IE51bWJlcihiPy5ydW5zID8/IDApLCBmYWlsdXJlczogTnVtYmVyKGI/LmZhaWx1cmVzID8/IDApIH07XG4gIH1cblxuICBjb25zdCByZXN1bHQgPSBldmFsdWF0ZUNhbmFyeUdhdGUoe1xuICAgIHJlbGVhc2VWZXJzaW9uLFxuICAgIGNhbmRpZGF0ZTogWy4uLmJ5T3JnLmVudHJpZXMoKV0ubWFwKChbb3JnSWQsIGVdKSA9PiAoeyBvcmdJZCwgb3V0Y29tZXM6IGUub3V0Y29tZXMgfSkpLFxuICAgIGJhc2VsaW5lLFxuICAgIHBhcmFtczogcmVzb2x2ZWQsXG4gIH0pO1xuICAvLyBUaGUgcmVhZCBpcyBhbHJlYWR5IGNhcHBlZCBwZXIgb3JnLCBzbyB0aGUgcHJlLWNhcCB0b3RhbCBpcyB0aGUgd2luZG93IGNvdW50LlxuICByZXR1cm4geyAuLi5yZXN1bHQsIHRvdGFsUnVuczogWy4uLmJ5T3JnLnZhbHVlcygpXS5yZWR1Y2UoKG4sIGUpID0+IG4gKyBlLnRvdGFsLCAwKSB9O1xufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gQWZmZWN0ZWQgdmlldyAowqc5KVxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuZXhwb3J0IGludGVyZmFjZSBBZmZlY3RlZE9yZyB7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIC8qKiBQaXBlbGluZXMgaW4gdGhpcyBvcmcgb24gdGhlIHJlbGVhc2Ugd2hvc2UgbGF0ZXN0IHN5bnRoIGZhaWxlZC4gKi9cbiAgcGlwZWxpbmVzOiBudW1iZXI7XG4gIHBpcGVsaW5lSWRzOiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBBZmZlY3RlZFBpcGVsaW5lcyB7XG4gIHJlbGVhc2VWZXJzaW9uOiBzdHJpbmc7XG4gIHRvdGFsUGlwZWxpbmVzOiBudW1iZXI7XG4gIG9yZ3M6IEFmZmVjdGVkT3JnW107XG59XG5cbi8qKlxuICogUGlwZWxpbmVzIHdob3NlIERFUExPWUVEIHJlbGVhc2UgaXMgYHJlbGVhc2VWZXJzaW9uYCAodGhlaXIgbWFuaWZlc3Qnc1xuICogYGJ1aWxkLWltYWdlYCBlbnRyeSkgYW5kIHdob3NlIExBVEVTVCB0ZXJtaW5hbCBzeW50aCBydW4gZmFpbGVkLCBncm91cGVkIGJ5XG4gKiBvcmcg4oCUIHRoZSBjb25zb2xlJ3MgQWZmZWN0ZWQgdmlldyBhbmQgdGhlIFwiTm90aWZ5IGFmZmVjdGVkIG93bmVyc1wiIHRhcmdldFxuICogc2V0LiBBIHBpcGVsaW5lIHdob3NlIGxhdGVzdCBzeW50aCBzdWNjZWVkZWQgaGFzIGhlYWxlZCBhbmQgaXMgbm90IGxpc3RlZC5cbiAqXG4gKiBFbGV2YXRlZCByZWFkOiBpdCBzcGFucyBldmVyeSBvcmcuIENhbGxlcnMgc2hvdyB0aGUgc3lzdGVtLW9yZyBvcGVyYXRvclxuICogY291bnRzLCBhbmQgbm90aWZ5IGVhY2ggb3JnIGFib3V0IGl0cyBvd24gcGlwZWxpbmVzIG9ubHkuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkQWZmZWN0ZWRQaXBlbGluZXMoZGI6IFNxbEV4ZWN1dG9yLCByZWxlYXNlVmVyc2lvbjogc3RyaW5nKTogUHJvbWlzZTxBZmZlY3RlZFBpcGVsaW5lcz4ge1xuICBjb25zdCByb3dzID0gYXdhaXQgZXhlY3V0ZVJvd3M8eyBvcmdfaWQ6IHN0cmluZzsgcGlwZWxpbmVfaWQ6IHN0cmluZyB9PihkYiwgc3FsYFxuICAgIFNFTEVDVCBtLm9yZ19pZCwgbS5waXBlbGluZV9pZDo6dGV4dCBBUyBwaXBlbGluZV9pZFxuICAgICAgRlJPTSBwaXBlbGluZV9zdGVwX21hbmlmZXN0cyBtXG4gICAgICBKT0lOIExBVEVSQUwgKFxuICAgICAgICBTRUxFQ1QgZS5zdGF0dXNcbiAgICAgICAgICBGUk9NIHBpcGVsaW5lX2V2ZW50cyBlXG4gICAgICAgICBXSEVSRSBlLnBpcGVsaW5lX2lkID0gbS5waXBlbGluZV9pZFxuICAgICAgICAgICBBTkQgZS50b29sY2hhaW5fcmVsZWFzZSBJUyBOT1QgTlVMTFxuICAgICAgICAgICBBTkQgZS5ldmVudF90eXBlID0gJ0FDVElPTicgQU5EIGUuc3RhdHVzIElOICgnU1VDQ0VFREVEJywgJ0ZBSUxFRCcpXG4gICAgICAgICAgIEFORCBOT1QgJHtJU19TRUxGX01VVEFUSU9OfVxuICAgICAgICAgT1JERVIgQlkgY29hbGVzY2UoZS5jb21wbGV0ZWRfYXQsIGUuY3JlYXRlZF9hdCkgREVTQywgZS5jcmVhdGVkX2F0IERFU0NcbiAgICAgICAgIExJTUlUIDFcbiAgICAgICkgbGF0ZXN0IE9OIGxhdGVzdC5zdGF0dXMgPSAnRkFJTEVEJ1xuICAgICBXSEVSRSBtLnN0ZXBfa2V5ID0gJHtCVUlMRF9JTUFHRV9TVEVQX0tFWX0gQU5EIG0ucGx1Z2luX25hbWUgPSAke0JVSUxEX0lNQUdFX1BMVUdJTn0gQU5EIG0ucGx1Z2luX3B1Ymxpc2hlciBJUyBOVUxMXG4gICAgICAgQU5EIG0ucGx1Z2luX3ZlcnNpb24gPSAke3JlbGVhc2VWZXJzaW9ufVxuICAgICBPUkRFUiBCWSBtLm9yZ19pZCwgbS5waXBlbGluZV9pZFxuICBgKTtcbiAgY29uc3QgYnlPcmcgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nW10+KCk7XG4gIGZvciAoY29uc3QgciBvZiByb3dzKSB7XG4gICAgY29uc3QgaWRzID0gYnlPcmcuZ2V0KHIub3JnX2lkKSA/PyBbXTtcbiAgICBpZiAoIWlkcy5pbmNsdWRlcyhyLnBpcGVsaW5lX2lkKSkgaWRzLnB1c2goci5waXBlbGluZV9pZCk7XG4gICAgYnlPcmcuc2V0KHIub3JnX2lkLCBpZHMpO1xuICB9XG4gIGNvbnN0IG9yZ3MgPSBbLi4uYnlPcmcuZW50cmllcygpXS5tYXAoKFtvcmdJZCwgcGlwZWxpbmVJZHNdKSA9PiAoeyBvcmdJZCwgcGlwZWxpbmVzOiBwaXBlbGluZUlkcy5sZW5ndGgsIHBpcGVsaW5lSWRzIH0pKTtcbiAgcmV0dXJuIHsgcmVsZWFzZVZlcnNpb24sIHRvdGFsUGlwZWxpbmVzOiBvcmdzLnJlZHVjZSgobiwgbykgPT4gbiArIG8ucGlwZWxpbmVzLCAwKSwgb3JncyB9O1xufVxuXG4vKiogVGhlIGN1cnJlbnQgcGxhdGZvcm0gZGVmYXVsdCAoZWxldmF0ZWQgcmVhZCksIG9yIG51bGwuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZFBsYXRmb3JtRGVmYXVsdFJlbGVhc2UoZGI6IFNxbEV4ZWN1dG9yKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gIGNvbnN0IFtyb3ddID0gYXdhaXQgZXhlY3V0ZVJvd3M8eyByZWxlYXNlX3ZlcnNpb246IHN0cmluZyB9PihkYiwgc3FsYFxuICAgIFNFTEVDVCByZWxlYXNlX3ZlcnNpb24gRlJPTSB0b29sY2hhaW5fcGxhdGZvcm1fZGVmYXVsdCBXSEVSRSBzaW5nbGV0b24gPSB0cnVlXG4gIGApO1xuICByZXR1cm4gcm93Py5yZWxlYXNlX3ZlcnNpb24gPz8gbnVsbDtcbn1cbiJdfQ==
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* with no behaviour. reporting-service.ts re-exports them, so the
|
|
6
6
|
* `@pipeline-builder/pipeline-data` public surface is unchanged.
|
|
7
7
|
*/
|
|
8
|
+
import type { WeekStart } from '@pipeline-builder/api-core';
|
|
8
9
|
export interface ExecutionCount {
|
|
9
10
|
id: string;
|
|
10
11
|
project: string;
|
|
@@ -165,7 +166,7 @@ export interface BucketOptions {
|
|
|
165
166
|
tz?: string;
|
|
166
167
|
/** Where the week starts. Postgres DATE_TRUNC is Monday-only, so `sunday`
|
|
167
168
|
* shifts the boundary explicitly. */
|
|
168
|
-
weekStart?:
|
|
169
|
+
weekStart?: WeekStart;
|
|
169
170
|
}
|
|
170
171
|
export interface ReportingRetentionOptions {
|
|
171
172
|
/** Rows deleted per statement (default 1000). */
|
|
@@ -230,7 +231,7 @@ export interface DoraOptions {
|
|
|
230
231
|
/** IANA timezone for the trend's buckets (see {@link BucketOptions}). */
|
|
231
232
|
tz?: string;
|
|
232
233
|
/** Week start for the trend's buckets. */
|
|
233
|
-
weekStart?:
|
|
234
|
+
weekStart?: WeekStart;
|
|
234
235
|
}
|
|
235
236
|
/**
|
|
236
237
|
* Per-org reporting settings surfaced to the org-admin panel. `incidentWindowHours`
|
|
@@ -540,9 +541,25 @@ export interface ComputeIngestMetric {
|
|
|
540
541
|
durationMs: number | null;
|
|
541
542
|
credits: number | null;
|
|
542
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* One terminal managed-toolchain run (docs/plans/bootstrap-image-versioning.md
|
|
546
|
+
* §7): a `synth` or `self-mutation` action whose row was stamped with the
|
|
547
|
+
* release from the platform's deployed manifest. ALERTING ONLY — the canary
|
|
548
|
+
* gate and the Affected view read the rows, never this metric.
|
|
549
|
+
*
|
|
550
|
+
* `platformDefaultRelease` is the default at ingest time, for the alert's
|
|
551
|
+
* baseline (`pipeline_toolchain_platform_default_seen_timestamp_seconds`).
|
|
552
|
+
*/
|
|
553
|
+
export interface ToolchainIngestMetric {
|
|
554
|
+
kind: 'toolchain';
|
|
555
|
+
toolchainRelease: string;
|
|
556
|
+
action: 'synth' | 'self-mutation';
|
|
557
|
+
outcome: 'succeeded' | 'failed';
|
|
558
|
+
platformDefaultRelease: string | null;
|
|
559
|
+
}
|
|
543
560
|
/** What the ingest fans out to its caller's metric registry. Discriminated, so
|
|
544
561
|
* a new kind cannot be silently counted as a stage outcome. */
|
|
545
|
-
export type IngestMetric = StageIngestMetric | ComputeIngestMetric;
|
|
562
|
+
export type IngestMetric = StageIngestMetric | ComputeIngestMetric | ToolchainIngestMetric;
|
|
546
563
|
/** Counts + the (possibly truncated) list of unregistered pipeline ids the caller can log. */
|
|
547
564
|
export interface IngestResult {
|
|
548
565
|
inserted: number;
|