Skip to content

extension_breakage_report #91

extension_breakage_report

extension_breakage_report #91

Workflow file for this run

name: Extension Report Intake
on:
repository_dispatch:
types:
- extension_breakage_report
- extension_user_report
jobs:
intake:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Upsert issue from incoming report
uses: actions/github-script@v7
with:
script: |
const AUTO_THRESHOLD = 3;
const BUFFER_LABEL = 'report-buffer';
const payload = context.payload.client_payload || {};
const report = payload.report || {};
const diagnostics = report.diagnostics || {};
const exactDigest = payload.digest || 'unknown';
const aggregateDigest = require('crypto')
.createHash('sha256')
.update(JSON.stringify({
type: report.type || 'auto_probe',
platform: diagnostics.platform || 'unknown',
reason: diagnostics.reason || 'unknown',
broken: diagnostics.probe?.broken || [],
selectorVersion: diagnostics.selectorVersion || 'unknown',
}))
.digest('hex')
.slice(0, 16);
const exactMarker = `<!-- cbv-report-digest:${exactDigest} -->`;
const aggregateMarker = `<!-- cbv-aggregate-digest:${aggregateDigest} -->`;
const isUserReport = report.type === 'user_report';
const isSyntheticTestBreakage = !isUserReport && (
diagnostics.reason === 'test_breakage' ||
String(diagnostics.selectorVersion || '').startsWith('test-')
);
if (isSyntheticTestBreakage) {
core.info('Skip synthetic test breakage report to avoid noisy issues.');
return;
}
const baseLabels = [
isUserReport ? 'user-report' : 'auto-report',
];
const finalLabels = [
'extension-report',
...baseLabels,
];
if ((diagnostics.probe?.broken || []).length) finalLabels.push('selector-broken');
const titlePrefix = isUserReport ? '[USER]' : '[AUTO]';
const titleCore = [
titlePrefix,
diagnostics.platform || 'unknown',
diagnostics.reason || 'breakage',
(diagnostics.probe?.broken || []).join(', ') || 'no-broken-selectors',
].join(' · ');
const title = titleCore;
const bufferTitle = `[BUFFER] ${titleCore}`;
const timestamp = new Date().toISOString();
function parseCount(body) {
const match = String(body || '').match(/<!-- cbv-buffer-count:(\d+) -->/);
return match ? Number(match[1]) : 0;
}
function buildBody({ count = null, promoted = false }) {
const lines = [
exactMarker,
aggregateMarker,
count != null ? `<!-- cbv-buffer-count:${count} -->` : '',
'## Report Summary',
'',
`- Type: \`${report.type || 'auto_probe'}\``,
`- Platform: \`${diagnostics.platform || 'unknown'}\``,
`- Reason: \`${diagnostics.reason || 'unknown'}\``,
`- Selector version: \`${diagnostics.selectorVersion || 'unknown'}\``,
`- URL: ${diagnostics.url || report.tabUrl || 'unknown'}`,
`- Broken probes: \`${(diagnostics.probe?.broken || []).join(', ') || 'none'}\``,
];
if (count != null) {
lines.push(`- Aggregate count: \`${count}\``);
}
if (promoted) {
lines.push(`- Promoted to visible issue at: ${timestamp}`);
}
lines.push(
'',
'## Diagnostics',
'',
'```json',
JSON.stringify(report, null, 2),
'```',
);
return lines.filter(Boolean).join('\n');
}
const issues = await github.paginate(github.rest.issues.listForRepo, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100,
});
const matchingIssues = issues.filter(issue => (issue.body || '').includes(aggregateMarker));
const visibleIssue = matchingIssues.find(issue => !(issue.labels || []).some(label => (label.name || label) === BUFFER_LABEL));
const bufferIssue = matchingIssues.find(issue => (issue.labels || []).some(label => (label.name || label) === BUFFER_LABEL));
if (visibleIssue) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: visibleIssue.number,
body: [
'A matching aggregated report was received again.',
'',
`- Aggregate digest: \`${aggregateDigest}\``,
`- Exact digest: \`${exactDigest}\``,
`- Event: \`${context.payload.action || context.payload.repository_dispatch?.event_type || context.payload.client_payload?.report?.type || 'repository_dispatch'}\``,
`- Reported at: ${timestamp}`,
`- URL: ${diagnostics.url || report.tabUrl || 'unknown'}`,
'',
'```json',
JSON.stringify(report, null, 2),
'```',
].join('\n'),
});
return;
}
if (isUserReport) {
if (bufferIssue) {
const count = parseCount(bufferIssue.body) + 1;
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: bufferIssue.number,
title,
body: buildBody({ count, promoted: true }),
labels: finalLabels,
});
} else {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body: buildBody({ count: 1, promoted: true }),
labels: finalLabels,
});
}
return;
}
if (!bufferIssue) {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: bufferTitle,
body: buildBody({ count: 1 }),
labels: [BUFFER_LABEL, ...baseLabels],
});
return;
}
const nextCount = parseCount(bufferIssue.body) + 1;
if (nextCount >= AUTO_THRESHOLD) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: bufferIssue.number,
title,
body: buildBody({ count: nextCount, promoted: true }),
labels: finalLabels,
});
return;
}
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: bufferIssue.number,
title: bufferTitle,
body: buildBody({ count: nextCount }),
labels: [BUFFER_LABEL, ...baseLabels],
});