fix: audit repository scope coverage #49
4 changed files with 26 additions and 21 deletions
6
.github/labeler.yml
vendored
6
.github/labeler.yml
vendored
|
|
@ -4,13 +4,13 @@
|
|||
- any-glob-to-any-file: ["src/**"]
|
||||
"scope:packaging":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ["scripts/**", ".forgejo/workflows/release.yml"]
|
||||
- any-glob-to-any-file: ["scripts/**", ".forgejo/workflows/release.yml", "package.json", "package-lock.json", "CHANGELOG.md", "changelog.d/**"]
|
||||
"scope:manifests":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ["manifests/**"]
|
||||
"scope:ci":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: [".forgejo/workflows/**"]
|
||||
- any-glob-to-any-file: [".forgejo/workflows/**", ".github/**", ".ceremony/**"]
|
||||
"scope:docs":
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ["README.md", "docs/**"]
|
||||
- any-glob-to-any-file: ["*.md", "docs/**"]
|
||||
|
|
|
|||
6
.github/labels.conf
vendored
6
.github/labels.conf
vendored
|
|
@ -1,7 +1,7 @@
|
|||
panel=codex-bot-andresmgsl glm-bot-andresmgsl claude-bot-andresmgsl kimi-bot-andresmgsl
|
||||
triage-actors=claude-bot-andresmgsl
|
||||
scope:cli|C5DEF5|src/ — the command surface (cli.js, api.js, config.js)
|
||||
scope:packaging|C5DEF5|scripts/ and the release workflow — deb build, registry publish, apt install path
|
||||
scope:packaging|C5DEF5|scripts/, release workflow, package manifests, changelog, and fragments — release packaging and version surfaces
|
||||
scope:manifests|C5DEF5|manifests/ — the fleet repo registry data
|
||||
scope:ci|C5DEF5|.forgejo/workflows/ — the test and label gates
|
||||
scope:docs|C5DEF5|README and docs/ — the prose contract
|
||||
scope:ci|C5DEF5|.forgejo/workflows/, .github/, and .ceremony/ — the test, label, and governance gates
|
||||
scope:docs|C5DEF5|root Markdown and docs/ — the prose contract
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ authoritative; this table is its human-readable restatement.
|
|||
| Scope | Covers |
|
||||
| --- | --- |
|
||||
| `scope:cli` | `src/` — the command surface (`cli.js`, `api.js`, and `config.js`) |
|
||||
| `scope:packaging` | `scripts/` and the release workflow — Debian package builds, registry publishing, and the APT install path |
|
||||
| `scope:packaging` | `scripts/`, release workflow, package manifests, changelog, and fragments — release packaging and version surfaces |
|
||||
| `scope:manifests` | `manifests/` — fleet repository registry data |
|
||||
| `scope:ci` | `.forgejo/workflows/` — test and label gates |
|
||||
| `scope:docs` | `README.md` and `docs/` — the prose contract |
|
||||
| `scope:ci` | `.forgejo/workflows/`, `.github/`, and `.ceremony/` — the test, label, and governance gates |
|
||||
| `scope:docs` | root Markdown and `docs/` — the prose contract |
|
||||
|
||||
These names and path descriptions restate the scope rows in
|
||||
[`.github/labels.conf`](.github/labels.conf).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
const { test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { spawn } = require('node:child_process');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const http = require('node:http');
|
||||
const os = require('node:os');
|
||||
|
|
@ -120,20 +121,24 @@ test('repository governance config resolves the current four-member panel and fi
|
|||
});
|
||||
});
|
||||
|
||||
test('repository scope mapping covers every configured scope with the ruled paths', () => {
|
||||
test('repository scope mapping covers every tracked path except the ruled residue', () => {
|
||||
const labeler = fs.readFileSync(REPOSITORY_LABELER, 'utf8');
|
||||
const expected = {
|
||||
'scope:cli': ['src/**'],
|
||||
'scope:packaging': ['scripts/**', '.forgejo/workflows/release.yml'],
|
||||
'scope:manifests': ['manifests/**'],
|
||||
'scope:ci': ['.forgejo/workflows/**'],
|
||||
'scope:docs': ['README.md', 'docs/**'],
|
||||
};
|
||||
const globs = [...labeler.matchAll(/any-glob-to-any-file:\s*(\[[^\n]+\])/g)]
|
||||
.flatMap((match) => JSON.parse(match[1]));
|
||||
const tracked = execFileSync('git', ['ls-files'], {
|
||||
cwd: path.join(__dirname, '..'),
|
||||
encoding: 'utf8',
|
||||
}).trim().split('\n');
|
||||
const allowed = new Set([
|
||||
'.gitignore',
|
||||
'assets/logo-mark.svg',
|
||||
...tracked.filter((filename) => /^test\/[^/]+\.test\.js$/.test(filename)),
|
||||
]);
|
||||
const uncovered = tracked.filter((filename) => (
|
||||
!allowed.has(filename) && !globs.some((glob) => path.matchesGlob(filename, glob))
|
||||
));
|
||||
|
||||
for (const [label, globs] of Object.entries(expected)) {
|
||||
assert.match(labeler, new RegExp(`^"${label}":`, 'm'), `${label} has no mapping`);
|
||||
for (const glob of globs) assert.ok(labeler.includes(JSON.stringify(glob)), `${label} does not map ${glob}`);
|
||||
}
|
||||
assert.deepEqual(uncovered, []);
|
||||
});
|
||||
|
||||
test('package lock versions match the package manifest', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue