(repo, title, token)
| 305 | } |
| 306 | |
| 307 | async function findExistingIssue(repo, title, token) { |
| 308 | const titleSig = escapeSearchQuery(title).slice(0, 120); |
| 309 | if (!titleSig) return null; |
| 310 | const q = 'repo:' + repo + ' is:issue is:open in:title "' + titleSig + '"'; |
| 311 | const url = 'https://api.github.com/search/issues?per_page=5&q=' + encodeURIComponent(q); |
| 312 | const headers = { |
| 313 | 'Accept': 'application/vnd.github+json', |
| 314 | 'X-GitHub-Api-Version': '2022-11-28', |
| 315 | }; |
| 316 | if (token) headers['Authorization'] = 'Bearer ' + token; |
| 317 | let response; |
| 318 | try { |
| 319 | response = await fetch(url, { method: 'GET', headers: headers, signal: AbortSignal.timeout(10000) }); |
| 320 | } catch (e) { |
| 321 | return null; |
| 322 | } |
| 323 | if (!response.ok) return null; |
| 324 | let data; |
| 325 | try { data = await response.json(); } catch (_) { return null; } |
| 326 | const items = Array.isArray(data && data.items) ? data.items : []; |
| 327 | const AUTO_PREFIX = '[Auto] Recurring failure: '; |
| 328 | const match = items.find(function (it) { |
| 329 | return it && typeof it.title === 'string' && it.title.trim() === title.trim() && it.state === 'open'; |
| 330 | }) || items.find(function (it) { |
| 331 | return it && it.state === 'open' && typeof it.title === 'string' && |
| 332 | it.title.startsWith(AUTO_PREFIX) && it.title.trim().startsWith(titleSig.trim()); |
| 333 | }); |
| 334 | if (!match) return null; |
| 335 | return { number: match.number, url: match.html_url, title: match.title }; |
| 336 | } |
| 337 | |
| 338 | async function maybeReportIssue(opts) { |
| 339 | const config = getConfig(); |
no test coverage detected