feat(qa): --exclude-rules for sonar triage
Also suppress groups whose keys sit in closed fixed-local issues (Sonar is not re-scanned between batches). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2914f7ebe3
commit
68f0dd824e
@ -145,7 +145,9 @@ export function postWebhook(url, payload) {
|
|||||||
function ghIssues(groups, apply, cap, log) {
|
function ghIssues(groups, apply, cap, log) {
|
||||||
if (run('gh', ['auth', 'status']).status !== 0) { log('gh unauthenticated: skipping GitHub issues (run `gh auth login`).'); return []; }
|
if (run('gh', ['auth', 'status']).status !== 0) { log('gh unauthenticated: skipping GitHub issues (run `gh auth login`).'); return []; }
|
||||||
const list = run('gh', ['issue', 'list', '--state', 'open', '--label', 'sonar', '--limit', '300', '--json', 'number,body']);
|
const list = run('gh', ['issue', 'list', '--state', 'open', '--label', 'sonar', '--limit', '300', '--json', 'number,body']);
|
||||||
const bodies = list.status === 0 ? JSON.parse(list.stdout).map((x) => x.body) : [];
|
// closed fixed-local issues also suppress (Sonar keys stay open until a re-scan)
|
||||||
|
const fixed = run('gh', ['issue', 'list', '--state', 'closed', '--label', 'fixed-local', '--limit', '300', '--json', 'number,body']);
|
||||||
|
const bodies = [list, fixed].flatMap((l) => (l.status === 0 ? JSON.parse(l.stdout).map((x) => x.body) : []));
|
||||||
const todo = groups.filter((g) => !hasMarker(bodies, g.keys));
|
const todo = groups.filter((g) => !hasMarker(bodies, g.keys));
|
||||||
log(`GitHub: ${groups.length - todo.length} duplicate(s) skipped, ${Math.min(todo.length, cap)} of ${todo.length} to create (cap ${cap}).`);
|
log(`GitHub: ${groups.length - todo.length} duplicate(s) skipped, ${Math.min(todo.length, cap)} of ${todo.length} to create (cap ${cap}).`);
|
||||||
const done = [];
|
const done = [];
|
||||||
@ -196,6 +198,7 @@ export async function main(argv, deps = {}) {
|
|||||||
else if (f === '--fix-small') a.fixSmall = true; else if (f === '--list-rules') a.list = true;
|
else if (f === '--fix-small') a.fixSmall = true; else if (f === '--list-rules') a.list = true;
|
||||||
else if (f === '--json') a.json = argv[++i]; else if (f === '--max-issues') a.max = +argv[++i];
|
else if (f === '--json') a.json = argv[++i]; else if (f === '--max-issues') a.max = +argv[++i];
|
||||||
else if (f === '--cap') a.cap = +argv[++i];
|
else if (f === '--cap') a.cap = +argv[++i];
|
||||||
|
else if (f === '--exclude-rules') a.exclude = (argv[++i] || '').split(',').filter(Boolean);
|
||||||
else throw new Error(`unknown flag ${f}`);
|
else throw new Error(`unknown flag ${f}`);
|
||||||
}
|
}
|
||||||
const cfg = deps.cfg || loadConfig();
|
const cfg = deps.cfg || loadConfig();
|
||||||
@ -212,7 +215,7 @@ export async function main(argv, deps = {}) {
|
|||||||
}
|
}
|
||||||
if (a.json) writeFileSync(a.json, JSON.stringify(res, null, 2));
|
if (a.json) writeFileSync(a.json, JSON.stringify(res, null, 2));
|
||||||
if (deps.noActions) return res;
|
if (deps.noActions) return res;
|
||||||
const groups = groupIssues([...res.medium, ...res.large]);
|
const groups = groupIssues([...res.medium, ...res.large]).filter((g) => !(a.exclude || []).includes(g.rule));
|
||||||
log(`\nMedium+ groups (per rule, max 40 locations): ${groups.length}`);
|
log(`\nMedium+ groups (per rule, max 40 locations): ${groups.length}`);
|
||||||
ghIssues(groups, a.apply, a.cap, log);
|
ghIssues(groups, a.apply, a.cap, log);
|
||||||
if (a.fixSmall) { if (a.apply) fixSmall(res.small, log); else log('fix-small needs --apply (dry-run).'); }
|
if (a.fixSmall) { if (a.apply) fixSmall(res.small, log); else log('fix-small needs --apply (dry-run).'); }
|
||||||
|
|||||||
@ -64,6 +64,13 @@ test('per-run cap limits created issues (dry-run)', async () => {
|
|||||||
assert.match(txt, /10 of \d+ to create \(cap 10\)|gh unauthenticated/);
|
assert.match(txt, /10 of \d+ to create \(cap 10\)|gh unauthenticated/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('--exclude-rules drops those rule groups', async () => {
|
||||||
|
const out = [];
|
||||||
|
const issues = [mk({ key: 'e1', rule: 'python:S3776' }), mk({ key: 'e2', rule: 'python:S3776' }), mk({ key: 'e3', rule: 'python:R9' })];
|
||||||
|
await main(['--dry-run', '--all', '--exclude-rules', 'python:S3776'], { token: 't', cfg, log: (s) => out.push(s), fetchFn: async () => page(issues) });
|
||||||
|
assert.match(out.join('\n'), /Medium\+ groups \(per rule, max 40 locations\): 1/);
|
||||||
|
});
|
||||||
|
|
||||||
const page = (issues) => ({ ok: true, json: async () => ({ issues, paging: { total: issues.length } }) });
|
const page = (issues) => ({ ok: true, json: async () => ({ issues, paging: { total: issues.length } }) });
|
||||||
|
|
||||||
test('fetch sends token as basic username, new-code filter', async () => {
|
test('fetch sends token as basic username, new-code filter', async () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user