@vitest-evals/github-reporter 0.16.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/README.md +16 -3
- package/dist/cli.js +72 -7
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +72 -7
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +34 -3
- package/dist/index.d.ts +34 -3
- package/dist/index.js +68 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +65 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -31,10 +31,19 @@ steps:
|
|
|
31
31
|
with:
|
|
32
32
|
results: vitest-results.json
|
|
33
33
|
publish-check: true
|
|
34
|
+
min-pass-rate: 0.8
|
|
34
35
|
```
|
|
35
36
|
|
|
37
|
+
On `pull_request`, the Check Run attaches to the PR head SHA (not the temporary
|
|
38
|
+
merge `GITHUB_SHA`) so it shows on the PR checks list. Override with the `sha`
|
|
39
|
+
input when needed.
|
|
40
|
+
|
|
41
|
+
When a Check Run publishes successfully with a gate, the action step soft-fails
|
|
42
|
+
by default so the Check Run owns green/red. If publishing is skipped, the step
|
|
43
|
+
still fails on a rejected gate. Set `soft-fail: false` to also fail the job.
|
|
44
|
+
|
|
36
45
|
If configuration or permission is missing, the action keeps the job summary and
|
|
37
|
-
workflow annotations and warns instead of failing.
|
|
46
|
+
workflow annotations and warns instead of failing solely for the missing check.
|
|
38
47
|
|
|
39
48
|
## Score and Pass-Rate Gates
|
|
40
49
|
|
|
@@ -49,9 +58,11 @@ workflow annotations and warns instead of failing.
|
|
|
49
58
|
|
|
50
59
|
- `fail-on-failures: true` requires every eval case to pass
|
|
51
60
|
- `min-pass-rate` and `min-score-average` set aggregate floors in the `0`-`1` range
|
|
52
|
-
- `status
|
|
61
|
+
- `status` and Check Run conclusion/title follow the gate
|
|
53
62
|
- quality misses become warnings when the gate still passes
|
|
54
63
|
- non-eval / infrastructure failures still fail hard
|
|
64
|
+
- published Check Runs soft-fail the step by default; set `soft-fail: false` to
|
|
65
|
+
also fail the workflow job
|
|
55
66
|
- use `evals-failed` / `pass-rate` for raw tallies (`pass-rate` is a 0-1 ratio)
|
|
56
67
|
|
|
57
68
|
## Sharded Reports
|
|
@@ -80,10 +91,12 @@ final reducer job:
|
|
|
80
91
|
| `results` | `vitest-results.json` | Vitest JSON result files. Supports paths, `*` and `**` globs, and newline-separated entries. |
|
|
81
92
|
| `publish-summary` | `true` | Write a GitHub Actions job summary. |
|
|
82
93
|
| `publish-annotations` | `true` | Emit GitHub workflow annotations for failed evals. |
|
|
83
|
-
| `publish-check` | `false` | Publish one GitHub Check Run for the combined report. |
|
|
94
|
+
| `publish-check` | `false` | Publish one GitHub Check Run for the combined report. Attaches to PR head on `pull_request`. |
|
|
84
95
|
| `check-name` | `vitest-evals` | Name of the GitHub Check Run. |
|
|
85
96
|
| `github-token` | `${{ github.token }}` | Token used for Check Run publishing. |
|
|
97
|
+
| `sha` | PR head, else `GITHUB_SHA` | Commit SHA for the Check Run. |
|
|
86
98
|
| `fail-on-failures` | `false` | Fail the action when any eval case failed. Equivalent to `min-pass-rate: 1`. |
|
|
99
|
+
| `soft-fail` | auto | Keep the step green when a published Check Run owns a failed gate. |
|
|
87
100
|
| `min-pass-rate` | unset | Minimum fraction of eval cases that must pass (`0`-`1`). |
|
|
88
101
|
| `min-score-average` | unset | Minimum average eval score across scored cases (`0`-`1`). |
|
|
89
102
|
| `max-annotations` | unset | Maximum number of failure annotations to publish. Check Run annotations are capped at 50 by GitHub. |
|
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 "--soft-fail":
|
|
45
|
+
options.softFail = true;
|
|
46
|
+
break;
|
|
47
|
+
case "--no-soft-fail":
|
|
48
|
+
options.softFail = false;
|
|
49
|
+
break;
|
|
44
50
|
case "--min-pass-rate":
|
|
45
51
|
options.minPassRate = readRatio(args, ++index, arg);
|
|
46
52
|
break;
|
|
@@ -645,6 +651,9 @@ function formatFloorSuffix(minPassRate, minScoreAverage) {
|
|
|
645
651
|
return parts.length === 0 ? "" : `; ${parts.join(", ")}`;
|
|
646
652
|
}
|
|
647
653
|
|
|
654
|
+
// src/github.ts
|
|
655
|
+
var import_node_fs = require("fs");
|
|
656
|
+
|
|
648
657
|
// src/summary.ts
|
|
649
658
|
var DEFAULT_MAX_FAILURES = 20;
|
|
650
659
|
var DEFAULT_MAX_REASON_CHARS = 8e3;
|
|
@@ -945,10 +954,44 @@ function formatCaseUsage(testCase) {
|
|
|
945
954
|
var DEFAULT_CHECK_NAME = "vitest-evals";
|
|
946
955
|
var MAX_CHECK_SUMMARY_LENGTH = 64e3;
|
|
947
956
|
var CHECK_SUMMARY_TRUNCATION_SUFFIX = "\n\n[truncated for GitHub Check Run]\n";
|
|
957
|
+
function resolveCheckSha(env = process.env, options = {}) {
|
|
958
|
+
const explicit = options.sha?.trim() || env.GITHUB_PR_HEAD_SHA?.trim();
|
|
959
|
+
if (explicit) {
|
|
960
|
+
return explicit;
|
|
961
|
+
}
|
|
962
|
+
const eventPath = options.eventPath?.trim() || env.GITHUB_EVENT_PATH?.trim();
|
|
963
|
+
if (eventPath) {
|
|
964
|
+
try {
|
|
965
|
+
const event = JSON.parse((0, import_node_fs.readFileSync)(eventPath, "utf8"));
|
|
966
|
+
const headSha = event.pull_request?.head?.sha;
|
|
967
|
+
if (typeof headSha === "string" && headSha.trim()) {
|
|
968
|
+
return headSha.trim();
|
|
969
|
+
}
|
|
970
|
+
} catch {
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
return env.GITHUB_SHA?.trim() || void 0;
|
|
974
|
+
}
|
|
975
|
+
function resolveCheckDetailsUrl(env = process.env, options = {}) {
|
|
976
|
+
const explicit = options.detailsUrl?.trim();
|
|
977
|
+
if (explicit) {
|
|
978
|
+
return explicit;
|
|
979
|
+
}
|
|
980
|
+
const server = env.GITHUB_SERVER_URL?.replace(/\/$/, "");
|
|
981
|
+
const repository = env.GITHUB_REPOSITORY?.trim();
|
|
982
|
+
const runId = env.GITHUB_RUN_ID?.trim();
|
|
983
|
+
if (!server || !repository || !runId) {
|
|
984
|
+
return void 0;
|
|
985
|
+
}
|
|
986
|
+
return `${server}/${repository}/actions/runs/${runId}`;
|
|
987
|
+
}
|
|
948
988
|
async function publishCheckRun(report, options = {}) {
|
|
949
989
|
const token = options.token ?? process.env.GITHUB_TOKEN;
|
|
950
990
|
const repository = options.repository ?? process.env.GITHUB_REPOSITORY;
|
|
951
|
-
const sha = options.sha
|
|
991
|
+
const sha = resolveCheckSha(process.env, { sha: options.sha });
|
|
992
|
+
const detailsUrl = resolveCheckDetailsUrl(process.env, {
|
|
993
|
+
detailsUrl: options.detailsUrl
|
|
994
|
+
});
|
|
952
995
|
if (!token) {
|
|
953
996
|
return { status: "skipped", reason: "missing GITHUB_TOKEN" };
|
|
954
997
|
}
|
|
@@ -956,7 +999,10 @@ async function publishCheckRun(report, options = {}) {
|
|
|
956
999
|
return { status: "skipped", reason: "missing GITHUB_REPOSITORY" };
|
|
957
1000
|
}
|
|
958
1001
|
if (!sha && options.checkRunId === void 0) {
|
|
959
|
-
return {
|
|
1002
|
+
return {
|
|
1003
|
+
status: "skipped",
|
|
1004
|
+
reason: "missing commit SHA (set --sha / options.sha, GITHUB_PR_HEAD_SHA, pull_request.head.sha, or GITHUB_SHA)"
|
|
1005
|
+
};
|
|
960
1006
|
}
|
|
961
1007
|
const [owner, repo] = repository.split("/");
|
|
962
1008
|
if (!owner || !repo) {
|
|
@@ -965,7 +1011,7 @@ async function publishCheckRun(report, options = {}) {
|
|
|
965
1011
|
reason: `invalid GitHub repository: ${repository}`
|
|
966
1012
|
};
|
|
967
1013
|
}
|
|
968
|
-
const payload = buildCheckRunPayload(report, options);
|
|
1014
|
+
const payload = buildCheckRunPayload(report, options, detailsUrl);
|
|
969
1015
|
const apiUrl = options.apiUrl ?? process.env.GITHUB_API_URL ?? "https://api.github.com";
|
|
970
1016
|
const requestUrl = options.checkRunId === void 0 ? `${apiUrl}/repos/${owner}/${repo}/check-runs` : `${apiUrl}/repos/${owner}/${repo}/check-runs/${options.checkRunId}`;
|
|
971
1017
|
const response = await fetch(requestUrl, {
|
|
@@ -980,6 +1026,7 @@ async function publishCheckRun(report, options = {}) {
|
|
|
980
1026
|
options.checkRunId === void 0 ? {
|
|
981
1027
|
name: options.name ?? DEFAULT_CHECK_NAME,
|
|
982
1028
|
head_sha: sha,
|
|
1029
|
+
...options.externalId ? { external_id: options.externalId } : {},
|
|
983
1030
|
...payload
|
|
984
1031
|
} : payload
|
|
985
1032
|
)
|
|
@@ -994,10 +1041,11 @@ async function publishCheckRun(report, options = {}) {
|
|
|
994
1041
|
return {
|
|
995
1042
|
status: options.checkRunId === void 0 ? "created" : "updated",
|
|
996
1043
|
id: data.id,
|
|
997
|
-
htmlUrl: data.html_url
|
|
1044
|
+
htmlUrl: data.html_url,
|
|
1045
|
+
sha
|
|
998
1046
|
};
|
|
999
1047
|
}
|
|
1000
|
-
function buildCheckRunPayload(report, options) {
|
|
1048
|
+
function buildCheckRunPayload(report, options, detailsUrl) {
|
|
1001
1049
|
const gate = options.gate ?? evaluateEvalGate(report);
|
|
1002
1050
|
const annotations = buildCheckAnnotations(report, {
|
|
1003
1051
|
maxAnnotations: options.maxAnnotations,
|
|
@@ -1007,6 +1055,7 @@ function buildCheckRunPayload(report, options) {
|
|
|
1007
1055
|
status: "completed",
|
|
1008
1056
|
conclusion: gate.ok ? "success" : "failure",
|
|
1009
1057
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1058
|
+
...detailsUrl ? { details_url: detailsUrl } : {},
|
|
1010
1059
|
output: {
|
|
1011
1060
|
title: gate.title,
|
|
1012
1061
|
summary: truncateCheckSummary(
|
|
@@ -1160,11 +1209,17 @@ async function publishEvalReport(options) {
|
|
|
1160
1209
|
name: options.checkName,
|
|
1161
1210
|
repository: options.repository,
|
|
1162
1211
|
sha: options.sha,
|
|
1212
|
+
detailsUrl: options.detailsUrl,
|
|
1213
|
+
externalId: options.externalId,
|
|
1163
1214
|
token: options.token,
|
|
1164
1215
|
gate
|
|
1165
1216
|
});
|
|
1166
1217
|
if (checkRun.status === "skipped") {
|
|
1167
1218
|
options.warn?.(`GitHub Check Run skipped: ${checkRun.reason}`);
|
|
1219
|
+
} else if (checkRun.htmlUrl) {
|
|
1220
|
+
console.log(`published check run: ${checkRun.htmlUrl}`);
|
|
1221
|
+
} else if (checkRun.id !== void 0) {
|
|
1222
|
+
console.log(`published check run id: ${checkRun.id}`);
|
|
1168
1223
|
}
|
|
1169
1224
|
} catch (error) {
|
|
1170
1225
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1174,14 +1229,21 @@ async function publishEvalReport(options) {
|
|
|
1174
1229
|
options.warn?.(message);
|
|
1175
1230
|
}
|
|
1176
1231
|
}
|
|
1232
|
+
const gateFailed = gate.enforced && !gate.ok;
|
|
1233
|
+
const wantsSoftFail = options.softFail ?? options.checkRun === true;
|
|
1234
|
+
const softFail = wantsSoftFail && checkRunPublished(checkRun);
|
|
1235
|
+
const shouldFail = gateFailed && !softFail;
|
|
1177
1236
|
return {
|
|
1178
1237
|
report,
|
|
1179
1238
|
resultFiles,
|
|
1180
1239
|
gate,
|
|
1181
|
-
shouldFail
|
|
1240
|
+
shouldFail,
|
|
1182
1241
|
checkRun
|
|
1183
1242
|
};
|
|
1184
1243
|
}
|
|
1244
|
+
function checkRunPublished(checkRun) {
|
|
1245
|
+
return checkRun?.status === "created" || checkRun?.status === "updated";
|
|
1246
|
+
}
|
|
1185
1247
|
|
|
1186
1248
|
// src/cli.ts
|
|
1187
1249
|
main().catch((error) => {
|
|
@@ -1206,6 +1268,7 @@ async function main() {
|
|
|
1206
1268
|
checkName: options.checkName,
|
|
1207
1269
|
failOnCheckError: options.failOnCheckError,
|
|
1208
1270
|
failOnFailures: options.failOnFailures,
|
|
1271
|
+
softFail: options.softFail,
|
|
1209
1272
|
minPassRate: options.minPassRate,
|
|
1210
1273
|
minScoreAverage: options.minScoreAverage,
|
|
1211
1274
|
maxAnnotations: options.maxAnnotations,
|
|
@@ -1241,12 +1304,14 @@ function usage() {
|
|
|
1241
1304
|
" --fail-on-failures Exit non-zero when any eval case failed",
|
|
1242
1305
|
" --min-pass-rate <0-1> Exit non-zero when eval pass rate is below this floor",
|
|
1243
1306
|
" --min-score-average <0-1> Exit non-zero when average score is below this floor",
|
|
1307
|
+
" --soft-fail Keep exit 0 when a published Check Run owns gate status",
|
|
1308
|
+
" --no-soft-fail Always exit non-zero when an enforced gate fails",
|
|
1244
1309
|
" --fail-on-check-error Fail when Check Run publishing fails",
|
|
1245
1310
|
" --check-run-id <id> Update an existing Check Run",
|
|
1246
1311
|
" --check-name <name> Check Run name (default: vitest-evals)",
|
|
1247
1312
|
" --token <token> GitHub token (default: GITHUB_TOKEN)",
|
|
1248
1313
|
" --repo <owner/repo> GitHub repository (default: GITHUB_REPOSITORY)",
|
|
1249
|
-
" --sha <sha> Git commit SHA (default: GITHUB_SHA)",
|
|
1314
|
+
" --sha <sha> Git commit SHA (default: PR head, then GITHUB_SHA)",
|
|
1250
1315
|
" --workspace <path> Workspace path for relative annotation files",
|
|
1251
1316
|
" --max-annotations <n> Maximum annotations to emit",
|
|
1252
1317
|
" --max-failures <n> Maximum failures to include in details"
|