@vitest-evals/github-reporter 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -1
- package/dist/cli.js +232 -17
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +232 -17
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +94 -1
- package/dist/index.d.ts +94 -1
- package/dist/index.js +360 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +358 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -36,6 +36,24 @@ steps:
|
|
|
36
36
|
If configuration or permission is missing, the action keeps the job summary and
|
|
37
37
|
workflow annotations and warns instead of failing.
|
|
38
38
|
|
|
39
|
+
## Score and Pass-Rate Gates
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
- id: report
|
|
43
|
+
uses: getsentry/vitest-evals@v0
|
|
44
|
+
with:
|
|
45
|
+
results: eval-results/*.json
|
|
46
|
+
publish-check: true
|
|
47
|
+
min-pass-rate: 0.8
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- `fail-on-failures: true` requires every eval case to pass
|
|
51
|
+
- `min-pass-rate` and `min-score-average` set aggregate floors in the `0`-`1` range
|
|
52
|
+
- `status`, Check Run conclusion/title, and step exit follow the gate
|
|
53
|
+
- quality misses become warnings when the gate still passes
|
|
54
|
+
- non-eval / infrastructure failures still fail hard
|
|
55
|
+
- use `evals-failed` / `pass-rate` for raw tallies (`pass-rate` is a 0-1 ratio)
|
|
56
|
+
|
|
39
57
|
## Sharded Reports
|
|
40
58
|
|
|
41
59
|
Upload one JSON artifact per shard, then publish one combined report from a
|
|
@@ -52,6 +70,7 @@ final reducer job:
|
|
|
52
70
|
with:
|
|
53
71
|
results: eval-results/*.json
|
|
54
72
|
publish-check: true
|
|
73
|
+
min-pass-rate: 0.8
|
|
55
74
|
```
|
|
56
75
|
|
|
57
76
|
## Inputs
|
|
@@ -64,7 +83,9 @@ final reducer job:
|
|
|
64
83
|
| `publish-check` | `false` | Publish one GitHub Check Run for the combined report. |
|
|
65
84
|
| `check-name` | `vitest-evals` | Name of the GitHub Check Run. |
|
|
66
85
|
| `github-token` | `${{ github.token }}` | Token used for Check Run publishing. |
|
|
67
|
-
| `fail-on-failures` | `false` | Fail the action when
|
|
86
|
+
| `fail-on-failures` | `false` | Fail the action when any eval case failed. Equivalent to `min-pass-rate: 1`. |
|
|
87
|
+
| `min-pass-rate` | unset | Minimum fraction of eval cases that must pass (`0`-`1`). |
|
|
88
|
+
| `min-score-average` | unset | Minimum average eval score across scored cases (`0`-`1`). |
|
|
68
89
|
| `max-annotations` | unset | Maximum number of failure annotations to publish. Check Run annotations are capped at 50 by GitHub. |
|
|
69
90
|
| `max-failures` | unset | Maximum number of detailed failures to include in summaries and checks. |
|
|
70
91
|
|
package/dist/cli.js
CHANGED
|
@@ -41,6 +41,12 @@ function parseCliArgs(args, env = process.env) {
|
|
|
41
41
|
case "--fail-on-check-error":
|
|
42
42
|
options.failOnCheckError = true;
|
|
43
43
|
break;
|
|
44
|
+
case "--min-pass-rate":
|
|
45
|
+
options.minPassRate = readRatio(args, ++index, arg);
|
|
46
|
+
break;
|
|
47
|
+
case "--min-score-average":
|
|
48
|
+
options.minScoreAverage = readRatio(args, ++index, arg);
|
|
49
|
+
break;
|
|
44
50
|
case "--max-annotations":
|
|
45
51
|
options.maxAnnotations = readInteger(args, ++index, arg);
|
|
46
52
|
break;
|
|
@@ -100,6 +106,14 @@ function readInteger(args, index, flag) {
|
|
|
100
106
|
}
|
|
101
107
|
return Number(rawValue);
|
|
102
108
|
}
|
|
109
|
+
function readRatio(args, index, flag) {
|
|
110
|
+
const rawValue = readValue(args, index, flag);
|
|
111
|
+
const parsed = Number(rawValue);
|
|
112
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
|
|
113
|
+
throw new Error(`Invalid ratio for ${flag}`);
|
|
114
|
+
}
|
|
115
|
+
return parsed;
|
|
116
|
+
}
|
|
103
117
|
|
|
104
118
|
// src/report.ts
|
|
105
119
|
var import_promises = require("fs/promises");
|
|
@@ -188,9 +202,10 @@ var DEFAULT_MAX_CHECK_ANNOTATIONS = 50;
|
|
|
188
202
|
var MAX_CHECK_FIELD_LENGTH = 64e3;
|
|
189
203
|
function renderWorkflowCommands(report, options = {}) {
|
|
190
204
|
const maxAnnotations = options.maxAnnotations ?? DEFAULT_MAX_WORKFLOW_ANNOTATIONS;
|
|
205
|
+
const command = caseAnnotationCommand(options.gate);
|
|
191
206
|
return report.failures.filter(hasAnnotationLocation).slice(0, maxAnnotations).map(
|
|
192
207
|
(testCase) => formatWorkflowCommand({
|
|
193
|
-
command
|
|
208
|
+
command,
|
|
194
209
|
properties: {
|
|
195
210
|
file: testCase.displayFile,
|
|
196
211
|
line: String(testCase.location.line),
|
|
@@ -206,11 +221,12 @@ function buildCheckAnnotations(report, options = {}) {
|
|
|
206
221
|
options.maxAnnotations ?? DEFAULT_MAX_CHECK_ANNOTATIONS,
|
|
207
222
|
DEFAULT_MAX_CHECK_ANNOTATIONS
|
|
208
223
|
);
|
|
224
|
+
const annotationLevel = caseAnnotationLevel(options.gate);
|
|
209
225
|
return report.failures.filter(hasAnnotationLocation).slice(0, maxAnnotations).map((testCase) => ({
|
|
210
226
|
path: testCase.displayFile,
|
|
211
227
|
start_line: testCase.location.line,
|
|
212
228
|
end_line: testCase.location.line,
|
|
213
|
-
annotation_level:
|
|
229
|
+
annotation_level: annotationLevel,
|
|
214
230
|
title: truncate(
|
|
215
231
|
`${testCase.primaryFailure?.judgeName ?? "vitest-evals"} - ${testCase.displayName}`,
|
|
216
232
|
255
|
|
@@ -222,6 +238,12 @@ function buildCheckAnnotations(report, options = {}) {
|
|
|
222
238
|
raw_details: truncate(formatRawDetails(testCase), MAX_CHECK_FIELD_LENGTH)
|
|
223
239
|
}));
|
|
224
240
|
}
|
|
241
|
+
function caseAnnotationCommand(gate) {
|
|
242
|
+
return gate?.ok ? "warning" : "error";
|
|
243
|
+
}
|
|
244
|
+
function caseAnnotationLevel(gate) {
|
|
245
|
+
return gate?.ok ? "warning" : "failure";
|
|
246
|
+
}
|
|
225
247
|
function hasAnnotationLocation(testCase) {
|
|
226
248
|
return Boolean(testCase.location);
|
|
227
249
|
}
|
|
@@ -465,6 +487,164 @@ function toolCallCount(testCase) {
|
|
|
465
487
|
return testCase.toolCalls.length;
|
|
466
488
|
}
|
|
467
489
|
|
|
490
|
+
// src/gate.ts
|
|
491
|
+
function evaluateEvalGate(report, policy = {}) {
|
|
492
|
+
const minPassRate = resolveMinPassRate(policy);
|
|
493
|
+
const minScoreAverage = policy.minScoreAverage;
|
|
494
|
+
const enforced = minPassRate !== void 0 || minScoreAverage !== void 0 || policy.failOnFailures === true;
|
|
495
|
+
const passRate = computePassRate(report);
|
|
496
|
+
const counts = formatEvalCounts(report, passRate);
|
|
497
|
+
if (!enforced) {
|
|
498
|
+
const ok = report.status === "passed";
|
|
499
|
+
return {
|
|
500
|
+
ok,
|
|
501
|
+
status: ok ? "passed" : "failed",
|
|
502
|
+
enforced: false,
|
|
503
|
+
passRate,
|
|
504
|
+
title: defaultCheckTitle(report),
|
|
505
|
+
message: ok ? `eval report passed: ${counts}` : `eval report failed: ${counts}`
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
const nonEvalFailures = Math.max(
|
|
509
|
+
0,
|
|
510
|
+
report.totals.failed - report.totals.evalFailed
|
|
511
|
+
);
|
|
512
|
+
if (nonEvalFailures > 0) {
|
|
513
|
+
return {
|
|
514
|
+
ok: false,
|
|
515
|
+
status: "failed",
|
|
516
|
+
enforced: true,
|
|
517
|
+
passRate,
|
|
518
|
+
title: "Eval report hard failure",
|
|
519
|
+
message: `${formatNumber(nonEvalFailures)} non-eval test failure${nonEvalFailures === 1 ? "" : "s"}; ${counts}`
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (report.totals.evalTotal === 0) {
|
|
523
|
+
return {
|
|
524
|
+
ok: false,
|
|
525
|
+
status: "failed",
|
|
526
|
+
enforced: true,
|
|
527
|
+
passRate: null,
|
|
528
|
+
title: "Eval report hard failure",
|
|
529
|
+
message: "no eval cases were reported"
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
if (report.status === "failed" && report.totals.failed === 0 && report.failures.length === 0) {
|
|
533
|
+
return {
|
|
534
|
+
ok: false,
|
|
535
|
+
status: "failed",
|
|
536
|
+
enforced: true,
|
|
537
|
+
passRate,
|
|
538
|
+
title: "Eval report hard failure",
|
|
539
|
+
message: `vitest run failed without counted test failures; ${counts}`
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (minPassRate !== void 0 && (passRate === null || passRate + Number.EPSILON < minPassRate)) {
|
|
543
|
+
return {
|
|
544
|
+
ok: false,
|
|
545
|
+
status: "failed",
|
|
546
|
+
enforced: true,
|
|
547
|
+
passRate,
|
|
548
|
+
title: `Eval pass rate ${formatPercent(passRate)} \u2014 required ${formatPercent(minPassRate)}`,
|
|
549
|
+
message: `eval pass rate below floor: ${counts}; required >= ${formatPercent(minPassRate)}`
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
if (minScoreAverage !== void 0) {
|
|
553
|
+
const average = report.score?.average;
|
|
554
|
+
if (average === void 0 || !Number.isFinite(average)) {
|
|
555
|
+
return {
|
|
556
|
+
ok: false,
|
|
557
|
+
status: "failed",
|
|
558
|
+
enforced: true,
|
|
559
|
+
passRate,
|
|
560
|
+
title: "Eval score gate failed",
|
|
561
|
+
message: `no score average available; required avg score >= ${formatScore(minScoreAverage)}`
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
if (average + Number.EPSILON < minScoreAverage) {
|
|
565
|
+
return {
|
|
566
|
+
ok: false,
|
|
567
|
+
status: "failed",
|
|
568
|
+
enforced: true,
|
|
569
|
+
passRate,
|
|
570
|
+
title: `Avg score ${formatScore(average)} \u2014 required ${formatScore(minScoreAverage)}`,
|
|
571
|
+
message: `avg score below floor: ${counts}; required avg score >= ${formatScore(minScoreAverage)}`
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
ok: true,
|
|
577
|
+
status: "passed",
|
|
578
|
+
enforced: true,
|
|
579
|
+
passRate,
|
|
580
|
+
title: enforcedPassTitle(report, passRate, minPassRate, minScoreAverage),
|
|
581
|
+
message: `eval gate passed: ${counts}${formatFloorSuffix(minPassRate, minScoreAverage)}`
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
function computePassRate(report) {
|
|
585
|
+
if (report.totals.evalTotal <= 0) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
return report.totals.evalPassed / report.totals.evalTotal;
|
|
589
|
+
}
|
|
590
|
+
function formatPercent(value) {
|
|
591
|
+
if (value == null || !Number.isFinite(value)) {
|
|
592
|
+
return "n/a";
|
|
593
|
+
}
|
|
594
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
595
|
+
}
|
|
596
|
+
function renderGateWorkflowCommand(gate) {
|
|
597
|
+
if (gate.ok || !gate.enforced) {
|
|
598
|
+
return void 0;
|
|
599
|
+
}
|
|
600
|
+
return `::error title=${escapeCommandProperty(gate.title)}::${escapeCommandData(gate.message)}`;
|
|
601
|
+
}
|
|
602
|
+
function resolveMinPassRate(policy) {
|
|
603
|
+
const configured = policy.minPassRate;
|
|
604
|
+
if (policy.failOnFailures) {
|
|
605
|
+
if (configured === void 0) {
|
|
606
|
+
return 1;
|
|
607
|
+
}
|
|
608
|
+
return Math.max(configured, 1);
|
|
609
|
+
}
|
|
610
|
+
return configured;
|
|
611
|
+
}
|
|
612
|
+
function defaultCheckTitle(report) {
|
|
613
|
+
if (report.failures.length === 0 && report.status === "passed") {
|
|
614
|
+
return "No eval failures";
|
|
615
|
+
}
|
|
616
|
+
if (report.failures.length === 0) {
|
|
617
|
+
return "Vitest run failed";
|
|
618
|
+
}
|
|
619
|
+
return `${report.failures.length} eval failure${report.failures.length === 1 ? "" : "s"}`;
|
|
620
|
+
}
|
|
621
|
+
function enforcedPassTitle(report, passRate, minPassRate, minScoreAverage) {
|
|
622
|
+
if (minPassRate !== void 0) {
|
|
623
|
+
return `Eval pass rate ${formatPercent(passRate)} \u2014 floor ${formatPercent(minPassRate)}`;
|
|
624
|
+
}
|
|
625
|
+
if (minScoreAverage !== void 0) {
|
|
626
|
+
return `Avg score ${formatScore(report.score?.average)} \u2014 floor ${formatScore(minScoreAverage)}`;
|
|
627
|
+
}
|
|
628
|
+
return defaultCheckTitle(report);
|
|
629
|
+
}
|
|
630
|
+
function formatEvalCounts(report, passRate) {
|
|
631
|
+
const scoreText = report.score?.average === void 0 ? "n/a" : formatScore(report.score.average);
|
|
632
|
+
const passRateText = passRate === null ? "n/a" : formatPercent(passRate);
|
|
633
|
+
return `${formatNumber(report.totals.evalPassed)}/${formatNumber(
|
|
634
|
+
report.totals.evalTotal
|
|
635
|
+
)} passed (${passRateText}), avg score ${scoreText}`;
|
|
636
|
+
}
|
|
637
|
+
function formatFloorSuffix(minPassRate, minScoreAverage) {
|
|
638
|
+
const parts = [];
|
|
639
|
+
if (minPassRate !== void 0) {
|
|
640
|
+
parts.push(`pass rate floor ${formatPercent(minPassRate)}`);
|
|
641
|
+
}
|
|
642
|
+
if (minScoreAverage !== void 0) {
|
|
643
|
+
parts.push(`avg score floor ${formatScore(minScoreAverage)}`);
|
|
644
|
+
}
|
|
645
|
+
return parts.length === 0 ? "" : `; ${parts.join(", ")}`;
|
|
646
|
+
}
|
|
647
|
+
|
|
468
648
|
// src/summary.ts
|
|
469
649
|
var DEFAULT_MAX_FAILURES = 20;
|
|
470
650
|
var DEFAULT_MAX_REASON_CHARS = 8e3;
|
|
@@ -485,20 +665,22 @@ function renderJobSummary(report, options = {}) {
|
|
|
485
665
|
const lines = [
|
|
486
666
|
"# vitest-evals",
|
|
487
667
|
"",
|
|
488
|
-
...renderSummaryTable(report, nonEvalFailures),
|
|
668
|
+
...renderSummaryTable(report, nonEvalFailures, options.gate),
|
|
489
669
|
"",
|
|
490
670
|
...renderScoreDistribution(report),
|
|
491
671
|
"## Results",
|
|
492
672
|
""
|
|
493
673
|
];
|
|
494
674
|
if (report.failures.length > 0) {
|
|
495
|
-
|
|
675
|
+
const failureHeading = options.gate?.ok === true ? "### Quality Misses" : "### Failures";
|
|
676
|
+
lines.push(failureHeading, "");
|
|
496
677
|
failures.forEach((testCase, index) => {
|
|
497
678
|
lines.push(...renderFailureDetails(testCase, index + 1, options), "");
|
|
498
679
|
});
|
|
499
680
|
if (report.failures.length > failures.length) {
|
|
681
|
+
const omittedLabel = options.gate?.ok === true ? "quality misses" : "failures";
|
|
500
682
|
lines.push(
|
|
501
|
-
`${report.failures.length - failures.length} more
|
|
683
|
+
`${report.failures.length - failures.length} more ${omittedLabel} omitted from this summary.`,
|
|
502
684
|
""
|
|
503
685
|
);
|
|
504
686
|
}
|
|
@@ -514,9 +696,9 @@ function renderJobSummary(report, options = {}) {
|
|
|
514
696
|
function formatCountLine(passed, failed, total) {
|
|
515
697
|
return `${formatNumber(passed)} passed, ${formatNumber(failed)} failed, ${formatNumber(total)} total`;
|
|
516
698
|
}
|
|
517
|
-
function renderSummaryTable(report, nonEvalFailures) {
|
|
699
|
+
function renderSummaryTable(report, nonEvalFailures, gate) {
|
|
518
700
|
const rows = [
|
|
519
|
-
["Status", report.status],
|
|
701
|
+
["Status", gate?.status ?? report.status],
|
|
520
702
|
[
|
|
521
703
|
"Evals",
|
|
522
704
|
formatCountLine(
|
|
@@ -526,9 +708,20 @@ function renderSummaryTable(report, nonEvalFailures) {
|
|
|
526
708
|
)
|
|
527
709
|
]
|
|
528
710
|
];
|
|
711
|
+
if (gate?.passRate !== void 0 && gate.passRate !== null) {
|
|
712
|
+
rows.push(["Pass Rate", formatPercent(gate.passRate)]);
|
|
713
|
+
} else if (report.totals.evalTotal > 0) {
|
|
714
|
+
rows.push([
|
|
715
|
+
"Pass Rate",
|
|
716
|
+
formatPercent(report.totals.evalPassed / report.totals.evalTotal)
|
|
717
|
+
]);
|
|
718
|
+
}
|
|
529
719
|
if (report.score) {
|
|
530
720
|
rows.push(["Score", formatScoreSummary(report.score)]);
|
|
531
721
|
}
|
|
722
|
+
if (gate?.enforced) {
|
|
723
|
+
rows.push(["Gate", gate.message]);
|
|
724
|
+
}
|
|
532
725
|
if (nonEvalFailures > 0) {
|
|
533
726
|
rows.push([
|
|
534
727
|
"Other Failures",
|
|
@@ -805,23 +998,25 @@ async function publishCheckRun(report, options = {}) {
|
|
|
805
998
|
};
|
|
806
999
|
}
|
|
807
1000
|
function buildCheckRunPayload(report, options) {
|
|
1001
|
+
const gate = options.gate ?? evaluateEvalGate(report);
|
|
808
1002
|
const annotations = buildCheckAnnotations(report, {
|
|
809
|
-
maxAnnotations: options.maxAnnotations
|
|
1003
|
+
maxAnnotations: options.maxAnnotations,
|
|
1004
|
+
gate
|
|
810
1005
|
});
|
|
811
|
-
const title = report.failures.length === 0 && report.status === "passed" ? "No eval failures" : report.failures.length === 0 ? "Vitest run failed" : `${report.failures.length} eval failure${report.failures.length === 1 ? "" : "s"}`;
|
|
812
1006
|
return {
|
|
813
1007
|
status: "completed",
|
|
814
|
-
conclusion:
|
|
1008
|
+
conclusion: gate.ok ? "success" : "failure",
|
|
815
1009
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
816
1010
|
output: {
|
|
817
|
-
title,
|
|
1011
|
+
title: gate.title,
|
|
818
1012
|
summary: truncateCheckSummary(
|
|
819
1013
|
renderJobSummary(report, {
|
|
820
1014
|
...options,
|
|
821
1015
|
maxFailures: options.maxFailures ?? 5,
|
|
822
1016
|
maxReasonChars: options.maxReasonChars ?? 4e3,
|
|
823
1017
|
maxOutputChars: options.maxOutputChars ?? 2e3,
|
|
824
|
-
maxToolCalls: options.maxToolCalls ?? 10
|
|
1018
|
+
maxToolCalls: options.maxToolCalls ?? 10,
|
|
1019
|
+
gate
|
|
825
1020
|
})
|
|
826
1021
|
),
|
|
827
1022
|
annotations
|
|
@@ -920,11 +1115,17 @@ async function publishEvalReport(options) {
|
|
|
920
1115
|
})
|
|
921
1116
|
);
|
|
922
1117
|
const report = mergeEvalReports(reports);
|
|
1118
|
+
const gate = evaluateEvalGate(report, {
|
|
1119
|
+
failOnFailures: options.failOnFailures,
|
|
1120
|
+
minPassRate: options.minPassRate,
|
|
1121
|
+
minScoreAverage: options.minScoreAverage
|
|
1122
|
+
});
|
|
923
1123
|
const summary = renderJobSummary(report, {
|
|
924
1124
|
maxFailures: options.maxFailures,
|
|
925
1125
|
maxOutputChars: options.maxOutputChars,
|
|
926
1126
|
maxReasonChars: options.maxReasonChars,
|
|
927
|
-
maxToolCalls: options.maxToolCalls
|
|
1127
|
+
maxToolCalls: options.maxToolCalls,
|
|
1128
|
+
gate
|
|
928
1129
|
});
|
|
929
1130
|
if (options.summaryEnabled !== false) {
|
|
930
1131
|
if (options.summaryPath) {
|
|
@@ -935,8 +1136,13 @@ async function publishEvalReport(options) {
|
|
|
935
1136
|
}
|
|
936
1137
|
}
|
|
937
1138
|
if (options.annotations) {
|
|
1139
|
+
const gateCommand = renderGateWorkflowCommand(gate);
|
|
1140
|
+
if (gateCommand) {
|
|
1141
|
+
console.log(gateCommand);
|
|
1142
|
+
}
|
|
938
1143
|
for (const command of renderWorkflowCommands(report, {
|
|
939
|
-
maxAnnotations: options.maxAnnotations
|
|
1144
|
+
maxAnnotations: options.maxAnnotations,
|
|
1145
|
+
gate
|
|
940
1146
|
})) {
|
|
941
1147
|
console.log(command);
|
|
942
1148
|
}
|
|
@@ -954,7 +1160,8 @@ async function publishEvalReport(options) {
|
|
|
954
1160
|
name: options.checkName,
|
|
955
1161
|
repository: options.repository,
|
|
956
1162
|
sha: options.sha,
|
|
957
|
-
token: options.token
|
|
1163
|
+
token: options.token,
|
|
1164
|
+
gate
|
|
958
1165
|
});
|
|
959
1166
|
if (checkRun.status === "skipped") {
|
|
960
1167
|
options.warn?.(`GitHub Check Run skipped: ${checkRun.reason}`);
|
|
@@ -970,6 +1177,8 @@ async function publishEvalReport(options) {
|
|
|
970
1177
|
return {
|
|
971
1178
|
report,
|
|
972
1179
|
resultFiles,
|
|
1180
|
+
gate,
|
|
1181
|
+
shouldFail: gate.enforced && !gate.ok,
|
|
973
1182
|
checkRun
|
|
974
1183
|
};
|
|
975
1184
|
}
|
|
@@ -996,6 +1205,9 @@ async function main() {
|
|
|
996
1205
|
checkRunId: options.checkRunId,
|
|
997
1206
|
checkName: options.checkName,
|
|
998
1207
|
failOnCheckError: options.failOnCheckError,
|
|
1208
|
+
failOnFailures: options.failOnFailures,
|
|
1209
|
+
minPassRate: options.minPassRate,
|
|
1210
|
+
minScoreAverage: options.minScoreAverage,
|
|
999
1211
|
maxAnnotations: options.maxAnnotations,
|
|
1000
1212
|
maxFailures: options.maxFailures,
|
|
1001
1213
|
repository: options.repository,
|
|
@@ -1003,7 +1215,8 @@ async function main() {
|
|
|
1003
1215
|
token: options.token,
|
|
1004
1216
|
warn
|
|
1005
1217
|
});
|
|
1006
|
-
if (
|
|
1218
|
+
if (result.shouldFail) {
|
|
1219
|
+
console.error(result.gate.message);
|
|
1007
1220
|
process.exitCode = 1;
|
|
1008
1221
|
}
|
|
1009
1222
|
}
|
|
@@ -1025,7 +1238,9 @@ function usage() {
|
|
|
1025
1238
|
" --annotations Emit GitHub workflow-command annotations",
|
|
1026
1239
|
" --no-annotations Disable workflow-command annotations",
|
|
1027
1240
|
" --check-run Publish a GitHub Check Run when configured",
|
|
1028
|
-
" --fail-on-failures Exit non-zero when
|
|
1241
|
+
" --fail-on-failures Exit non-zero when any eval case failed",
|
|
1242
|
+
" --min-pass-rate <0-1> Exit non-zero when eval pass rate is below this floor",
|
|
1243
|
+
" --min-score-average <0-1> Exit non-zero when average score is below this floor",
|
|
1029
1244
|
" --fail-on-check-error Fail when Check Run publishing fails",
|
|
1030
1245
|
" --check-run-id <id> Update an existing Check Run",
|
|
1031
1246
|
" --check-name <name> Check Run name (default: vitest-evals)",
|