ci: supersede #367 with combined tiered PR pipeline redesign (#369)

* ci: streamline PR pipeline gates

* test: annotate ci-test-scope stderr assertions for lint rule

---------

Co-authored-by: Colin <colin@solvely.net>
This commit is contained in:
Tom Boucher
2026-05-26 19:11:58 -04:00
committed by GitHub
parent aaa19b0609
commit 9dd2c87c6c
15 changed files with 950 additions and 192 deletions

View File

@@ -11,6 +11,8 @@
// node scripts/run-tests.cjs --suite integration # *.integration.test.cjs
// node scripts/run-tests.cjs --suite install # *.install.test.cjs
// node scripts/run-tests.cjs --suite slow # *.slow.test.cjs
// node scripts/run-tests.cjs --files "a.test.cjs b.test.cjs"
// node scripts/run-tests.cjs --files-from /tmp/selected-tests.txt
//
// Suite grouping convention: filename suffix marker before `.test.cjs`.
// A file named `foo.security.test.cjs` belongs to the `security` suite.
@@ -28,6 +30,8 @@ const MARKED_SUITES = ['integration', 'install', 'security', 'slow'];
function parseArgs(argv) {
let suite = null;
let seen = false;
let files = null;
let filesFrom = null;
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === '--suite') {
@@ -50,11 +54,50 @@ function parseArgs(argv) {
if (!suite) {
return { error: '--suite requires a value' };
}
} else if (a === '--files') {
if (files !== null) {
return { error: 'duplicate --files flag' };
}
const v = argv[i + 1];
if (!v || v.startsWith('--')) {
return { error: '--files requires a value' };
}
files = v;
i++;
} else if (a.startsWith('--files=')) {
if (files !== null) {
return { error: 'duplicate --files flag' };
}
files = a.slice('--files='.length);
if (!files) {
return { error: '--files requires a value' };
}
} else if (a === '--files-from') {
if (filesFrom !== null) {
return { error: 'duplicate --files-from flag' };
}
const v = argv[i + 1];
if (!v || v.startsWith('--')) {
return { error: '--files-from requires a value' };
}
filesFrom = v;
i++;
} else if (a.startsWith('--files-from=')) {
if (filesFrom !== null) {
return { error: 'duplicate --files-from flag' };
}
filesFrom = a.slice('--files-from='.length);
if (!filesFrom) {
return { error: '--files-from requires a value' };
}
} else {
return { error: `unknown argument: ${a}` };
}
}
return { suite };
if (files !== null && filesFrom !== null) {
return { error: '--files and --files-from cannot be combined' };
}
return { suite, files, filesFrom };
}
// Return the marked suite name embedded in a filename, or null if it's unmarked.
@@ -79,6 +122,38 @@ function selectFiles(allFiles, suite) {
return allFiles.filter(f => suiteOf(f) === suite);
}
function splitFileList(value) {
if (!value) return [];
return value
.split(/[,\s]+/)
.map(v => v.trim())
.filter(Boolean)
.map(v => v.replace(/^tests[\\/]/, ''));
}
function selectExplicitFiles(allFiles, filesValue, filesFrom) {
const fs = require('fs');
const requested = filesFrom
? splitFileList(fs.readFileSync(filesFrom, 'utf8'))
: splitFileList(filesValue);
const available = new Set(allFiles);
const selected = [];
const missing = [];
for (const file of requested) {
if (available.has(file)) {
selected.push(file);
} else {
missing.push(file);
}
}
if (missing.length > 0) {
return {
error: `requested test file(s) not found: ${missing.join(', ')}`,
};
}
return { files: [...new Set(selected)] };
}
function main() {
const args = process.argv.slice(2);
const parsed = parseArgs(args);
@@ -107,7 +182,18 @@ function main() {
process.exit(1);
}
const selected = selectFiles(allFiles, suite).map(f => join(testDir, f));
let selectedNames;
if (parsed.files !== null || parsed.filesFrom !== null) {
const explicit = selectExplicitFiles(allFiles, parsed.files, parsed.filesFrom);
if (explicit.error) {
console.error(`run-tests: ${explicit.error}`);
process.exit(2);
}
selectedNames = explicit.files;
} else {
selectedNames = selectFiles(allFiles, suite);
}
const selected = selectedNames.map(f => join(testDir, f));
if (selected.length === 0) {
// Empty suite: report and exit 0 so empty lanes (e.g. `security` before