(opts)
| 336 | } |
| 337 | |
| 338 | async function maybeReportIssue(opts) { |
| 339 | const config = getConfig(); |
| 340 | if (!config) return; |
| 341 | |
| 342 | const signals = opts.signals || []; |
| 343 | |
| 344 | if (!shouldReport(signals, config)) return; |
| 345 | |
| 346 | // Local triage: suppress confidently host-side failures instead of opening a |
| 347 | // public issue for them. Default-open -- only recognized host buckets are |
| 348 | // suppressed; anything we cannot classify is still filed. |
| 349 | const strict = String(process.env.EVOLVER_ISSUE_STRICT_CLASSIFY || 'true').toLowerCase() !== 'false'; |
| 350 | const classification = classifyFailure(opts); |
| 351 | if (strict && SUPPRESS_BUCKETS.has(classification.bucket)) { |
| 352 | console.log('[IssueReporter] Suppressing public issue [' + classification.bucket + ']: ' + classification.reason + '. Logged locally; not filing to ' + config.repo + '.'); |
| 353 | recordSuppression(signals, classification); |
| 354 | return; |
| 355 | } |
| 356 | |
| 357 | const token = getGithubToken(); |
| 358 | if (!token) { |
| 359 | console.log('[IssueReporter] No GitHub token available. Skipping auto-report.'); |
| 360 | return; |
| 361 | } |
| 362 | |
| 363 | const errorSig = extractErrorSignature(signals); |
| 364 | const titleSig = errorSig.slice(0, 80); |
| 365 | const title = '[Auto] Recurring failure: ' + titleSig; |
| 366 | const body = buildIssueBody(opts); |
| 367 | |
| 368 | try { |
| 369 | const existing = await findExistingIssue(config.repo, title, token); |
| 370 | if (existing) { |
| 371 | console.log('[IssueReporter] Open issue already exists (#' + existing.number + '): ' + existing.url + '. Skipping duplicate report.'); |
| 372 | const state = readState(); |
| 373 | const errorKey = computeErrorKey(signals); |
| 374 | let recentKeys = Array.isArray(state.recentIssueKeys) ? state.recentIssueKeys : []; |
| 375 | if (!recentKeys.includes(errorKey)) recentKeys.push(errorKey); |
| 376 | if (recentKeys.length > 20) recentKeys = recentKeys.slice(-20); |
| 377 | writeState({ |
| 378 | lastReportedAt: state.lastReportedAt || null, |
| 379 | recentIssueKeys: recentKeys, |
| 380 | lastIssueUrl: existing.url, |
| 381 | lastIssueNumber: existing.number, |
| 382 | lastSkippedAt: new Date().toISOString(), |
| 383 | }); |
| 384 | return; |
| 385 | } |
| 386 | |
| 387 | const result = await createGithubIssue(config.repo, title, body, token); |
| 388 | console.log('[IssueReporter] Created GitHub issue #' + result.number + ': ' + result.url); |
| 389 | |
| 390 | const state = readState(); |
| 391 | const errorKey = computeErrorKey(signals); |
| 392 | let recentKeys = Array.isArray(state.recentIssueKeys) ? state.recentIssueKeys : []; |
| 393 | recentKeys.push(errorKey); |
| 394 | if (recentKeys.length > 20) recentKeys = recentKeys.slice(-20); |
| 395 | writeState({ |
nothing calls this directly
no test coverage detected