@vitest-evals/github-reporter 0.15.0 → 0.16.1

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/index.js CHANGED
@@ -22,9 +22,16 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  buildCheckAnnotations: () => buildCheckAnnotations,
24
24
  collectEvalReport: () => collectEvalReport,
25
+ computePassRate: () => computePassRate,
26
+ evaluateEvalGate: () => evaluateEvalGate,
27
+ formatPercent: () => formatPercent,
25
28
  publishCheckRun: () => publishCheckRun,
29
+ publishEvalReport: () => publishEvalReport,
30
+ renderGateWorkflowCommand: () => renderGateWorkflowCommand,
26
31
  renderJobSummary: () => renderJobSummary,
27
- renderWorkflowCommands: () => renderWorkflowCommands
32
+ renderWorkflowCommands: () => renderWorkflowCommands,
33
+ resolveCheckDetailsUrl: () => resolveCheckDetailsUrl,
34
+ resolveCheckSha: () => resolveCheckSha
28
35
  });
29
36
  module.exports = __toCommonJS(index_exports);
30
37
 
@@ -297,9 +304,10 @@ var DEFAULT_MAX_CHECK_ANNOTATIONS = 50;
297
304
  var MAX_CHECK_FIELD_LENGTH = 64e3;
298
305
  function renderWorkflowCommands(report, options = {}) {
299
306
  const maxAnnotations = options.maxAnnotations ?? DEFAULT_MAX_WORKFLOW_ANNOTATIONS;
307
+ const command = caseAnnotationCommand(options.gate);
300
308
  return report.failures.filter(hasAnnotationLocation).slice(0, maxAnnotations).map(
301
309
  (testCase) => formatWorkflowCommand({
302
- command: "error",
310
+ command,
303
311
  properties: {
304
312
  file: testCase.displayFile,
305
313
  line: String(testCase.location.line),
@@ -315,11 +323,12 @@ function buildCheckAnnotations(report, options = {}) {
315
323
  options.maxAnnotations ?? DEFAULT_MAX_CHECK_ANNOTATIONS,
316
324
  DEFAULT_MAX_CHECK_ANNOTATIONS
317
325
  );
326
+ const annotationLevel = caseAnnotationLevel(options.gate);
318
327
  return report.failures.filter(hasAnnotationLocation).slice(0, maxAnnotations).map((testCase) => ({
319
328
  path: testCase.displayFile,
320
329
  start_line: testCase.location.line,
321
330
  end_line: testCase.location.line,
322
- annotation_level: "failure",
331
+ annotation_level: annotationLevel,
323
332
  title: truncate(
324
333
  `${testCase.primaryFailure?.judgeName ?? "vitest-evals"} - ${testCase.displayName}`,
325
334
  255
@@ -331,6 +340,12 @@ function buildCheckAnnotations(report, options = {}) {
331
340
  raw_details: truncate(formatRawDetails(testCase), MAX_CHECK_FIELD_LENGTH)
332
341
  }));
333
342
  }
343
+ function caseAnnotationCommand(gate) {
344
+ return gate?.ok ? "warning" : "error";
345
+ }
346
+ function caseAnnotationLevel(gate) {
347
+ return gate?.ok ? "warning" : "failure";
348
+ }
334
349
  function hasAnnotationLocation(testCase) {
335
350
  return Boolean(testCase.location);
336
351
  }
@@ -390,6 +405,167 @@ function formatWorkflowCommand({
390
405
  return `::${command} ${renderedProperties}::${escapeCommandData(message)}`;
391
406
  }
392
407
 
408
+ // src/gate.ts
409
+ function evaluateEvalGate(report, policy = {}) {
410
+ const minPassRate = resolveMinPassRate(policy);
411
+ const minScoreAverage = policy.minScoreAverage;
412
+ const enforced = minPassRate !== void 0 || minScoreAverage !== void 0 || policy.failOnFailures === true;
413
+ const passRate = computePassRate(report);
414
+ const counts = formatEvalCounts(report, passRate);
415
+ if (!enforced) {
416
+ const ok = report.status === "passed";
417
+ return {
418
+ ok,
419
+ status: ok ? "passed" : "failed",
420
+ enforced: false,
421
+ passRate,
422
+ title: defaultCheckTitle(report),
423
+ message: ok ? `eval report passed: ${counts}` : `eval report failed: ${counts}`
424
+ };
425
+ }
426
+ const nonEvalFailures = Math.max(
427
+ 0,
428
+ report.totals.failed - report.totals.evalFailed
429
+ );
430
+ if (nonEvalFailures > 0) {
431
+ return {
432
+ ok: false,
433
+ status: "failed",
434
+ enforced: true,
435
+ passRate,
436
+ title: "Eval report hard failure",
437
+ message: `${formatNumber(nonEvalFailures)} non-eval test failure${nonEvalFailures === 1 ? "" : "s"}; ${counts}`
438
+ };
439
+ }
440
+ if (report.totals.evalTotal === 0) {
441
+ return {
442
+ ok: false,
443
+ status: "failed",
444
+ enforced: true,
445
+ passRate: null,
446
+ title: "Eval report hard failure",
447
+ message: "no eval cases were reported"
448
+ };
449
+ }
450
+ if (report.status === "failed" && report.totals.failed === 0 && report.failures.length === 0) {
451
+ return {
452
+ ok: false,
453
+ status: "failed",
454
+ enforced: true,
455
+ passRate,
456
+ title: "Eval report hard failure",
457
+ message: `vitest run failed without counted test failures; ${counts}`
458
+ };
459
+ }
460
+ if (minPassRate !== void 0 && (passRate === null || passRate + Number.EPSILON < minPassRate)) {
461
+ return {
462
+ ok: false,
463
+ status: "failed",
464
+ enforced: true,
465
+ passRate,
466
+ title: `Eval pass rate ${formatPercent(passRate)} \u2014 required ${formatPercent(minPassRate)}`,
467
+ message: `eval pass rate below floor: ${counts}; required >= ${formatPercent(minPassRate)}`
468
+ };
469
+ }
470
+ if (minScoreAverage !== void 0) {
471
+ const average = report.score?.average;
472
+ if (average === void 0 || !Number.isFinite(average)) {
473
+ return {
474
+ ok: false,
475
+ status: "failed",
476
+ enforced: true,
477
+ passRate,
478
+ title: "Eval score gate failed",
479
+ message: `no score average available; required avg score >= ${formatScore(minScoreAverage)}`
480
+ };
481
+ }
482
+ if (average + Number.EPSILON < minScoreAverage) {
483
+ return {
484
+ ok: false,
485
+ status: "failed",
486
+ enforced: true,
487
+ passRate,
488
+ title: `Avg score ${formatScore(average)} \u2014 required ${formatScore(minScoreAverage)}`,
489
+ message: `avg score below floor: ${counts}; required avg score >= ${formatScore(minScoreAverage)}`
490
+ };
491
+ }
492
+ }
493
+ return {
494
+ ok: true,
495
+ status: "passed",
496
+ enforced: true,
497
+ passRate,
498
+ title: enforcedPassTitle(report, passRate, minPassRate, minScoreAverage),
499
+ message: `eval gate passed: ${counts}${formatFloorSuffix(minPassRate, minScoreAverage)}`
500
+ };
501
+ }
502
+ function computePassRate(report) {
503
+ if (report.totals.evalTotal <= 0) {
504
+ return null;
505
+ }
506
+ return report.totals.evalPassed / report.totals.evalTotal;
507
+ }
508
+ function formatPercent(value) {
509
+ if (value == null || !Number.isFinite(value)) {
510
+ return "n/a";
511
+ }
512
+ return `${(value * 100).toFixed(1)}%`;
513
+ }
514
+ function renderGateWorkflowCommand(gate) {
515
+ if (gate.ok || !gate.enforced) {
516
+ return void 0;
517
+ }
518
+ return `::error title=${escapeCommandProperty(gate.title)}::${escapeCommandData(gate.message)}`;
519
+ }
520
+ function resolveMinPassRate(policy) {
521
+ const configured = policy.minPassRate;
522
+ if (policy.failOnFailures) {
523
+ if (configured === void 0) {
524
+ return 1;
525
+ }
526
+ return Math.max(configured, 1);
527
+ }
528
+ return configured;
529
+ }
530
+ function defaultCheckTitle(report) {
531
+ if (report.failures.length === 0 && report.status === "passed") {
532
+ return "No eval failures";
533
+ }
534
+ if (report.failures.length === 0) {
535
+ return "Vitest run failed";
536
+ }
537
+ return `${report.failures.length} eval failure${report.failures.length === 1 ? "" : "s"}`;
538
+ }
539
+ function enforcedPassTitle(report, passRate, minPassRate, minScoreAverage) {
540
+ if (minPassRate !== void 0) {
541
+ return `Eval pass rate ${formatPercent(passRate)} \u2014 floor ${formatPercent(minPassRate)}`;
542
+ }
543
+ if (minScoreAverage !== void 0) {
544
+ return `Avg score ${formatScore(report.score?.average)} \u2014 floor ${formatScore(minScoreAverage)}`;
545
+ }
546
+ return defaultCheckTitle(report);
547
+ }
548
+ function formatEvalCounts(report, passRate) {
549
+ const scoreText = report.score?.average === void 0 ? "n/a" : formatScore(report.score.average);
550
+ const passRateText = passRate === null ? "n/a" : formatPercent(passRate);
551
+ return `${formatNumber(report.totals.evalPassed)}/${formatNumber(
552
+ report.totals.evalTotal
553
+ )} passed (${passRateText}), avg score ${scoreText}`;
554
+ }
555
+ function formatFloorSuffix(minPassRate, minScoreAverage) {
556
+ const parts = [];
557
+ if (minPassRate !== void 0) {
558
+ parts.push(`pass rate floor ${formatPercent(minPassRate)}`);
559
+ }
560
+ if (minScoreAverage !== void 0) {
561
+ parts.push(`avg score floor ${formatScore(minScoreAverage)}`);
562
+ }
563
+ return parts.length === 0 ? "" : `; ${parts.join(", ")}`;
564
+ }
565
+
566
+ // src/github.ts
567
+ var import_node_fs = require("fs");
568
+
393
569
  // src/summary.ts
394
570
  var DEFAULT_MAX_FAILURES = 20;
395
571
  var DEFAULT_MAX_REASON_CHARS = 8e3;
@@ -410,20 +586,22 @@ function renderJobSummary(report, options = {}) {
410
586
  const lines = [
411
587
  "# vitest-evals",
412
588
  "",
413
- ...renderSummaryTable(report, nonEvalFailures),
589
+ ...renderSummaryTable(report, nonEvalFailures, options.gate),
414
590
  "",
415
591
  ...renderScoreDistribution(report),
416
592
  "## Results",
417
593
  ""
418
594
  ];
419
595
  if (report.failures.length > 0) {
420
- lines.push("### Failures", "");
596
+ const failureHeading = options.gate?.ok === true ? "### Quality Misses" : "### Failures";
597
+ lines.push(failureHeading, "");
421
598
  failures.forEach((testCase, index) => {
422
599
  lines.push(...renderFailureDetails(testCase, index + 1, options), "");
423
600
  });
424
601
  if (report.failures.length > failures.length) {
602
+ const omittedLabel = options.gate?.ok === true ? "quality misses" : "failures";
425
603
  lines.push(
426
- `${report.failures.length - failures.length} more failures omitted from this summary.`,
604
+ `${report.failures.length - failures.length} more ${omittedLabel} omitted from this summary.`,
427
605
  ""
428
606
  );
429
607
  }
@@ -439,9 +617,9 @@ function renderJobSummary(report, options = {}) {
439
617
  function formatCountLine(passed, failed, total) {
440
618
  return `${formatNumber(passed)} passed, ${formatNumber(failed)} failed, ${formatNumber(total)} total`;
441
619
  }
442
- function renderSummaryTable(report, nonEvalFailures) {
620
+ function renderSummaryTable(report, nonEvalFailures, gate) {
443
621
  const rows = [
444
- ["Status", report.status],
622
+ ["Status", gate?.status ?? report.status],
445
623
  [
446
624
  "Evals",
447
625
  formatCountLine(
@@ -451,9 +629,20 @@ function renderSummaryTable(report, nonEvalFailures) {
451
629
  )
452
630
  ]
453
631
  ];
632
+ if (gate?.passRate !== void 0 && gate.passRate !== null) {
633
+ rows.push(["Pass Rate", formatPercent(gate.passRate)]);
634
+ } else if (report.totals.evalTotal > 0) {
635
+ rows.push([
636
+ "Pass Rate",
637
+ formatPercent(report.totals.evalPassed / report.totals.evalTotal)
638
+ ]);
639
+ }
454
640
  if (report.score) {
455
641
  rows.push(["Score", formatScoreSummary(report.score)]);
456
642
  }
643
+ if (gate?.enforced) {
644
+ rows.push(["Gate", gate.message]);
645
+ }
457
646
  if (nonEvalFailures > 0) {
458
647
  rows.push([
459
648
  "Other Failures",
@@ -677,10 +866,44 @@ function formatCaseUsage(testCase) {
677
866
  var DEFAULT_CHECK_NAME = "vitest-evals";
678
867
  var MAX_CHECK_SUMMARY_LENGTH = 64e3;
679
868
  var CHECK_SUMMARY_TRUNCATION_SUFFIX = "\n\n[truncated for GitHub Check Run]\n";
869
+ function resolveCheckSha(env = process.env, options = {}) {
870
+ const explicit = options.sha?.trim() || env.GITHUB_PR_HEAD_SHA?.trim();
871
+ if (explicit) {
872
+ return explicit;
873
+ }
874
+ const eventPath = options.eventPath?.trim() || env.GITHUB_EVENT_PATH?.trim();
875
+ if (eventPath) {
876
+ try {
877
+ const event = JSON.parse((0, import_node_fs.readFileSync)(eventPath, "utf8"));
878
+ const headSha = event.pull_request?.head?.sha;
879
+ if (typeof headSha === "string" && headSha.trim()) {
880
+ return headSha.trim();
881
+ }
882
+ } catch {
883
+ }
884
+ }
885
+ return env.GITHUB_SHA?.trim() || void 0;
886
+ }
887
+ function resolveCheckDetailsUrl(env = process.env, options = {}) {
888
+ const explicit = options.detailsUrl?.trim();
889
+ if (explicit) {
890
+ return explicit;
891
+ }
892
+ const server = env.GITHUB_SERVER_URL?.replace(/\/$/, "");
893
+ const repository = env.GITHUB_REPOSITORY?.trim();
894
+ const runId = env.GITHUB_RUN_ID?.trim();
895
+ if (!server || !repository || !runId) {
896
+ return void 0;
897
+ }
898
+ return `${server}/${repository}/actions/runs/${runId}`;
899
+ }
680
900
  async function publishCheckRun(report, options = {}) {
681
901
  const token = options.token ?? process.env.GITHUB_TOKEN;
682
902
  const repository = options.repository ?? process.env.GITHUB_REPOSITORY;
683
- const sha = options.sha ?? process.env.GITHUB_SHA;
903
+ const sha = resolveCheckSha(process.env, { sha: options.sha });
904
+ const detailsUrl = resolveCheckDetailsUrl(process.env, {
905
+ detailsUrl: options.detailsUrl
906
+ });
684
907
  if (!token) {
685
908
  return { status: "skipped", reason: "missing GITHUB_TOKEN" };
686
909
  }
@@ -688,7 +911,10 @@ async function publishCheckRun(report, options = {}) {
688
911
  return { status: "skipped", reason: "missing GITHUB_REPOSITORY" };
689
912
  }
690
913
  if (!sha && options.checkRunId === void 0) {
691
- return { status: "skipped", reason: "missing GITHUB_SHA" };
914
+ return {
915
+ status: "skipped",
916
+ reason: "missing commit SHA (set --sha / options.sha, GITHUB_PR_HEAD_SHA, pull_request.head.sha, or GITHUB_SHA)"
917
+ };
692
918
  }
693
919
  const [owner, repo] = repository.split("/");
694
920
  if (!owner || !repo) {
@@ -697,7 +923,7 @@ async function publishCheckRun(report, options = {}) {
697
923
  reason: `invalid GitHub repository: ${repository}`
698
924
  };
699
925
  }
700
- const payload = buildCheckRunPayload(report, options);
926
+ const payload = buildCheckRunPayload(report, options, detailsUrl);
701
927
  const apiUrl = options.apiUrl ?? process.env.GITHUB_API_URL ?? "https://api.github.com";
702
928
  const requestUrl = options.checkRunId === void 0 ? `${apiUrl}/repos/${owner}/${repo}/check-runs` : `${apiUrl}/repos/${owner}/${repo}/check-runs/${options.checkRunId}`;
703
929
  const response = await fetch(requestUrl, {
@@ -712,6 +938,7 @@ async function publishCheckRun(report, options = {}) {
712
938
  options.checkRunId === void 0 ? {
713
939
  name: options.name ?? DEFAULT_CHECK_NAME,
714
940
  head_sha: sha,
941
+ ...options.externalId ? { external_id: options.externalId } : {},
715
942
  ...payload
716
943
  } : payload
717
944
  )
@@ -726,27 +953,31 @@ async function publishCheckRun(report, options = {}) {
726
953
  return {
727
954
  status: options.checkRunId === void 0 ? "created" : "updated",
728
955
  id: data.id,
729
- htmlUrl: data.html_url
956
+ htmlUrl: data.html_url,
957
+ sha
730
958
  };
731
959
  }
732
- function buildCheckRunPayload(report, options) {
960
+ function buildCheckRunPayload(report, options, detailsUrl) {
961
+ const gate = options.gate ?? evaluateEvalGate(report);
733
962
  const annotations = buildCheckAnnotations(report, {
734
- maxAnnotations: options.maxAnnotations
963
+ maxAnnotations: options.maxAnnotations,
964
+ gate
735
965
  });
736
- 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"}`;
737
966
  return {
738
967
  status: "completed",
739
- conclusion: report.status === "passed" ? "success" : "failure",
968
+ conclusion: gate.ok ? "success" : "failure",
740
969
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
970
+ ...detailsUrl ? { details_url: detailsUrl } : {},
741
971
  output: {
742
- title,
972
+ title: gate.title,
743
973
  summary: truncateCheckSummary(
744
974
  renderJobSummary(report, {
745
975
  ...options,
746
976
  maxFailures: options.maxFailures ?? 5,
747
977
  maxReasonChars: options.maxReasonChars ?? 4e3,
748
978
  maxOutputChars: options.maxOutputChars ?? 2e3,
749
- maxToolCalls: options.maxToolCalls ?? 10
979
+ maxToolCalls: options.maxToolCalls ?? 10,
980
+ gate
750
981
  })
751
982
  ),
752
983
  annotations
@@ -759,12 +990,189 @@ function truncateCheckSummary(summary) {
759
990
  }
760
991
  return `${summary.slice(0, MAX_CHECK_SUMMARY_LENGTH - CHECK_SUMMARY_TRUNCATION_SUFFIX.length).trimEnd()}${CHECK_SUMMARY_TRUNCATION_SUFFIX}`;
761
992
  }
993
+
994
+ // src/report.ts
995
+ var import_promises = require("fs/promises");
996
+ var import_node = require("@vitest-evals/core/node");
997
+
998
+ // src/merge.ts
999
+ function mergeEvalReports(reports) {
1000
+ const cases = reports.flatMap((report) => report.cases);
1001
+ const failures = reports.flatMap((report) => report.failures);
1002
+ const scoredCases = cases.map((testCase) => testCase.eval?.avgScore).filter(
1003
+ (score) => typeof score === "number" && Number.isFinite(score)
1004
+ );
1005
+ const startedAtValues = reports.map((report) => report.startedAt).filter(
1006
+ (startedAt2) => typeof startedAt2 === "number" && Number.isFinite(startedAt2)
1007
+ );
1008
+ const startedAt = startedAtValues.length > 0 ? Math.min(...startedAtValues) : void 0;
1009
+ return {
1010
+ status: reports.some((report) => report.status === "failed") ? "failed" : "passed",
1011
+ startedAt,
1012
+ durationMs: mergeDuration(reports),
1013
+ totals: {
1014
+ total: sum(reports, (report) => report.totals.total),
1015
+ passed: sum(reports, (report) => report.totals.passed),
1016
+ failed: sum(reports, (report) => report.totals.failed),
1017
+ skipped: sum(reports, (report) => report.totals.skipped),
1018
+ evalTotal: sum(reports, (report) => report.totals.evalTotal),
1019
+ evalPassed: sum(reports, (report) => report.totals.evalPassed),
1020
+ evalFailed: sum(reports, (report) => report.totals.evalFailed)
1021
+ },
1022
+ score: scoredCases.length > 0 ? {
1023
+ average: scoredCases.reduce((total, score) => total + score, 0) / scoredCases.length,
1024
+ minimum: Math.min(...scoredCases)
1025
+ } : void 0,
1026
+ usage: mergeUsage(reports.map((report) => report.usage)),
1027
+ cases,
1028
+ failures
1029
+ };
1030
+ }
1031
+ function mergeUsage(usages) {
1032
+ return {
1033
+ inputTokens: sum(usages, (usage) => usage.inputTokens),
1034
+ outputTokens: sum(usages, (usage) => usage.outputTokens),
1035
+ reasoningTokens: sum(usages, (usage) => usage.reasoningTokens),
1036
+ totalTokens: sum(usages, (usage) => usage.totalTokens),
1037
+ toolCalls: sum(usages, (usage) => usage.toolCalls)
1038
+ };
1039
+ }
1040
+ function mergeDuration(reports) {
1041
+ const durations = reports.map((report) => report.durationMs).filter(
1042
+ (durationMs) => typeof durationMs === "number" && Number.isFinite(durationMs)
1043
+ );
1044
+ const intervals = reports.map((report) => {
1045
+ if (typeof report.startedAt !== "number" || !Number.isFinite(report.startedAt) || typeof report.durationMs !== "number" || !Number.isFinite(report.durationMs)) {
1046
+ return void 0;
1047
+ }
1048
+ return {
1049
+ start: report.startedAt,
1050
+ end: report.startedAt + report.durationMs
1051
+ };
1052
+ }).filter(
1053
+ (interval) => Boolean(interval)
1054
+ );
1055
+ if (intervals.length > 0 && intervals.length === durations.length) {
1056
+ return Math.max(...intervals.map((interval) => interval.end)) - Math.min(...intervals.map((interval) => interval.start));
1057
+ }
1058
+ return durations.length > 0 ? durations.reduce((total, durationMs) => total + durationMs, 0) : void 0;
1059
+ }
1060
+ function sum(items, select) {
1061
+ return items.reduce((total, item) => total + select(item), 0);
1062
+ }
1063
+
1064
+ // src/report.ts
1065
+ async function publishEvalReport(options) {
1066
+ const resultFiles = await (0, import_node.resolveResultFiles)(options.resultPatterns, {
1067
+ cwd: options.cwd
1068
+ });
1069
+ if (resultFiles.length === 0) {
1070
+ throw new Error(
1071
+ `No eval result files matched: ${options.resultPatterns.join(", ")}`
1072
+ );
1073
+ }
1074
+ const reports = await Promise.all(
1075
+ resultFiles.map(async (resultFile) => {
1076
+ const json = await (0, import_node.readVitestJsonReportFile)(resultFile);
1077
+ return collectEvalReport(json, {
1078
+ workspace: options.workspace
1079
+ });
1080
+ })
1081
+ );
1082
+ const report = mergeEvalReports(reports);
1083
+ const gate = evaluateEvalGate(report, {
1084
+ failOnFailures: options.failOnFailures,
1085
+ minPassRate: options.minPassRate,
1086
+ minScoreAverage: options.minScoreAverage
1087
+ });
1088
+ const summary = renderJobSummary(report, {
1089
+ maxFailures: options.maxFailures,
1090
+ maxOutputChars: options.maxOutputChars,
1091
+ maxReasonChars: options.maxReasonChars,
1092
+ maxToolCalls: options.maxToolCalls,
1093
+ gate
1094
+ });
1095
+ if (options.summaryEnabled !== false) {
1096
+ if (options.summaryPath) {
1097
+ await (0, import_promises.appendFile)(options.summaryPath, `${summary}
1098
+ `);
1099
+ } else {
1100
+ console.log(summary);
1101
+ }
1102
+ }
1103
+ if (options.annotations) {
1104
+ const gateCommand = renderGateWorkflowCommand(gate);
1105
+ if (gateCommand) {
1106
+ console.log(gateCommand);
1107
+ }
1108
+ for (const command of renderWorkflowCommands(report, {
1109
+ maxAnnotations: options.maxAnnotations,
1110
+ gate
1111
+ })) {
1112
+ console.log(command);
1113
+ }
1114
+ }
1115
+ let checkRun;
1116
+ if (options.checkRun) {
1117
+ try {
1118
+ checkRun = await publishCheckRun(report, {
1119
+ checkRunId: options.checkRunId,
1120
+ maxAnnotations: options.maxAnnotations,
1121
+ maxFailures: options.maxFailures,
1122
+ maxOutputChars: options.maxOutputChars,
1123
+ maxReasonChars: options.maxReasonChars,
1124
+ maxToolCalls: options.maxToolCalls,
1125
+ name: options.checkName,
1126
+ repository: options.repository,
1127
+ sha: options.sha,
1128
+ detailsUrl: options.detailsUrl,
1129
+ externalId: options.externalId,
1130
+ token: options.token,
1131
+ gate
1132
+ });
1133
+ if (checkRun.status === "skipped") {
1134
+ options.warn?.(`GitHub Check Run skipped: ${checkRun.reason}`);
1135
+ } else if (checkRun.htmlUrl) {
1136
+ console.log(`published check run: ${checkRun.htmlUrl}`);
1137
+ } else if (checkRun.id !== void 0) {
1138
+ console.log(`published check run id: ${checkRun.id}`);
1139
+ }
1140
+ } catch (error) {
1141
+ const message = error instanceof Error ? error.message : String(error);
1142
+ if (options.failOnCheckError) {
1143
+ throw error;
1144
+ }
1145
+ options.warn?.(message);
1146
+ }
1147
+ }
1148
+ const gateFailed = gate.enforced && !gate.ok;
1149
+ const wantsSoftFail = options.softFail ?? options.checkRun === true;
1150
+ const softFail = wantsSoftFail && checkRunPublished(checkRun);
1151
+ const shouldFail = gateFailed && !softFail;
1152
+ return {
1153
+ report,
1154
+ resultFiles,
1155
+ gate,
1156
+ shouldFail,
1157
+ checkRun
1158
+ };
1159
+ }
1160
+ function checkRunPublished(checkRun) {
1161
+ return checkRun?.status === "created" || checkRun?.status === "updated";
1162
+ }
762
1163
  // Annotate the CommonJS export names for ESM import in node:
763
1164
  0 && (module.exports = {
764
1165
  buildCheckAnnotations,
765
1166
  collectEvalReport,
1167
+ computePassRate,
1168
+ evaluateEvalGate,
1169
+ formatPercent,
766
1170
  publishCheckRun,
1171
+ publishEvalReport,
1172
+ renderGateWorkflowCommand,
767
1173
  renderJobSummary,
768
- renderWorkflowCommands
1174
+ renderWorkflowCommands,
1175
+ resolveCheckDetailsUrl,
1176
+ resolveCheckSha
769
1177
  });
770
1178
  //# sourceMappingURL=index.js.map