@cleocode/core 2026.10.17 → 2026.10.18-rc.ci.688
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/audit-prune.d.ts +37 -8
- package/dist/audit-prune.d.ts.map +1 -1
- package/dist/audit-prune.js +46 -12
- package/dist/audit-prune.js.map +1 -1
- package/dist/build-provenance.json +2 -2
- package/dist/cloud/manifest-check.d.ts +21 -0
- package/dist/cloud/manifest-check.d.ts.map +1 -1
- package/dist/cloud/manifest-check.js +46 -1
- package/dist/cloud/manifest-check.js.map +1 -1
- package/dist/cloud/nexus-vault.d.ts +19 -0
- package/dist/cloud/nexus-vault.d.ts.map +1 -1
- package/dist/cloud/nexus-vault.js +134 -23
- package/dist/cloud/nexus-vault.js.map +1 -1
- package/dist/cloud/signing.d.ts.map +1 -1
- package/dist/cloud/signing.js +4 -0
- package/dist/cloud/signing.js.map +1 -1
- package/dist/doctor/row-identity.d.ts.map +1 -1
- package/dist/doctor/row-identity.js +2 -1
- package/dist/doctor/row-identity.js.map +1 -1
- package/dist/git/work-tree.d.ts +17 -0
- package/dist/git/work-tree.d.ts.map +1 -1
- package/dist/git/work-tree.js +30 -0
- package/dist/git/work-tree.js.map +1 -1
- package/dist/goal/close.d.ts +95 -0
- package/dist/goal/close.d.ts.map +1 -0
- package/dist/goal/close.js +497 -0
- package/dist/goal/close.js.map +1 -0
- package/dist/goal/draft.d.ts +81 -0
- package/dist/goal/draft.d.ts.map +1 -0
- package/dist/goal/draft.js +331 -0
- package/dist/goal/draft.js.map +1 -0
- package/dist/hooks/release-gate.d.ts +3 -0
- package/dist/hooks/release-gate.d.ts.map +1 -1
- package/dist/hooks/release-gate.js +4 -1
- package/dist/hooks/release-gate.js.map +1 -1
- package/dist/orchestration/lean-gain.d.ts +26 -0
- package/dist/orchestration/lean-gain.d.ts.map +1 -0
- package/dist/orchestration/lean-gain.js +61 -0
- package/dist/orchestration/lean-gain.js.map +1 -0
- package/dist/release/backfill.d.ts.map +1 -1
- package/dist/release/backfill.js +15 -5
- package/dist/release/backfill.js.map +1 -1
- package/dist/release/ci-evidence.d.ts +90 -5
- package/dist/release/ci-evidence.d.ts.map +1 -1
- package/dist/release/ci-evidence.js +277 -4
- package/dist/release/ci-evidence.js.map +1 -1
- package/dist/release/plan.d.ts +4 -1
- package/dist/release/plan.d.ts.map +1 -1
- package/dist/release/plan.js +10 -2
- package/dist/release/plan.js.map +1 -1
- package/dist/release/reconcile.d.ts +10 -0
- package/dist/release/reconcile.d.ts.map +1 -1
- package/dist/release/reconcile.js +42 -19
- package/dist/release/reconcile.js.map +1 -1
- package/dist/release/release-manifest.d.ts +2 -1
- package/dist/release/release-manifest.d.ts.map +1 -1
- package/dist/release/release-manifest.js +15 -1
- package/dist/release/release-manifest.js.map +1 -1
- package/dist/resources/admission-ledger.d.ts +21 -4
- package/dist/resources/admission-ledger.d.ts.map +1 -1
- package/dist/resources/admission-ledger.js +102 -19
- package/dist/resources/admission-ledger.js.map +1 -1
- package/dist/resources/run-class.d.ts +22 -0
- package/dist/resources/run-class.d.ts.map +1 -1
- package/dist/resources/run-class.js +81 -0
- package/dist/resources/run-class.js.map +1 -1
- package/dist/sessions/handoff.d.ts +2 -2
- package/dist/sessions/handoff.js +2 -2
- package/dist/store/audit-log-schema.d.ts +106 -39
- package/dist/store/audit-log-schema.d.ts.map +1 -1
- package/dist/store/audit-log-schema.js +6 -3
- package/dist/store/audit-log-schema.js.map +1 -1
- package/dist/store/row-identity-registry.d.ts +42 -6
- package/dist/store/row-identity-registry.d.ts.map +1 -1
- package/dist/store/row-identity-registry.js +256 -34
- package/dist/store/row-identity-registry.js.map +1 -1
- package/dist/store/row-identity.d.ts.map +1 -1
- package/dist/store/row-identity.js +16 -4
- package/dist/store/row-identity.js.map +1 -1
- package/dist/store/schema/cleo-global/nexus.d.ts +16 -0
- package/dist/store/schema/cleo-global/nexus.d.ts.map +1 -1
- package/dist/store/schema/cleo-global/nexus.js +4 -1
- package/dist/store/schema/cleo-global/nexus.js.map +1 -1
- package/dist/store/schema/cleo-project/audit.d.ts +48 -0
- package/dist/store/schema/cleo-project/audit.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/audit.js +15 -1
- package/dist/store/schema/cleo-project/audit.js.map +1 -1
- package/dist/store/schema/cleo-project/lifecycle.d.ts +112 -0
- package/dist/store/schema/cleo-project/lifecycle.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/lifecycle.js +36 -4
- package/dist/store/schema/cleo-project/lifecycle.js.map +1 -1
- package/dist/store/schema/cleo-project/provenance-commits.d.ts +48 -0
- package/dist/store/schema/cleo-project/provenance-commits.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/provenance-commits.js +10 -1
- package/dist/store/schema/cleo-project/provenance-commits.js.map +1 -1
- package/dist/store/schema/cleo-project/provenance-rest.d.ts +128 -0
- package/dist/store/schema/cleo-project/provenance-rest.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/provenance-rest.js +25 -1
- package/dist/store/schema/cleo-project/provenance-rest.js.map +1 -1
- package/dist/store/schema/cleo-project/tasks-core.d.ts +16 -0
- package/dist/store/schema/cleo-project/tasks-core.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/tasks-core.js +6 -1
- package/dist/store/schema/cleo-project/tasks-core.js.map +1 -1
- package/dist/store/session-store.d.ts +5 -4
- package/dist/store/session-store.d.ts.map +1 -1
- package/dist/store/session-store.js +19 -5
- package/dist/store/session-store.js.map +1 -1
- package/dist/store/sync/apply/parent-delete.d.ts.map +1 -1
- package/dist/store/sync/apply/parent-delete.js +8 -0
- package/dist/store/sync/apply/parent-delete.js.map +1 -1
- package/dist/store/sync/apply/write-api.d.ts +3 -2
- package/dist/store/sync/apply/write-api.d.ts.map +1 -1
- package/dist/store/sync/apply/write-api.js +19 -0
- package/dist/store/sync/apply/write-api.js.map +1 -1
- package/dist/store/sync/capture.d.ts +11 -0
- package/dist/store/sync/capture.d.ts.map +1 -1
- package/dist/store/sync/capture.js +13 -3
- package/dist/store/sync/capture.js.map +1 -1
- package/dist/store/sync/flags.d.ts +10 -0
- package/dist/store/sync/flags.d.ts.map +1 -1
- package/dist/store/sync/flags.js +43 -5
- package/dist/store/sync/flags.js.map +1 -1
- package/dist/store/sync/timestamp-canon.d.ts.map +1 -1
- package/dist/store/sync/timestamp-canon.js +37 -0
- package/dist/store/sync/timestamp-canon.js.map +1 -1
- package/dist/store/tasks-schema.d.ts +1 -1
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.js +5 -1
- package/dist/store/tasks-schema.js.map +1 -1
- package/dist/store/twin-collapse.d.ts +2 -1
- package/dist/store/twin-collapse.d.ts.map +1 -1
- package/dist/store/twin-collapse.js +128 -104
- package/dist/store/twin-collapse.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +390 -38
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/store/vault-manifest.d.ts +9 -2
- package/dist/store/vault-manifest.d.ts.map +1 -1
- package/dist/store/vault-manifest.js +25 -4
- package/dist/store/vault-manifest.js.map +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +3 -2
- package/dist/system/health.js.map +1 -1
- package/dist/tasks/affected-packages.d.ts +21 -2
- package/dist/tasks/affected-packages.d.ts.map +1 -1
- package/dist/tasks/affected-packages.js +96 -8
- package/dist/tasks/affected-packages.js.map +1 -1
- package/dist/tasks/affected-scope.d.ts +7 -2
- package/dist/tasks/affected-scope.d.ts.map +1 -1
- package/dist/tasks/affected-scope.js +158 -14
- package/dist/tasks/affected-scope.js.map +1 -1
- package/dist/tasks/change-set.d.ts +10 -0
- package/dist/tasks/change-set.d.ts.map +1 -1
- package/dist/tasks/change-set.js +70 -3
- package/dist/tasks/change-set.js.map +1 -1
- package/dist/tasks/done-plan.d.ts +11 -5
- package/dist/tasks/done-plan.d.ts.map +1 -1
- package/dist/tasks/done-plan.js +54 -10
- package/dist/tasks/done-plan.js.map +1 -1
- package/dist/tasks/done-record.d.ts.map +1 -1
- package/dist/tasks/done-record.js +25 -0
- package/dist/tasks/done-record.js.map +1 -1
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +22 -2
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/gate-result-cache.d.ts.map +1 -1
- package/dist/tasks/gate-result-cache.js +24 -3
- package/dist/tasks/gate-result-cache.js.map +1 -1
- package/dist/tasks/gate-runner.d.ts +27 -3
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +77 -29
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/qa-run-binding.d.ts +22 -3
- package/dist/tasks/qa-run-binding.d.ts.map +1 -1
- package/dist/tasks/qa-run-binding.js +46 -4
- package/dist/tasks/qa-run-binding.js.map +1 -1
- package/dist/tasks/test-run-binding.d.ts +21 -3
- package/dist/tasks/test-run-binding.d.ts.map +1 -1
- package/dist/tasks/test-run-binding.js +60 -14
- package/dist/tasks/test-run-binding.js.map +1 -1
- package/dist/tasks/tool-cache.d.ts.map +1 -1
- package/dist/tasks/tool-cache.js +24 -12
- package/dist/tasks/tool-cache.js.map +1 -1
- package/migrations/drizzle-cleo-global/20261016130000_t13777-nexus-audit-row-uids/migration.sql +18 -0
- package/migrations/drizzle-cleo-project/20261015100000_t12914-lifecycle-release-row-uids/migration.sql +88 -0
- package/migrations/drizzle-cleo-project/20261016130000_t13777-log-row-uids/migration.sql +42 -0
- package/migrations/drizzle-cleo-project/20261016140000_t13777-audit-collapse/migration.sql +31 -0
- package/package.json +18 -18
- package/templates/CLEO-REFERENCE.md +1 -1
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
* decided first-parent ancestor, i.e. the PR merged into a red main and
|
|
35
35
|
* introduced no failure of its own. See {@link inheritedOnMerge}.
|
|
36
36
|
*
|
|
37
|
+
* A third (T13809): a merge commit whose run FAILED only on the flaky-quarantine
|
|
38
|
+
* budget, with every test passing, failed no test of the PR's. See
|
|
39
|
+
* {@link nonTestFailuresOn}. The stand-in search also looks past later runs red
|
|
40
|
+
* for that reason alone, up to {@link CI_DESCENDANT_MAX_CANDIDATES} commits.
|
|
41
|
+
*
|
|
37
42
|
* @task T12634
|
|
38
43
|
*/
|
|
39
44
|
import type { EvidenceAtom, EvidenceValidationContext } from '@cleocode/contracts';
|
|
@@ -98,8 +103,9 @@ export interface EvaluateMergeCommitChecksOptions {
|
|
|
98
103
|
*/
|
|
99
104
|
descendantSha?: string;
|
|
100
105
|
/**
|
|
101
|
-
* The merge commit's failures were all inherited from its
|
|
102
|
-
*
|
|
106
|
+
* The merge commit's failures were not the PR's: all inherited from its
|
|
107
|
+
* base (T13453), or only the flaky-quarantine budget with every test passing
|
|
108
|
+
* (T13809). The descendant may stand in for a FAILED required check too.
|
|
103
109
|
*/
|
|
104
110
|
inheritedFailures?: boolean;
|
|
105
111
|
}
|
|
@@ -124,7 +130,7 @@ export interface MainCommit {
|
|
|
124
130
|
parents: number;
|
|
125
131
|
}
|
|
126
132
|
/** Most later default-branch commits examined for a green stand-in run (T12742). */
|
|
127
|
-
export declare const CI_DESCENDANT_MAX_CANDIDATES =
|
|
133
|
+
export declare const CI_DESCENDANT_MAX_CANDIDATES = 20;
|
|
128
134
|
/** Latest committer time after the merge commit a stand-in may have (T12742): 7 days. */
|
|
129
135
|
export declare const CI_DESCENDANT_MAX_AGE_SECONDS: number;
|
|
130
136
|
/** A `ci:` atom resolution: the validated atom, or the refusal. */
|
|
@@ -191,6 +197,11 @@ export interface ResolveCiEvidenceOptions {
|
|
|
191
197
|
* `git log --first-parent -- <paths>`.
|
|
192
198
|
*/
|
|
193
199
|
touchingCommits?: (from: string, to: string, paths: readonly string[], cwd: string) => MainCommit[] | null;
|
|
200
|
+
/**
|
|
201
|
+
* Plain-text log of one Actions job (a check-run id), or null when it cannot
|
|
202
|
+
* be read (T13809). Defaults to `gh api repos/{owner}/{repo}/actions/jobs/<id>/logs`.
|
|
203
|
+
*/
|
|
204
|
+
fetchJobLog?: (jobId: number, cwd: string) => Promise<string | null>;
|
|
194
205
|
}
|
|
195
206
|
/**
|
|
196
207
|
* Whether the project opted in to merge-commit CI as test/QA evidence
|
|
@@ -390,6 +401,70 @@ export declare function inheritedOnMerge(required: readonly string[], checks: re
|
|
|
390
401
|
sha: string;
|
|
391
402
|
checks: readonly CommitCheck[];
|
|
392
403
|
}>, pins: Record<string, RequiredCheckPinSpec>): InheritedOnMergeResult;
|
|
404
|
+
/**
|
|
405
|
+
* Whether a unit-test job log shows a failure caused by the flaky-quarantine
|
|
406
|
+
* budget alone (T13809). `scripts/ci-flaky-quarantine.mjs` fails a main run
|
|
407
|
+
* whose quarantine is over budget right after the first test run, before it
|
|
408
|
+
* applies its own rule that a quarantined test's failure never blocks. So
|
|
409
|
+
* the log qualifies when the budget heading is present, no error escaped a
|
|
410
|
+
* test, and every failing test of that run is one the quarantine lists (no
|
|
411
|
+
* failure at all is the common case). Only the run's own summary block counts
|
|
412
|
+
* ({@link runSummary}), never a summary a test echoed. Any doubt is `false`:
|
|
413
|
+
* no summary block, a kill or crash marker, a failing test outside the
|
|
414
|
+
* quarantine, a file that failed to load, or counts that do not add up
|
|
415
|
+
* (per-file `(N tests | M failed)` lines included).
|
|
416
|
+
*
|
|
417
|
+
* @param log - The job's plain-text log (ANSI colour codes allowed).
|
|
418
|
+
* @returns Whether the job failed only on the quarantine budget.
|
|
419
|
+
* @task T13809
|
|
420
|
+
*/
|
|
421
|
+
export declare function isQuarantineBudgetOnlyLog(log: string): boolean;
|
|
422
|
+
/** Result of {@link nonTestFailuresOn}. */
|
|
423
|
+
export type NonTestFailuresResult = {
|
|
424
|
+
ok: true;
|
|
425
|
+
jobs: Array<{
|
|
426
|
+
name: string;
|
|
427
|
+
mergeRunId: number;
|
|
428
|
+
}>;
|
|
429
|
+
} | {
|
|
430
|
+
ok: false;
|
|
431
|
+
reason: string;
|
|
432
|
+
};
|
|
433
|
+
/**
|
|
434
|
+
* Whether every failure on a commit's CI is a non-test failure (T13809): each
|
|
435
|
+
* failing job of a pinned workflow failed on the flaky-quarantine budget alone
|
|
436
|
+
* ({@link isQuarantineBudgetOnlyLog}) with every test passing. The required
|
|
437
|
+
* checks themselves (the aggregate job and its workflow run) only report the
|
|
438
|
+
* jobs they need, so their failure follows; any cancelled or timed-out job, a
|
|
439
|
+
* pending run, or an unreadable log refuses.
|
|
440
|
+
*
|
|
441
|
+
* @param checks - Checks fetched for `sha`.
|
|
442
|
+
* @param sha - The commit judged.
|
|
443
|
+
* @param required - Required check names.
|
|
444
|
+
* @param pins - Required-name to app/workflow pin.
|
|
445
|
+
* @param fetchJobLog - Plain-text log of one job.
|
|
446
|
+
* @param cwd - Repository work tree for `gh`.
|
|
447
|
+
* @returns The quarantine-only jobs, or why the commit failed for real.
|
|
448
|
+
* @task T13809
|
|
449
|
+
*/
|
|
450
|
+
export declare function nonTestFailuresOn(checks: readonly CommitCheck[], sha: string, required: readonly string[], pins: Record<string, RequiredCheckPinSpec>, fetchJobLog: NonNullable<ResolveCiEvidenceOptions['fetchJobLog']>, cwd: string): Promise<NonTestFailuresResult>;
|
|
451
|
+
/**
|
|
452
|
+
* Whether a later commit's CI was cancelled rather than failed (T13809): its
|
|
453
|
+
* pinned workflow run concluded `cancelled` (a concurrency group stopped it)
|
|
454
|
+
* and every failing job's log is readable and names no failed test; a job a
|
|
455
|
+
* cancel interrupted often reports `failure` with nothing failed in its log. Such a run decides nothing, as a
|
|
456
|
+
* cancelled run never did (T12742).
|
|
457
|
+
*
|
|
458
|
+
* @param checks - Checks fetched for `sha`.
|
|
459
|
+
* @param sha - The later commit.
|
|
460
|
+
* @param required - Required check names.
|
|
461
|
+
* @param pins - Required-name to app/workflow pin.
|
|
462
|
+
* @param fetchJobLog - Plain-text log of one job.
|
|
463
|
+
* @param cwd - Repository work tree for `gh`.
|
|
464
|
+
* @returns Whether the run was cancelled with no test failing.
|
|
465
|
+
* @task T13809
|
|
466
|
+
*/
|
|
467
|
+
export declare function cancelledWithoutTestFailure(checks: readonly CommitCheck[], sha: string, required: readonly string[], pins: Record<string, RequiredCheckPinSpec>, fetchJobLog: NonNullable<ResolveCiEvidenceOptions['fetchJobLog']>, cwd: string): Promise<boolean>;
|
|
393
468
|
/** Inputs of {@link findGreenDescendant}. */
|
|
394
469
|
export interface FindGreenDescendantInput {
|
|
395
470
|
/** Required check names. */
|
|
@@ -430,6 +505,8 @@ export interface FindGreenDescendantInput {
|
|
|
430
505
|
isAncestor: NonNullable<ResolveCiEvidenceOptions['isAncestor']>;
|
|
431
506
|
/** First parent of a commit (T13453: the inherited-failure walk); absent = never inherit. */
|
|
432
507
|
firstParentOf?: NonNullable<ResolveCiEvidenceOptions['firstParentOf']>;
|
|
508
|
+
/** Log of one job (T13809: the quarantine-budget check); absent = never classify. */
|
|
509
|
+
fetchJobLog?: NonNullable<ResolveCiEvidenceOptions['fetchJobLog']>;
|
|
433
510
|
}
|
|
434
511
|
/** Result of {@link findGreenDescendant}. */
|
|
435
512
|
export type FindGreenDescendantResult = {
|
|
@@ -447,6 +524,11 @@ export type FindGreenDescendantResult = {
|
|
|
447
524
|
prHeadSha?: string;
|
|
448
525
|
/** The merge commit's inherited failures, when it FAILED rather than being superseded (T13453). */
|
|
449
526
|
inherited?: InheritedFailure[];
|
|
527
|
+
/** The merge commit's jobs that failed on the flaky-quarantine budget alone (T13809). */
|
|
528
|
+
nonTest?: Array<{
|
|
529
|
+
name: string;
|
|
530
|
+
mergeRunId: number;
|
|
531
|
+
}>;
|
|
450
532
|
} | {
|
|
451
533
|
ok: false;
|
|
452
534
|
reason: string;
|
|
@@ -457,7 +539,9 @@ export type FindGreenDescendantResult = {
|
|
|
457
539
|
*
|
|
458
540
|
* The stand-in's CI tested the DESCENDANT's tree, not the merge tree. It is
|
|
459
541
|
* accepted only when that difference cannot matter to the PR:
|
|
460
|
-
* - the merge commit must be superseded, not failed ({@link supersededOnMerge})
|
|
542
|
+
* - the merge commit must be superseded, not failed ({@link supersededOnMerge}),
|
|
543
|
+
* unless its failures were inherited from main ({@link inheritedOnMerge}) or
|
|
544
|
+
* were only the flaky-quarantine budget ({@link nonTestFailuresOn}, T13809);
|
|
461
545
|
* - the PR's own final head (`prHeadSha`) must have a green latest
|
|
462
546
|
* `pull_request` run for every superseded check — a later fix on main
|
|
463
547
|
* never rescues a broken PR;
|
|
@@ -473,7 +557,8 @@ export type FindGreenDescendantResult = {
|
|
|
473
557
|
* failed job of a pinned workflow) refuses, and a PENDING run refuses with
|
|
474
558
|
* "wait for <sha>" — a later green run is never shopped for past one that
|
|
475
559
|
* is red or may still go red. Only cancelled, skipped and missing
|
|
476
|
-
* (never-started) runs
|
|
560
|
+
* (never-started) runs, and a run red only for the flaky-quarantine budget
|
|
561
|
+
* (T13809), move on to the next candidate.
|
|
477
562
|
*
|
|
478
563
|
* @param input - Merge-commit checks, pins, changed paths, PR head and injected I/O.
|
|
479
564
|
* @returns The stand-in SHA, its checks and proven range, or the refusal reason.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-evidence.d.ts","sourceRoot":"","sources":["../../src/release/ci-evidence.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ci-evidence.d.ts","sourceRoot":"","sources":["../../src/release/ci-evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
* decided first-parent ancestor, i.e. the PR merged into a red main and
|
|
35
35
|
* introduced no failure of its own. See {@link inheritedOnMerge}.
|
|
36
36
|
*
|
|
37
|
+
* A third (T13809): a merge commit whose run FAILED only on the flaky-quarantine
|
|
38
|
+
* budget, with every test passing, failed no test of the PR's. See
|
|
39
|
+
* {@link nonTestFailuresOn}. The stand-in search also looks past later runs red
|
|
40
|
+
* for that reason alone, up to {@link CI_DESCENDANT_MAX_CANDIDATES} commits.
|
|
41
|
+
*
|
|
37
42
|
* @task T12634
|
|
38
43
|
*/
|
|
39
44
|
import { execFileSync } from 'node:child_process';
|
|
@@ -46,7 +51,7 @@ import { describeRequiredWorkflowsSource, readRequiredCheckPins, resolvePrEviden
|
|
|
46
51
|
/** First-parent ancestors searched for a decided base verdict of a failing job (T13453). */
|
|
47
52
|
export const CI_INHERITED_MAX_ANCESTORS = 10;
|
|
48
53
|
/** Most later default-branch commits examined for a green stand-in run (T12742). */
|
|
49
|
-
export const CI_DESCENDANT_MAX_CANDIDATES =
|
|
54
|
+
export const CI_DESCENDANT_MAX_CANDIDATES = 20;
|
|
50
55
|
/** Latest committer time after the merge commit a stand-in may have (T12742): 7 days. */
|
|
51
56
|
export const CI_DESCENDANT_MAX_AGE_SECONDS = 7 * 24 * 60 * 60;
|
|
52
57
|
/** Merge-commit verdicts a later green main run may stand in for (T12742). */
|
|
@@ -601,6 +606,204 @@ export function inheritedOnMerge(required, checks, mergeCommitSha, ancestors, pi
|
|
|
601
606
|
const names = required.filter((name) => !judgeOnSha(checks.filter((c) => c.name === name && pinMatches(c, pins[name])), mergeCommitSha).ok);
|
|
602
607
|
return { ok: true, names, inherited };
|
|
603
608
|
}
|
|
609
|
+
/**
|
|
610
|
+
* The heading `scripts/ci-flaky-quarantine.mjs` prints when it fails a main
|
|
611
|
+
* run because the open quarantine exceeds its budget (T13145). It returns
|
|
612
|
+
* right after the first test run, whatever that run's outcome.
|
|
613
|
+
*/
|
|
614
|
+
const QUARANTINE_BUDGET_HEADING = '### Flaky quarantine over budget';
|
|
615
|
+
/** Test-title form shared by vitest's ` > `-joined names and the quarantine list. */
|
|
616
|
+
const normTitle = (title) => title
|
|
617
|
+
.replace(/\s*>\s*/g, ' ')
|
|
618
|
+
.replace(/\s+/g, ' ')
|
|
619
|
+
.trim();
|
|
620
|
+
/** The failed count in one vitest summary line (0 when it names none). */
|
|
621
|
+
const failedIn = (line) => Number(/(\d+) failed/.exec(line)?.[1] ?? 0);
|
|
622
|
+
/**
|
|
623
|
+
* The run's own vitest summary block in `text` (T13809): the LAST `Test Files`
|
|
624
|
+
* line, preceded by a blank or test-result line and followed within four lines
|
|
625
|
+
* by its `Tests`, `Start at` and `Duration` lines, with no test result or test
|
|
626
|
+
* output after it. A killed run has no block of its own; a block a test echoed
|
|
627
|
+
* (even a full one) is followed by more test output, or follows a child's
|
|
628
|
+
* unprefixed output. Null when there is none.
|
|
629
|
+
*/
|
|
630
|
+
/** A vitest per-file result, a test's captured output, or a failure line. */
|
|
631
|
+
const TEST_RESULT_LINE = /^\S*\s+(?:[✓❯×✗↓]\s|stdout \||stderr \|)|\bFAIL\s{2,}/;
|
|
632
|
+
/** What vitest prints right before its summary: a blank (timestamp-only) or result line. */
|
|
633
|
+
const BEFORE_SUMMARY_LINE = /^(?:\d{4}-\d\d-\d\dT\S+Z)?\s*$|^\S*\s+[✓❯×✗↓]\s|\bFAIL\s{2,}/;
|
|
634
|
+
function runSummary(text) {
|
|
635
|
+
const lines = text.split('\n');
|
|
636
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
637
|
+
if (!/\bTest Files\s{2,}\d/.test(lines[i] ?? ''))
|
|
638
|
+
continue;
|
|
639
|
+
// A child vitest's echoed block may follow its unprefixed output (inherited stdio).
|
|
640
|
+
if (i > 0 && !BEFORE_SUMMARY_LINE.test(lines[i - 1] ?? ''))
|
|
641
|
+
return null;
|
|
642
|
+
const block = lines.slice(i + 1, i + 5);
|
|
643
|
+
const tests = block.find((l) => /\bTests\s{2,}\d/.test(l));
|
|
644
|
+
if (!tests || !block.some((l) => /\bStart at\s{2,}/.test(l)))
|
|
645
|
+
return null;
|
|
646
|
+
const duration = block.findIndex((l) => /\bDuration\s{2,}/.test(l));
|
|
647
|
+
if (duration < 0)
|
|
648
|
+
return null;
|
|
649
|
+
// The run's own summary is its LAST vitest output: a test result or test
|
|
650
|
+
// output after it means this block was echoed by a test, and the run itself
|
|
651
|
+
// died before printing its own (an OOM-killed run prints no marker).
|
|
652
|
+
if (lines.slice(i + 2 + duration).some((l) => TEST_RESULT_LINE.test(l)))
|
|
653
|
+
return null;
|
|
654
|
+
return { failedFiles: failedIn(lines[i] ?? ''), failedTests: failedIn(tests) };
|
|
655
|
+
}
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* vitest's own report that a worker died or ran out of memory. A test's child
|
|
660
|
+
* processes print `Killed` and signal names as ordinary output, so those are
|
|
661
|
+
* not markers; a killed vitest leaves no summary block ({@link runSummary}).
|
|
662
|
+
*/
|
|
663
|
+
const RUN_DIED = /Worker exited unexpectedly|Worker terminated due to reaching memory limit|JavaScript heap out of memory/;
|
|
664
|
+
/**
|
|
665
|
+
* Whether a unit-test job log shows a failure caused by the flaky-quarantine
|
|
666
|
+
* budget alone (T13809). `scripts/ci-flaky-quarantine.mjs` fails a main run
|
|
667
|
+
* whose quarantine is over budget right after the first test run, before it
|
|
668
|
+
* applies its own rule that a quarantined test's failure never blocks. So
|
|
669
|
+
* the log qualifies when the budget heading is present, no error escaped a
|
|
670
|
+
* test, and every failing test of that run is one the quarantine lists (no
|
|
671
|
+
* failure at all is the common case). Only the run's own summary block counts
|
|
672
|
+
* ({@link runSummary}), never a summary a test echoed. Any doubt is `false`:
|
|
673
|
+
* no summary block, a kill or crash marker, a failing test outside the
|
|
674
|
+
* quarantine, a file that failed to load, or counts that do not add up
|
|
675
|
+
* (per-file `(N tests | M failed)` lines included).
|
|
676
|
+
*
|
|
677
|
+
* @param log - The job's plain-text log (ANSI colour codes allowed).
|
|
678
|
+
* @returns Whether the job failed only on the quarantine budget.
|
|
679
|
+
* @task T13809
|
|
680
|
+
*/
|
|
681
|
+
export function isQuarantineBudgetOnlyLog(log) {
|
|
682
|
+
const text = log.replace(/\u001b\[[0-9;]*m/g, '');
|
|
683
|
+
// The script's own verdict is the last heading, with its count line under it
|
|
684
|
+
// (a test's fixture output may carry the heading too).
|
|
685
|
+
const heading = text.lastIndexOf(QUARANTINE_BUDGET_HEADING);
|
|
686
|
+
if (heading < 0)
|
|
687
|
+
return false;
|
|
688
|
+
if (!/^[^\n]*\n(?:[^\n]*\n)?[^\n]*\d+ tests are quarantined \(most \d+\)/.test(text.slice(heading))) {
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
const run = text.slice(0, heading);
|
|
692
|
+
if (/Unhandled (Errors?|Rejection)|\bErrors\s{2,}\d/.test(run) || RUN_DIED.test(run))
|
|
693
|
+
return false;
|
|
694
|
+
// Only the run's own summary counts; a summary a test echoed never stands in.
|
|
695
|
+
const summary = runSummary(run);
|
|
696
|
+
if (summary === null)
|
|
697
|
+
return false;
|
|
698
|
+
const { failedFiles, failedTests } = summary;
|
|
699
|
+
const testPath = String.raw `\S+\.(?:test|spec)\.[cm]?[jt]sx?`;
|
|
700
|
+
// Every per-file `(N tests | M failed)` line must agree with the summary.
|
|
701
|
+
const perFile = [
|
|
702
|
+
...run.matchAll(new RegExp(`❯\\s+(?:\\S+\\s+)?(${testPath})\\s+\\([^)]*?(\\d+) failed`, 'gm')),
|
|
703
|
+
].map((m) => ({ file: m[1] ?? '', failed: Number(m[2] ?? 0) }));
|
|
704
|
+
if (perFile.reduce((a, f) => a + f.failed, 0) !== failedTests)
|
|
705
|
+
return false;
|
|
706
|
+
if (failedFiles === 0 && failedTests === 0)
|
|
707
|
+
return true;
|
|
708
|
+
const quarantined = [
|
|
709
|
+
...text.slice(heading).matchAll(new RegExp(`- \`(${testPath})\` > (.+)$`, 'gm')),
|
|
710
|
+
].map((m) => ({ file: m[1] ?? '', title: normTitle(m[2] ?? '') }));
|
|
711
|
+
const failing = new Map();
|
|
712
|
+
for (const m of run.matchAll(new RegExp(`\\bFAIL\\s{2,}(?:\\S+\\s+)?(${testPath}) > (.+)$`, 'gm'))) {
|
|
713
|
+
const entry = { file: m[1] ?? '', title: normTitle(m[2] ?? '') };
|
|
714
|
+
failing.set(`${entry.file}\u0000${entry.title}`, entry);
|
|
715
|
+
}
|
|
716
|
+
if (failing.size !== failedTests)
|
|
717
|
+
return false;
|
|
718
|
+
if (new Set([...failing.values()].map((f) => f.file)).size !== failedFiles)
|
|
719
|
+
return false;
|
|
720
|
+
if (!perFile.every((p) => [...failing.values()].some((f) => f.file === p.file)))
|
|
721
|
+
return false;
|
|
722
|
+
return [...failing.values()].every((f) => quarantined.some((q) => q.title === f.title && q.file.endsWith(f.file)));
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Whether every failure on a commit's CI is a non-test failure (T13809): each
|
|
726
|
+
* failing job of a pinned workflow failed on the flaky-quarantine budget alone
|
|
727
|
+
* ({@link isQuarantineBudgetOnlyLog}) with every test passing. The required
|
|
728
|
+
* checks themselves (the aggregate job and its workflow run) only report the
|
|
729
|
+
* jobs they need, so their failure follows; any cancelled or timed-out job, a
|
|
730
|
+
* pending run, or an unreadable log refuses.
|
|
731
|
+
*
|
|
732
|
+
* @param checks - Checks fetched for `sha`.
|
|
733
|
+
* @param sha - The commit judged.
|
|
734
|
+
* @param required - Required check names.
|
|
735
|
+
* @param pins - Required-name to app/workflow pin.
|
|
736
|
+
* @param fetchJobLog - Plain-text log of one job.
|
|
737
|
+
* @param cwd - Repository work tree for `gh`.
|
|
738
|
+
* @returns The quarantine-only jobs, or why the commit failed for real.
|
|
739
|
+
* @task T13809
|
|
740
|
+
*/
|
|
741
|
+
export async function nonTestFailuresOn(checks, sha, required, pins, fetchJobLog, cwd) {
|
|
742
|
+
const latest = latestRuns(checks, sha, pinnedScope(required, pins));
|
|
743
|
+
const jobs = [];
|
|
744
|
+
for (const c of latest) {
|
|
745
|
+
if (c.status !== 'completed') {
|
|
746
|
+
return { ok: false, reason: `${c.name}: pending on ${sha.slice(0, 12)}` };
|
|
747
|
+
}
|
|
748
|
+
if (c.conclusion === 'success' || c.conclusion === 'skipped')
|
|
749
|
+
continue;
|
|
750
|
+
// The required checks (the aggregate job and its workflow run) follow the jobs they need.
|
|
751
|
+
if (c.source === 'workflow-run' || required.includes(c.name))
|
|
752
|
+
continue;
|
|
753
|
+
const log = c.conclusion === 'failure' ? await fetchJobLog(c.id, cwd) : null;
|
|
754
|
+
if (log === null || !isQuarantineBudgetOnlyLog(log)) {
|
|
755
|
+
return {
|
|
756
|
+
ok: false,
|
|
757
|
+
reason: `${c.name}: ${c.conclusion} on ${sha.slice(0, 12)}${log === null && c.conclusion === 'failure' ? ' (its log could not be read)' : ''} — not the flaky-quarantine budget alone`,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
jobs.push({ name: c.name, mergeRunId: c.id });
|
|
761
|
+
}
|
|
762
|
+
return jobs.length > 0
|
|
763
|
+
? { ok: true, jobs }
|
|
764
|
+
: { ok: false, reason: `no job failed on ${sha.slice(0, 12)} for the quarantine budget` };
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Whether a later commit's CI was cancelled rather than failed (T13809): its
|
|
768
|
+
* pinned workflow run concluded `cancelled` (a concurrency group stopped it)
|
|
769
|
+
* and every failing job's log is readable and names no failed test; a job a
|
|
770
|
+
* cancel interrupted often reports `failure` with nothing failed in its log. Such a run decides nothing, as a
|
|
771
|
+
* cancelled run never did (T12742).
|
|
772
|
+
*
|
|
773
|
+
* @param checks - Checks fetched for `sha`.
|
|
774
|
+
* @param sha - The later commit.
|
|
775
|
+
* @param required - Required check names.
|
|
776
|
+
* @param pins - Required-name to app/workflow pin.
|
|
777
|
+
* @param fetchJobLog - Plain-text log of one job.
|
|
778
|
+
* @param cwd - Repository work tree for `gh`.
|
|
779
|
+
* @returns Whether the run was cancelled with no test failing.
|
|
780
|
+
* @task T13809
|
|
781
|
+
*/
|
|
782
|
+
export async function cancelledWithoutTestFailure(checks, sha, required, pins, fetchJobLog, cwd) {
|
|
783
|
+
const latest = latestRuns(checks, sha, pinnedScope(required, pins));
|
|
784
|
+
const runs = latest.filter((c) => c.source === 'workflow-run');
|
|
785
|
+
if (!runs.some((r) => r.conclusion === 'cancelled'))
|
|
786
|
+
return false;
|
|
787
|
+
if (runs.some((r) => r.conclusion !== 'cancelled' && r.conclusion !== 'success'))
|
|
788
|
+
return false;
|
|
789
|
+
for (const c of latest) {
|
|
790
|
+
if (c.source === 'workflow-run' || required.includes(c.name))
|
|
791
|
+
continue;
|
|
792
|
+
if (c.status !== 'completed')
|
|
793
|
+
return false;
|
|
794
|
+
if (HARMLESS_CONCLUSIONS.has(c.conclusion ?? ''))
|
|
795
|
+
continue;
|
|
796
|
+
const log = await fetchJobLog(c.id, cwd);
|
|
797
|
+
// An unreadable log proves nothing about the job: never skip past it.
|
|
798
|
+
if (log === null)
|
|
799
|
+
return false;
|
|
800
|
+
const text = log.replace(/\u001b\[[0-9;]*m/g, '');
|
|
801
|
+
if (/\bFAIL\s{2,}|\d+ failed/.test(text) || RUN_DIED.test(text)) {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
604
807
|
/**
|
|
605
808
|
* CI-definition paths no commit in a descendant range may touch (T12742): the
|
|
606
809
|
* pinned workflow file of every required check, local composite actions, and
|
|
@@ -620,7 +823,9 @@ function ciDefinitionPaths(required, pins) {
|
|
|
620
823
|
*
|
|
621
824
|
* The stand-in's CI tested the DESCENDANT's tree, not the merge tree. It is
|
|
622
825
|
* accepted only when that difference cannot matter to the PR:
|
|
623
|
-
* - the merge commit must be superseded, not failed ({@link supersededOnMerge})
|
|
826
|
+
* - the merge commit must be superseded, not failed ({@link supersededOnMerge}),
|
|
827
|
+
* unless its failures were inherited from main ({@link inheritedOnMerge}) or
|
|
828
|
+
* were only the flaky-quarantine budget ({@link nonTestFailuresOn}, T13809);
|
|
624
829
|
* - the PR's own final head (`prHeadSha`) must have a green latest
|
|
625
830
|
* `pull_request` run for every superseded check — a later fix on main
|
|
626
831
|
* never rescues a broken PR;
|
|
@@ -636,7 +841,8 @@ function ciDefinitionPaths(required, pins) {
|
|
|
636
841
|
* failed job of a pinned workflow) refuses, and a PENDING run refuses with
|
|
637
842
|
* "wait for <sha>" — a later green run is never shopped for past one that
|
|
638
843
|
* is red or may still go red. Only cancelled, skipped and missing
|
|
639
|
-
* (never-started) runs
|
|
844
|
+
* (never-started) runs, and a run red only for the flaky-quarantine budget
|
|
845
|
+
* (T13809), move on to the next candidate.
|
|
640
846
|
*
|
|
641
847
|
* @param input - Merge-commit checks, pins, changed paths, PR head and injected I/O.
|
|
642
848
|
* @returns The stand-in SHA, its checks and proven range, or the refusal reason.
|
|
@@ -649,6 +855,19 @@ export async function findGreenDescendant(input) {
|
|
|
649
855
|
pins,
|
|
650
856
|
});
|
|
651
857
|
let inherited;
|
|
858
|
+
let nonTest;
|
|
859
|
+
if (!superseded.ok && input.fetchJobLog) {
|
|
860
|
+
// T13809: a merge-commit run red only because main's flaky quarantine was
|
|
861
|
+
// over budget (every test passed) failed no test of the PR's.
|
|
862
|
+
const result = await nonTestFailuresOn(input.checks, merge, required, pins, input.fetchJobLog, cwd);
|
|
863
|
+
if (result.ok) {
|
|
864
|
+
nonTest = result.jobs;
|
|
865
|
+
superseded = {
|
|
866
|
+
ok: true,
|
|
867
|
+
names: required.filter((name) => !judgeOnSha(input.checks.filter((c) => c.name === name && pinMatches(c, pins[name])), merge).ok),
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
}
|
|
652
871
|
if (!superseded.ok && input.firstParentOf) {
|
|
653
872
|
// T13453: a real failure may still be main's, inherited by a PR that
|
|
654
873
|
// merged into a red main. Walk the first-parent ancestors for each
|
|
@@ -736,6 +955,17 @@ export async function findGreenDescendant(input) {
|
|
|
736
955
|
if (!fetched.ok)
|
|
737
956
|
return { ok: false, reason: fetched.reason };
|
|
738
957
|
const failedJob = firstRealFailure(fetched.checks, sha, scope);
|
|
958
|
+
if (failedJob && input.fetchJobLog) {
|
|
959
|
+
// T13809: a later run red only for the quarantine budget, or cancelled
|
|
960
|
+
// with no test failing, decides nothing; look on. Any other failure still
|
|
961
|
+
// refuses: a green run is never shopped for past a real one.
|
|
962
|
+
const quarantineOnly = await nonTestFailuresOn(fetched.checks, sha, required, pins, input.fetchJobLog, cwd);
|
|
963
|
+
if (quarantineOnly.ok)
|
|
964
|
+
continue;
|
|
965
|
+
if (await cancelledWithoutTestFailure(fetched.checks, sha, required, pins, input.fetchJobLog, cwd)) {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
739
969
|
if (failedJob) {
|
|
740
970
|
return {
|
|
741
971
|
ok: false,
|
|
@@ -771,6 +1001,7 @@ export async function findGreenDescendant(input) {
|
|
|
771
1001
|
range: `${merge}..${sha}`,
|
|
772
1002
|
...(head ? { prHeadSha: head } : {}),
|
|
773
1003
|
...(inherited ? { inherited } : {}),
|
|
1004
|
+
...(nonTest ? { nonTest } : {}),
|
|
774
1005
|
};
|
|
775
1006
|
}
|
|
776
1007
|
}
|
|
@@ -926,6 +1157,29 @@ function ghApi(path, cwd) {
|
|
|
926
1157
|
return null;
|
|
927
1158
|
}
|
|
928
1159
|
}
|
|
1160
|
+
/** Default {@link ResolveCiEvidenceOptions.fetchJobLog}: the job's plain-text log via `gh` (T13809). */
|
|
1161
|
+
async function defaultFetchJobLog(jobId, cwd) {
|
|
1162
|
+
if (!isGitWorkTree(cwd) || !isGhCliAvailable())
|
|
1163
|
+
return null;
|
|
1164
|
+
try {
|
|
1165
|
+
// Job logs carry ANSI colour; gh refuses to print them without the flag.
|
|
1166
|
+
const args = [
|
|
1167
|
+
'api',
|
|
1168
|
+
'--allow-escape-sequences',
|
|
1169
|
+
`repos/{owner}/{repo}/actions/jobs/${jobId}/logs`,
|
|
1170
|
+
];
|
|
1171
|
+
return execFileSync('gh', args, {
|
|
1172
|
+
cwd,
|
|
1173
|
+
encoding: 'utf-8',
|
|
1174
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1175
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
1176
|
+
timeout: ghQueryTimeoutMs(),
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
catch {
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
929
1183
|
/**
|
|
930
1184
|
* Drop runs a schedule started from every fetch (T13623). A nightly run that
|
|
931
1185
|
* happens to land on a merge commit tests main at that hour, not the merge:
|
|
@@ -1294,6 +1548,7 @@ export async function resolveCiEvidenceAtom(prNumber, roots, opts = {}) {
|
|
|
1294
1548
|
touchingCommits: opts.touchingCommits ?? listPathTouchingMainCommits,
|
|
1295
1549
|
isAncestor: opts.isAncestor ?? defaultIsAncestor,
|
|
1296
1550
|
firstParentOf: opts.firstParentOf ?? defaultFirstParentOf,
|
|
1551
|
+
fetchJobLog: opts.fetchJobLog ?? defaultFetchJobLog,
|
|
1297
1552
|
});
|
|
1298
1553
|
if (found.ok) {
|
|
1299
1554
|
const withDescendant = [...checks, ...found.checks];
|
|
@@ -1301,7 +1556,7 @@ export async function resolveCiEvidenceAtom(prNumber, roots, opts = {}) {
|
|
|
1301
1556
|
...(treeEqualHead ? { treeEquivalentSha: treeEqualHead } : {}),
|
|
1302
1557
|
pins,
|
|
1303
1558
|
descendantSha: found.sha,
|
|
1304
|
-
...(found.inherited ? { inheritedFailures: true } : {}),
|
|
1559
|
+
...(found.inherited || found.nonTest ? { inheritedFailures: true } : {}),
|
|
1305
1560
|
});
|
|
1306
1561
|
if (rejudged.ok) {
|
|
1307
1562
|
checks = withDescendant;
|
|
@@ -1311,6 +1566,7 @@ export async function resolveCiEvidenceAtom(prNumber, roots, opts = {}) {
|
|
|
1311
1566
|
range: found.range,
|
|
1312
1567
|
...(found.prHeadSha ? { prHeadSha: found.prHeadSha } : {}),
|
|
1313
1568
|
...(found.inherited ? { inherited: found.inherited } : {}),
|
|
1569
|
+
...(found.nonTest ? { nonTest: found.nonTest } : {}),
|
|
1314
1570
|
};
|
|
1315
1571
|
}
|
|
1316
1572
|
else {
|
|
@@ -1348,6 +1604,8 @@ export async function resolveCiEvidenceAtom(prNumber, roots, opts = {}) {
|
|
|
1348
1604
|
const docsOnlyDiff = pr.changedPaths.length > 0 && pr.changedPaths.every(isCiDocumentPath);
|
|
1349
1605
|
const isCode = !docsOnlyDiff;
|
|
1350
1606
|
const jobsByGate = {};
|
|
1607
|
+
const fetchJobLog = opts.fetchJobLog ?? defaultFetchJobLog;
|
|
1608
|
+
let mergeNonTest;
|
|
1351
1609
|
if (isCode) {
|
|
1352
1610
|
for (const g of gateLists) {
|
|
1353
1611
|
const globs = readCiChecks(roots.storeRoot).jobs?.[g.key];
|
|
@@ -1370,6 +1628,20 @@ export async function resolveCiEvidenceAtom(prNumber, roots, opts = {}) {
|
|
|
1370
1628
|
}
|
|
1371
1629
|
const scope = { ...(app !== undefined ? { app } : {}), workflows: scopeWorkflows };
|
|
1372
1630
|
let judgedJobs = evaluateJobs(globs, checks, pr.mergeCommitSha, scope);
|
|
1631
|
+
if (!judgedJobs.ok) {
|
|
1632
|
+
// T13809: a job red only on the quarantine budget ran every test and
|
|
1633
|
+
// they all passed; for this gate it ran and succeeded.
|
|
1634
|
+
mergeNonTest ??= descendant?.nonTest
|
|
1635
|
+
? { ok: true, jobs: descendant.nonTest }
|
|
1636
|
+
: await nonTestFailuresOn(checks, pr.mergeCommitSha, required.workflows, pins, fetchJobLog, cwd);
|
|
1637
|
+
if (mergeNonTest.ok) {
|
|
1638
|
+
const passed = new Set(mergeNonTest.jobs.map((j) => j.mergeRunId));
|
|
1639
|
+
const asRun = checks.map((c) => (passed.has(c.id) ? { ...c, conclusion: 'success' } : c));
|
|
1640
|
+
const onMerge = evaluateJobs(globs, asRun, pr.mergeCommitSha, scope);
|
|
1641
|
+
if (onMerge.ok)
|
|
1642
|
+
judgedJobs = onMerge;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1373
1645
|
if (!judgedJobs.ok && treeEqualHead && !mainOnly) {
|
|
1374
1646
|
const onHead = evaluateJobs(globs, checks, treeEqualHead, scope, 'pull_request');
|
|
1375
1647
|
if (onHead.ok)
|
|
@@ -1431,6 +1703,7 @@ export async function resolveCiEvidenceAtom(prNumber, roots, opts = {}) {
|
|
|
1431
1703
|
descendantRange: descendant.range,
|
|
1432
1704
|
...(descendant.prHeadSha ? { descendantPrHeadSha: descendant.prHeadSha } : {}),
|
|
1433
1705
|
...(descendant.inherited ? { inheritedFailures: descendant.inherited } : {}),
|
|
1706
|
+
...(descendant.nonTest ? { nonTestFailures: descendant.nonTest } : {}),
|
|
1434
1707
|
}
|
|
1435
1708
|
: {}),
|
|
1436
1709
|
...(mainOnly ? { mainOnly: true } : {}),
|