mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 06:06:18 -08:00
fix(ci): address bot review feedback on policy guard and baseline generator
- Drive Tier B/C validation from each policy entry's validationCommand (falling back to nx test), skipping projects with an e2e target since the E2E workflow already runs them (Codex). - Fail when a Tier A entry has no test target (Greptile, adapted: checking all entries against test targets would false-positive on the intentionally spec-less e2e/mock-server tiers). - Guard against missing JSON array in nx show projects output (Greptile). - Scan .tsx files in the max-lines baseline generator to match the ESLint rule's file patterns (Greptile). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b0e5f292a1
commit
bfd29e7561
4 files changed
+79
-27
No files matched your search
@@ -70,14 +70,8 @@ jobs:
|
||||
CI: true
|
||||
NX_TASKS_RUNNER_DYNAMIC_OUTPUT: false
|
||||
|
||||
- name: Run Tier B/C unit tests without coverage
|
||||
run: |
|
||||
projects="$(node tools/coverage/check-coverage-policy.mjs --list-non-tier-a)"
|
||||
if [ -n "$projects" ]; then
|
||||
pnpm nx run-many --target=test --projects "$projects" --parallel=3 --output-style=static
|
||||
else
|
||||
echo "No Tier B/C projects with a test target."
|
||||
fi
|
||||
- name: Run Tier B/C validation commands
|
||||
run: node tools/coverage/check-coverage-policy.mjs --run-non-tier-a
|
||||
env:
|
||||
CI: true
|
||||
NX_TASKS_RUNNER_DYNAMIC_OUTPUT: false
|
||||
|
||||
@@ -44,9 +44,11 @@ the baseline.
|
||||
Use `tools/coverage/coverage-policy.json` as the source of truth for coverage
|
||||
ownership. Every project with a `test` target must be classified in a tier;
|
||||
`pnpm run coverage:policy:check` (part of `coverage:ci`) fails CI when a new
|
||||
project is missing from the policy or a listed project no longer exists.
|
||||
CI runs Tier A with coverage (uploaded to Codecov) and Tier B/C tests without
|
||||
coverage.
|
||||
project is missing from the policy, a listed project no longer exists, or a
|
||||
Tier A entry has no test target. CI runs Tier A with coverage (uploaded to
|
||||
Codecov) and each Tier B/C project's `validationCommand` (falling back to
|
||||
`nx test`) without coverage; projects with an `e2e` target are skipped there
|
||||
because the E2E workflow already runs them on every PR.
|
||||
|
||||
| Tier | Rule | Validation |
|
||||
| --- | --- | --- |
|
||||
|
||||
@@ -9,13 +9,16 @@
|
||||
*
|
||||
* Usage:
|
||||
* node tools/coverage/check-coverage-policy.mjs
|
||||
* Validate the policy. Exits non-zero on unclassified or stale entries.
|
||||
* node tools/coverage/check-coverage-policy.mjs --list-non-tier-a
|
||||
* Print a comma-separated list of Tier B/C projects that have a test
|
||||
* target, for running them in CI without coverage.
|
||||
* Validate the policy. Exits non-zero on unclassified projects, stale
|
||||
* entries, or Tier A entries without a test target.
|
||||
* node tools/coverage/check-coverage-policy.mjs --run-non-tier-a
|
||||
* Run the validation command for each Tier B/C project. Uses the
|
||||
* entry's `validationCommand` when present, falling back to
|
||||
* `pnpm nx test <project>`. Projects with an `e2e` target are skipped
|
||||
* because the E2E workflow already runs them on every PR.
|
||||
*/
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
@@ -36,11 +39,59 @@ function showProjects(extraArgs = []) {
|
||||
['nx', 'show', 'projects', '--json', ...extraArgs],
|
||||
{ cwd: workspaceRoot, encoding: 'utf8' }
|
||||
);
|
||||
return new Set(JSON.parse(output.slice(output.indexOf('['))));
|
||||
const start = output.indexOf('[');
|
||||
if (start === -1) {
|
||||
throw new Error(
|
||||
`nx show projects --json produced no JSON array.\nRaw output:\n${output}`
|
||||
);
|
||||
}
|
||||
return new Set(JSON.parse(output.slice(start)));
|
||||
}
|
||||
|
||||
const testProjects = showProjects(['--withTarget', 'test']);
|
||||
const allProjects = showProjects();
|
||||
const e2eProjects = showProjects(['--withTarget', 'e2e']);
|
||||
|
||||
function runNonTierA() {
|
||||
let failed = false;
|
||||
|
||||
for (const project of [...tiers.tierB, ...tiers.tierC]) {
|
||||
if (e2eProjects.has(project.name)) {
|
||||
console.log(
|
||||
`==> Skipping ${project.name}: covered by the E2E workflow.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const command =
|
||||
project.validationCommand ??
|
||||
(testProjects.has(project.name)
|
||||
? `pnpm nx test ${project.name}`
|
||||
: null);
|
||||
if (!command) {
|
||||
console.log(
|
||||
`==> Skipping ${project.name}: no validation command or test target.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`\n==> Validating ${project.name}: ${command}`);
|
||||
const result = spawnSync(command, {
|
||||
cwd: workspaceRoot,
|
||||
shell: true,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(failed ? 1 : 0);
|
||||
}
|
||||
|
||||
if (process.argv.includes('--run-non-tier-a')) {
|
||||
runNonTierA();
|
||||
}
|
||||
|
||||
const classified = new Map(
|
||||
Object.entries(tiers).flatMap(([tier, projects]) =>
|
||||
@@ -48,16 +99,11 @@ const classified = new Map(
|
||||
)
|
||||
);
|
||||
|
||||
if (process.argv.includes('--list-non-tier-a')) {
|
||||
const nonTierA = [...tiers.tierB, ...tiers.tierC]
|
||||
.map((project) => project.name)
|
||||
.filter((name) => testProjects.has(name));
|
||||
process.stdout.write(nonTierA.join(','));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const unclassified = [...testProjects].filter((name) => !classified.has(name));
|
||||
const stale = [...classified.keys()].filter((name) => !allProjects.has(name));
|
||||
const tierAWithoutTest = tiers.tierA
|
||||
.map((project) => project.name)
|
||||
.filter((name) => !testProjects.has(name));
|
||||
|
||||
if (unclassified.length > 0) {
|
||||
console.error(
|
||||
@@ -72,13 +118,23 @@ if (unclassified.length > 0) {
|
||||
|
||||
if (stale.length > 0) {
|
||||
console.error(
|
||||
'Coverage policy lists projects that no longer have a test target:\n' +
|
||||
'Coverage policy lists projects that do not exist in the workspace:\n' +
|
||||
stale.map((name) => ` - ${name}`).join('\n') +
|
||||
'\n\nRemove them from tools/coverage/coverage-policy.json.'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (tierAWithoutTest.length > 0) {
|
||||
console.error(
|
||||
'Tier A projects must have a test target, but these do not:\n' +
|
||||
tierAWithoutTest.map((name) => ` - ${name}`).join('\n') +
|
||||
'\n\nRestore the test target or move the project to Tier B/C in ' +
|
||||
'tools/coverage/coverage-policy.json.'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Coverage policy OK: ${testProjects.size} test projects classified ` +
|
||||
`(${tiers.tierA.length} Tier A, ${tiers.tierB.length} Tier B, ${tiers.tierC.length} Tier C).`
|
||||
|
||||
@@ -25,7 +25,7 @@ function collectTsFiles(dir, results) {
|
||||
collectTsFiles(path.join(dir, entry.name), results);
|
||||
}
|
||||
} else if (
|
||||
entry.name.endsWith('.ts') &&
|
||||
(entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) &&
|
||||
!entry.name.endsWith('.d.ts')
|
||||
) {
|
||||
results.push(path.join(dir, entry.name));
|
||||
|
||||
Reference in new issue
Block a user