@norskvideo/ctl-dev-kit 0.1.87 → 0.1.88
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.
|
@@ -55,12 +55,31 @@ on:
|
|
|
55
55
|
permissions:
|
|
56
56
|
contents: read
|
|
57
57
|
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
58
|
+
# A ctl-candidate validation must not be cancellable out of a result, because
|
|
59
|
+
# release-collect scores anything other than a literal success as red — a
|
|
60
|
+
# CANCELLED run included — and answers a red with one re-dispatch.
|
|
61
|
+
#
|
|
62
|
+
# Keying on event_name alone was not enough: every candidate is the SAME event,
|
|
63
|
+
# so they shared a group and cancelled each other, and the re-dispatch cancelled
|
|
64
|
+
# the very run it was asking for. On 2026-09-05 commentary was dispatched four
|
|
65
|
+
# times in thirteen minutes, every run cancelled before it could start on the
|
|
66
|
+
# backlogged pool, and rc-gate/0.1.0-2026-09-05-98e09d1/FAILED was written for a
|
|
67
|
+
# candidate on which nothing had actually failed — while playout, studio and
|
|
68
|
+
# reuters, fast enough to finish inside the window, all went green.
|
|
69
|
+
#
|
|
70
|
+
# So: separate the candidates (client_payload.version — empty on push/PR, which
|
|
71
|
+
# leaves those groups exactly as they were), and do not cancel a dispatch run.
|
|
72
|
+
# A superseded PENDING run is dropped before it starts, fires no
|
|
73
|
+
# report-candidate, and scores nothing — which is why queueing is safe here
|
|
74
|
+
# where cancelling is not. Pushes still cancel pushes.
|
|
75
|
+
#
|
|
76
|
+
# Trade-off, stated: two candidates' validations can now overlap in one repo,
|
|
77
|
+
# and their tiers derive host ports from the same per-slug bands. The bands were
|
|
78
|
+
# moved out of the kernel's ephemeral range (ctl-test-harness 0.1.42); an
|
|
79
|
+
# ephemeral daemon port is the durable answer and is not wired yet.
|
|
61
80
|
concurrency:
|
|
62
|
-
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
|
63
|
-
cancel-in-progress:
|
|
81
|
+
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}-${{ github.event.client_payload.version }}
|
|
82
|
+
cancel-in-progress: ${{ github.event_name != 'repository_dispatch' }}
|
|
64
83
|
|
|
65
84
|
jobs:
|
|
66
85
|
integration:
|
|
@@ -161,3 +161,37 @@ export function jobsMissingTestTempStep(yaml: string): string[] {
|
|
|
161
161
|
settle();
|
|
162
162
|
return bad;
|
|
163
163
|
}
|
|
164
|
+
|
|
165
|
+
/** A `ctl-candidate` validation that another candidate, or its own retry, can
|
|
166
|
+
* cancel.
|
|
167
|
+
*
|
|
168
|
+
* release-collect scores anything other than a literal success as red — a
|
|
169
|
+
* CANCELLED validation included — and answers a red with one re-dispatch. So a
|
|
170
|
+
* concurrency group shared across candidates makes the gate eat itself:
|
|
171
|
+
* candidate B's dispatch cancels A's in-flight run, A scores red, A retries,
|
|
172
|
+
* and that retry cancels B's run. Observed 2026-09-05: commentary dispatched
|
|
173
|
+
* four times in thirteen minutes, every run cancelled, and
|
|
174
|
+
* `rc-gate/0.1.0-2026-09-05-98e09d1/FAILED` written for a candidate on which
|
|
175
|
+
* nothing had actually failed.
|
|
176
|
+
*
|
|
177
|
+
* Both halves are needed. The group must SEPARATE candidates
|
|
178
|
+
* (`client_payload.version`), so a different candidate never shares one; and a
|
|
179
|
+
* dispatch run must not be cancellable, so a retry queues behind the run it is
|
|
180
|
+
* asking for rather than killing it. A superseded PENDING run is dropped
|
|
181
|
+
* before it starts, so it fires no `report-candidate` and scores nothing —
|
|
182
|
+
* which is why queueing is safe where cancelling is not. */
|
|
183
|
+
export function candidateConcurrencyProblems(yaml: string): string[] {
|
|
184
|
+
const block = yaml.match(/^concurrency:\n((?:[ \t]+.*\n)+)/m)?.[1] ?? "";
|
|
185
|
+
const group = block.match(/^\s*group:\s*(.*)$/m)?.[1] ?? "";
|
|
186
|
+
const cancel = (block.match(/^\s*cancel-in-progress:\s*(.*)$/m)?.[1] ?? "").trim();
|
|
187
|
+
const problems: string[] = [];
|
|
188
|
+
if (!/client_payload\.version/.test(group)) {
|
|
189
|
+
problems.push(
|
|
190
|
+
"group does not separate candidates: a newer candidate cancels an older validation, which scores red",
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (cancel === "true") {
|
|
194
|
+
problems.push("cancel-in-progress is unconditionally true: the re-dispatch cancels the run it is asking for");
|
|
195
|
+
}
|
|
196
|
+
return problems;
|
|
197
|
+
}
|