@tested/cli 0.1.8 → 0.1.9
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 +6 -6
- package/dist/td.js +56 -21
- package/dist/tested.js +56 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ CI uses the composite Action (`tested-hq/cli/action@main`). It installs `@tested
|
|
|
25
25
|
```yaml
|
|
26
26
|
- uses: tested-hq/cli/action@main
|
|
27
27
|
with:
|
|
28
|
-
version: 0.1.
|
|
28
|
+
version: 0.1.9
|
|
29
29
|
token: ${{ secrets.TESTED_TOKEN }}
|
|
30
30
|
```
|
|
31
31
|
|
|
@@ -132,7 +132,7 @@ If `thresholds` is missing from `.tested.yaml`, `tested check` prints a notice o
|
|
|
132
132
|
|
|
133
133
|
### Flags (per package)
|
|
134
134
|
|
|
135
|
-
Independent floors so a monorepo total cannot hide one package. Each flag is graded from **this run's** coverage files
|
|
135
|
+
Independent floors so a monorepo total cannot hide one package. Each flag is graded from **this run's** coverage files.
|
|
136
136
|
|
|
137
137
|
```yaml
|
|
138
138
|
flags:
|
|
@@ -144,11 +144,11 @@ flags:
|
|
|
144
144
|
paths: ["apps/api/**"]
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
`tested check` applies global `thresholds` plus each flag whose paths appear in the merged coverage. Per-flag patch is new executable lines in those paths. A flag with no files this run is **
|
|
147
|
+
`tested check` applies global `thresholds` plus each flag whose paths appear in the merged coverage. Per-flag patch is new executable lines in those paths. A flag with no files this run is **skipped** (not 0%) — an affected-graph or scoped job that did not collect that package is not a fail.
|
|
148
148
|
|
|
149
|
-
A job already scoped to one package: `tested check --flag frontend` (Action `flag:`). That coverage file **is** the flag.
|
|
149
|
+
A job already scoped to one package: `tested check --flag frontend` (Action `flag:`). That coverage file **is** the flag. Other packages are omitted from this upload.
|
|
150
150
|
|
|
151
|
-
`--json` lists per-flag results (`flags.frontend.patchCheck` → `tested.dev / patch / frontend`). `
|
|
151
|
+
`--json` lists per-flag results (`flags.frontend.patchCheck` → `tested.dev / patch / frontend`). Skipped flags have `status: missing` and `skipped: true` with no `executable` / `pct`. `tested push` omits skipped flags from ingest so the last successful upload for that package still stands. `tested diff --json` includes the same map as check.
|
|
152
152
|
|
|
153
153
|
## Coverage formats
|
|
154
154
|
|
|
@@ -195,7 +195,7 @@ Or pass `--file` repeatedly / Action `files`. A CI matrix must not conclude the
|
|
|
195
195
|
```yaml
|
|
196
196
|
- uses: tested-hq/cli/action@main
|
|
197
197
|
with:
|
|
198
|
-
version: 0.1.
|
|
198
|
+
version: 0.1.9
|
|
199
199
|
push: true
|
|
200
200
|
pr-number: ${{ github.event.pull_request.number }}
|
|
201
201
|
token: ${{ secrets.TESTED_TOKEN }}
|
package/dist/td.js
CHANGED
|
@@ -1129,7 +1129,7 @@ var TestedConfigSchema = z2.object({
|
|
|
1129
1129
|
}).optional(),
|
|
1130
1130
|
/**
|
|
1131
1131
|
* Per-package gates. Each flag is graded from this run's coverage files
|
|
1132
|
-
* only — a missing path is
|
|
1132
|
+
* only — a missing path is skipped (not 0%), never another flag's totals.
|
|
1133
1133
|
*/
|
|
1134
1134
|
flags: z2.record(FlagNameSchema, FlagConfigSchema).optional()
|
|
1135
1135
|
});
|
|
@@ -1164,17 +1164,21 @@ var DiffOutputSchema = z2.object({
|
|
|
1164
1164
|
ignored: z2.array(z2.string())
|
|
1165
1165
|
});
|
|
1166
1166
|
var FlagMetricJsonSchema = z2.object({
|
|
1167
|
-
|
|
1167
|
+
/** Omitted when this flag had no files this run (`skipped: true`). */
|
|
1168
|
+
pct: z2.number().min(0).max(100).optional(),
|
|
1168
1169
|
threshold: z2.number().min(0).max(100),
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1170
|
+
/** Omitted when skipped — a missing flag is not a 0% fail. */
|
|
1171
|
+
pass: z2.boolean().optional(),
|
|
1172
|
+
executable: z2.number().int().nonnegative().optional(),
|
|
1173
|
+
covered: z2.number().int().nonnegative().optional(),
|
|
1172
1174
|
skipped: z2.literal(true).optional(),
|
|
1173
1175
|
reason: z2.string().optional()
|
|
1174
1176
|
});
|
|
1175
1177
|
var FlagResultJsonSchema = z2.object({
|
|
1176
1178
|
status: z2.enum(["pass", "fail", "missing"]),
|
|
1177
1179
|
present: z2.boolean(),
|
|
1180
|
+
/** True when this flag had no coverage files this run (not a 0% result). */
|
|
1181
|
+
skipped: z2.literal(true).optional(),
|
|
1178
1182
|
reason: z2.string().optional(),
|
|
1179
1183
|
patchCheck: z2.string(),
|
|
1180
1184
|
projectCheck: z2.string(),
|
|
@@ -1742,13 +1746,11 @@ function totalsToMetric(totals, threshold, kind) {
|
|
|
1742
1746
|
covered: totals.covered
|
|
1743
1747
|
};
|
|
1744
1748
|
}
|
|
1745
|
-
function missingMetric(threshold) {
|
|
1749
|
+
function missingMetric(threshold, reason) {
|
|
1746
1750
|
return {
|
|
1747
|
-
pct: 0,
|
|
1748
1751
|
threshold,
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
covered: 0
|
|
1752
|
+
skipped: true,
|
|
1753
|
+
reason
|
|
1752
1754
|
};
|
|
1753
1755
|
}
|
|
1754
1756
|
function checkSlug(kind, name) {
|
|
@@ -1778,8 +1780,8 @@ function missingFlag(name, thresholds, reason) {
|
|
|
1778
1780
|
reason,
|
|
1779
1781
|
patchCheck: checkSlug("patch", name),
|
|
1780
1782
|
projectCheck: checkSlug("project", name),
|
|
1781
|
-
patch: missingMetric(thresholds.patch),
|
|
1782
|
-
project: missingMetric(thresholds.project)
|
|
1783
|
+
patch: missingMetric(thresholds.patch, reason),
|
|
1784
|
+
project: missingMetric(thresholds.project, reason)
|
|
1783
1785
|
};
|
|
1784
1786
|
}
|
|
1785
1787
|
function evaluateFlags(input) {
|
|
@@ -1810,7 +1812,7 @@ function evaluateFlags(input) {
|
|
|
1810
1812
|
return results;
|
|
1811
1813
|
}
|
|
1812
1814
|
function flagsPass(results) {
|
|
1813
|
-
return results.every((f) => f.status
|
|
1815
|
+
return results.every((f) => f.status !== "fail");
|
|
1814
1816
|
}
|
|
1815
1817
|
function flagsToJson(results) {
|
|
1816
1818
|
const out = {};
|
|
@@ -1818,19 +1820,44 @@ function flagsToJson(results) {
|
|
|
1818
1820
|
out[flag.name] = {
|
|
1819
1821
|
status: flag.status,
|
|
1820
1822
|
present: flag.present,
|
|
1823
|
+
...flag.status === "missing" ? { skipped: true } : {},
|
|
1821
1824
|
...flag.reason ? { reason: flag.reason } : {},
|
|
1822
1825
|
patchCheck: flag.patchCheck,
|
|
1823
1826
|
projectCheck: flag.projectCheck,
|
|
1824
|
-
patch: flag.patch,
|
|
1825
|
-
project: flag.project
|
|
1827
|
+
patch: metricToJson(flag.patch),
|
|
1828
|
+
project: metricToJson(flag.project)
|
|
1826
1829
|
};
|
|
1827
1830
|
}
|
|
1828
1831
|
return out;
|
|
1829
1832
|
}
|
|
1833
|
+
function metricToJson(metric) {
|
|
1834
|
+
if (metric.skipped) {
|
|
1835
|
+
return {
|
|
1836
|
+
threshold: metric.threshold,
|
|
1837
|
+
skipped: true,
|
|
1838
|
+
...metric.reason ? { reason: metric.reason } : {}
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
return {
|
|
1842
|
+
pct: metric.pct ?? 0,
|
|
1843
|
+
threshold: metric.threshold,
|
|
1844
|
+
pass: metric.pass ?? false,
|
|
1845
|
+
executable: metric.executable ?? 0,
|
|
1846
|
+
covered: metric.covered ?? 0,
|
|
1847
|
+
...metric.reason ? { reason: metric.reason } : {}
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
function flagsToIngestJson(results) {
|
|
1851
|
+
const present = results.filter((f) => f.present);
|
|
1852
|
+
return present.length > 0 ? flagsToJson(present) : void 0;
|
|
1853
|
+
}
|
|
1830
1854
|
function resolveFlagsJson(input) {
|
|
1831
1855
|
const results = evaluateFlags(input);
|
|
1832
1856
|
return results.length > 0 ? flagsToJson(results) : void 0;
|
|
1833
1857
|
}
|
|
1858
|
+
function resolveIngestFlagsJson(input) {
|
|
1859
|
+
return flagsToIngestJson(evaluateFlags(input));
|
|
1860
|
+
}
|
|
1834
1861
|
|
|
1835
1862
|
// src/core/coverage-merge.ts
|
|
1836
1863
|
function emptyToUndef(value) {
|
|
@@ -2553,7 +2580,7 @@ async function executePush(cli, deps) {
|
|
|
2553
2580
|
let flags;
|
|
2554
2581
|
if (!handshakeOnly && !computeDiffFn) {
|
|
2555
2582
|
try {
|
|
2556
|
-
flags =
|
|
2583
|
+
flags = resolveIngestFlagsJson({
|
|
2557
2584
|
config,
|
|
2558
2585
|
files,
|
|
2559
2586
|
addedByFile,
|
|
@@ -3072,7 +3099,7 @@ import pc3 from "picocolors";
|
|
|
3072
3099
|
// package.json
|
|
3073
3100
|
var package_default = {
|
|
3074
3101
|
name: "@tested/cli",
|
|
3075
|
-
version: "0.1.
|
|
3102
|
+
version: "0.1.9",
|
|
3076
3103
|
description: "Coverage your agent can use. CLI for patch + project coverage with agent-readable JSON output.",
|
|
3077
3104
|
license: "MIT",
|
|
3078
3105
|
homepage: "https://tested.dev",
|
|
@@ -3718,6 +3745,12 @@ function formatMetricLine(label, pct3, threshold, pass, indent = " ") {
|
|
|
3718
3745
|
const status = pass ? badge("pass") : badge("fail");
|
|
3719
3746
|
return `${indent}${label.padEnd(8)} ${pctStr}% (threshold ${threshold}) ${status}`;
|
|
3720
3747
|
}
|
|
3748
|
+
function presentPct(metric) {
|
|
3749
|
+
if (metric.pct === void 0 || metric.pass === void 0) {
|
|
3750
|
+
throw new Error("present flag metric missing pct/pass");
|
|
3751
|
+
}
|
|
3752
|
+
return { pct: metric.pct, pass: metric.pass };
|
|
3753
|
+
}
|
|
3721
3754
|
function formatFlagLines(results) {
|
|
3722
3755
|
if (results.length === 0) return [];
|
|
3723
3756
|
const lines = [""];
|
|
@@ -3734,16 +3767,18 @@ function formatFlagLines(results) {
|
|
|
3734
3767
|
` ${"Patch".padEnd(8)} ${dim("-")} ${EMPTY_PATCH_REASON} ${badge("skip")}`
|
|
3735
3768
|
);
|
|
3736
3769
|
} else {
|
|
3770
|
+
const patch = presentPct(flag.patch);
|
|
3737
3771
|
lines.push(
|
|
3738
|
-
formatMetricLine("Patch",
|
|
3772
|
+
formatMetricLine("Patch", patch.pct, flag.patch.threshold, patch.pass, " ")
|
|
3739
3773
|
);
|
|
3740
3774
|
}
|
|
3775
|
+
const project = presentPct(flag.project);
|
|
3741
3776
|
lines.push(
|
|
3742
3777
|
formatMetricLine(
|
|
3743
3778
|
"Project",
|
|
3744
|
-
|
|
3779
|
+
project.pct,
|
|
3745
3780
|
flag.project.threshold,
|
|
3746
|
-
|
|
3781
|
+
project.pass,
|
|
3747
3782
|
" "
|
|
3748
3783
|
)
|
|
3749
3784
|
);
|
|
@@ -3831,7 +3866,7 @@ ${tip("add thresholds.patch / thresholds.project to enforce")}
|
|
|
3831
3866
|
if (missing.length > 0) {
|
|
3832
3867
|
lines.push(
|
|
3833
3868
|
dim(
|
|
3834
|
-
"
|
|
3869
|
+
"Flags with no files this run are skipped (not 0%). Scope the job with --flag when this coverage file is one package."
|
|
3835
3870
|
)
|
|
3836
3871
|
);
|
|
3837
3872
|
}
|
package/dist/tested.js
CHANGED
|
@@ -1129,7 +1129,7 @@ var TestedConfigSchema = z2.object({
|
|
|
1129
1129
|
}).optional(),
|
|
1130
1130
|
/**
|
|
1131
1131
|
* Per-package gates. Each flag is graded from this run's coverage files
|
|
1132
|
-
* only — a missing path is
|
|
1132
|
+
* only — a missing path is skipped (not 0%), never another flag's totals.
|
|
1133
1133
|
*/
|
|
1134
1134
|
flags: z2.record(FlagNameSchema, FlagConfigSchema).optional()
|
|
1135
1135
|
});
|
|
@@ -1164,17 +1164,21 @@ var DiffOutputSchema = z2.object({
|
|
|
1164
1164
|
ignored: z2.array(z2.string())
|
|
1165
1165
|
});
|
|
1166
1166
|
var FlagMetricJsonSchema = z2.object({
|
|
1167
|
-
|
|
1167
|
+
/** Omitted when this flag had no files this run (`skipped: true`). */
|
|
1168
|
+
pct: z2.number().min(0).max(100).optional(),
|
|
1168
1169
|
threshold: z2.number().min(0).max(100),
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1170
|
+
/** Omitted when skipped — a missing flag is not a 0% fail. */
|
|
1171
|
+
pass: z2.boolean().optional(),
|
|
1172
|
+
executable: z2.number().int().nonnegative().optional(),
|
|
1173
|
+
covered: z2.number().int().nonnegative().optional(),
|
|
1172
1174
|
skipped: z2.literal(true).optional(),
|
|
1173
1175
|
reason: z2.string().optional()
|
|
1174
1176
|
});
|
|
1175
1177
|
var FlagResultJsonSchema = z2.object({
|
|
1176
1178
|
status: z2.enum(["pass", "fail", "missing"]),
|
|
1177
1179
|
present: z2.boolean(),
|
|
1180
|
+
/** True when this flag had no coverage files this run (not a 0% result). */
|
|
1181
|
+
skipped: z2.literal(true).optional(),
|
|
1178
1182
|
reason: z2.string().optional(),
|
|
1179
1183
|
patchCheck: z2.string(),
|
|
1180
1184
|
projectCheck: z2.string(),
|
|
@@ -1742,13 +1746,11 @@ function totalsToMetric(totals, threshold, kind) {
|
|
|
1742
1746
|
covered: totals.covered
|
|
1743
1747
|
};
|
|
1744
1748
|
}
|
|
1745
|
-
function missingMetric(threshold) {
|
|
1749
|
+
function missingMetric(threshold, reason) {
|
|
1746
1750
|
return {
|
|
1747
|
-
pct: 0,
|
|
1748
1751
|
threshold,
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
covered: 0
|
|
1752
|
+
skipped: true,
|
|
1753
|
+
reason
|
|
1752
1754
|
};
|
|
1753
1755
|
}
|
|
1754
1756
|
function checkSlug(kind, name) {
|
|
@@ -1778,8 +1780,8 @@ function missingFlag(name, thresholds, reason) {
|
|
|
1778
1780
|
reason,
|
|
1779
1781
|
patchCheck: checkSlug("patch", name),
|
|
1780
1782
|
projectCheck: checkSlug("project", name),
|
|
1781
|
-
patch: missingMetric(thresholds.patch),
|
|
1782
|
-
project: missingMetric(thresholds.project)
|
|
1783
|
+
patch: missingMetric(thresholds.patch, reason),
|
|
1784
|
+
project: missingMetric(thresholds.project, reason)
|
|
1783
1785
|
};
|
|
1784
1786
|
}
|
|
1785
1787
|
function evaluateFlags(input) {
|
|
@@ -1810,7 +1812,7 @@ function evaluateFlags(input) {
|
|
|
1810
1812
|
return results;
|
|
1811
1813
|
}
|
|
1812
1814
|
function flagsPass(results) {
|
|
1813
|
-
return results.every((f) => f.status
|
|
1815
|
+
return results.every((f) => f.status !== "fail");
|
|
1814
1816
|
}
|
|
1815
1817
|
function flagsToJson(results) {
|
|
1816
1818
|
const out = {};
|
|
@@ -1818,19 +1820,44 @@ function flagsToJson(results) {
|
|
|
1818
1820
|
out[flag.name] = {
|
|
1819
1821
|
status: flag.status,
|
|
1820
1822
|
present: flag.present,
|
|
1823
|
+
...flag.status === "missing" ? { skipped: true } : {},
|
|
1821
1824
|
...flag.reason ? { reason: flag.reason } : {},
|
|
1822
1825
|
patchCheck: flag.patchCheck,
|
|
1823
1826
|
projectCheck: flag.projectCheck,
|
|
1824
|
-
patch: flag.patch,
|
|
1825
|
-
project: flag.project
|
|
1827
|
+
patch: metricToJson(flag.patch),
|
|
1828
|
+
project: metricToJson(flag.project)
|
|
1826
1829
|
};
|
|
1827
1830
|
}
|
|
1828
1831
|
return out;
|
|
1829
1832
|
}
|
|
1833
|
+
function metricToJson(metric) {
|
|
1834
|
+
if (metric.skipped) {
|
|
1835
|
+
return {
|
|
1836
|
+
threshold: metric.threshold,
|
|
1837
|
+
skipped: true,
|
|
1838
|
+
...metric.reason ? { reason: metric.reason } : {}
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
return {
|
|
1842
|
+
pct: metric.pct ?? 0,
|
|
1843
|
+
threshold: metric.threshold,
|
|
1844
|
+
pass: metric.pass ?? false,
|
|
1845
|
+
executable: metric.executable ?? 0,
|
|
1846
|
+
covered: metric.covered ?? 0,
|
|
1847
|
+
...metric.reason ? { reason: metric.reason } : {}
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
function flagsToIngestJson(results) {
|
|
1851
|
+
const present = results.filter((f) => f.present);
|
|
1852
|
+
return present.length > 0 ? flagsToJson(present) : void 0;
|
|
1853
|
+
}
|
|
1830
1854
|
function resolveFlagsJson(input) {
|
|
1831
1855
|
const results = evaluateFlags(input);
|
|
1832
1856
|
return results.length > 0 ? flagsToJson(results) : void 0;
|
|
1833
1857
|
}
|
|
1858
|
+
function resolveIngestFlagsJson(input) {
|
|
1859
|
+
return flagsToIngestJson(evaluateFlags(input));
|
|
1860
|
+
}
|
|
1834
1861
|
|
|
1835
1862
|
// src/core/coverage-merge.ts
|
|
1836
1863
|
function emptyToUndef(value) {
|
|
@@ -2553,7 +2580,7 @@ async function executePush(cli, deps) {
|
|
|
2553
2580
|
let flags;
|
|
2554
2581
|
if (!handshakeOnly && !computeDiffFn) {
|
|
2555
2582
|
try {
|
|
2556
|
-
flags =
|
|
2583
|
+
flags = resolveIngestFlagsJson({
|
|
2557
2584
|
config,
|
|
2558
2585
|
files,
|
|
2559
2586
|
addedByFile,
|
|
@@ -3072,7 +3099,7 @@ import pc3 from "picocolors";
|
|
|
3072
3099
|
// package.json
|
|
3073
3100
|
var package_default = {
|
|
3074
3101
|
name: "@tested/cli",
|
|
3075
|
-
version: "0.1.
|
|
3102
|
+
version: "0.1.9",
|
|
3076
3103
|
description: "Coverage your agent can use. CLI for patch + project coverage with agent-readable JSON output.",
|
|
3077
3104
|
license: "MIT",
|
|
3078
3105
|
homepage: "https://tested.dev",
|
|
@@ -3718,6 +3745,12 @@ function formatMetricLine(label, pct3, threshold, pass, indent = " ") {
|
|
|
3718
3745
|
const status = pass ? badge("pass") : badge("fail");
|
|
3719
3746
|
return `${indent}${label.padEnd(8)} ${pctStr}% (threshold ${threshold}) ${status}`;
|
|
3720
3747
|
}
|
|
3748
|
+
function presentPct(metric) {
|
|
3749
|
+
if (metric.pct === void 0 || metric.pass === void 0) {
|
|
3750
|
+
throw new Error("present flag metric missing pct/pass");
|
|
3751
|
+
}
|
|
3752
|
+
return { pct: metric.pct, pass: metric.pass };
|
|
3753
|
+
}
|
|
3721
3754
|
function formatFlagLines(results) {
|
|
3722
3755
|
if (results.length === 0) return [];
|
|
3723
3756
|
const lines = [""];
|
|
@@ -3734,16 +3767,18 @@ function formatFlagLines(results) {
|
|
|
3734
3767
|
` ${"Patch".padEnd(8)} ${dim("-")} ${EMPTY_PATCH_REASON} ${badge("skip")}`
|
|
3735
3768
|
);
|
|
3736
3769
|
} else {
|
|
3770
|
+
const patch = presentPct(flag.patch);
|
|
3737
3771
|
lines.push(
|
|
3738
|
-
formatMetricLine("Patch",
|
|
3772
|
+
formatMetricLine("Patch", patch.pct, flag.patch.threshold, patch.pass, " ")
|
|
3739
3773
|
);
|
|
3740
3774
|
}
|
|
3775
|
+
const project = presentPct(flag.project);
|
|
3741
3776
|
lines.push(
|
|
3742
3777
|
formatMetricLine(
|
|
3743
3778
|
"Project",
|
|
3744
|
-
|
|
3779
|
+
project.pct,
|
|
3745
3780
|
flag.project.threshold,
|
|
3746
|
-
|
|
3781
|
+
project.pass,
|
|
3747
3782
|
" "
|
|
3748
3783
|
)
|
|
3749
3784
|
);
|
|
@@ -3831,7 +3866,7 @@ ${tip("add thresholds.patch / thresholds.project to enforce")}
|
|
|
3831
3866
|
if (missing.length > 0) {
|
|
3832
3867
|
lines.push(
|
|
3833
3868
|
dim(
|
|
3834
|
-
"
|
|
3869
|
+
"Flags with no files this run are skipped (not 0%). Scope the job with --flag when this coverage file is one package."
|
|
3835
3870
|
)
|
|
3836
3871
|
);
|
|
3837
3872
|
}
|
package/package.json
CHANGED