| import remarkParse from 'remark-parse'; |
| import { unified } from 'unified'; |
|
|
| |
| const matchTitle = ['路由地址', 'Routes']; |
| const maintainerURL = 'https://raw.githubusercontent.com/DIYgod/RSSHub/gh-pages/build/maintainers.json'; |
| const successTag = 'bug ping: pinged'; |
| const parseFailTag = 'bug ping: parsing failed'; |
| const failTag = 'bug ping: not found'; |
| const deprecatedRoute = 'route: deprecated'; |
| const route = 'route'; |
|
|
| |
| |
| const dndUsernames = new Set([]); |
|
|
| async function parseBodyRoutes(body, core) { |
| const ast = await unified().use(remarkParse).parse(body); |
|
|
| |
| const title = ast.children[0].children[0].value.trim(); |
| core.debug(`title: ${title}`); |
| if (!matchTitle.some((ele) => ele.localeCompare(title) === 0)) { |
| return null; |
| } |
|
|
| let routes = ast.children[1].value.trim(); |
| core.debug(`routes: ${JSON.stringify(routes)}`); |
| if (routes.localeCompare('NOROUTE') === 0) { |
| return null; |
| } |
|
|
| if (routes) { |
| routes = routes.split(/\r?\n/).filter(Boolean); |
| const dedup = [...new Set(routes)]; |
| if (dedup.length !== routes.length) { |
| core.warning('Duplication detected.'); |
| } |
| core.debug(dedup); |
| return dedup; |
| } |
|
|
| throw new Error('unable to parse the issue body: route does not exist'); |
| } |
|
|
| async function getMaintainersByRoutes(routes, core) { |
| const response = await fetch(maintainerURL); |
| const maintainers = await response.json(); |
|
|
| return routes.map((e) => { |
| const m = maintainers[e]; |
| if (m === undefined) { |
| core.warning(`Route ${e} not found`); |
| } |
|
|
| return m; |
| }); |
| } |
|
|
| export default async function callMaintainer({ github, context, core }) { |
| const body = context.payload.issue.body; |
| const issueFacts = { |
| issue_number: context.issue.number, |
| owner: context.repo.owner, |
| repo: context.repo.repo, |
| }; |
|
|
| const addLabels = (labels) => |
| github.rest.issues |
| .addLabels({ |
| ...issueFacts, |
| labels, |
| }) |
| .catch((error) => { |
| core.warning(error); |
| }); |
| const updateIssueState = (state) => |
| github.rest.issues |
| .update({ |
| ...issueFacts, |
| state, |
| }) |
| .catch((error) => { |
| core.warning(error); |
| }); |
|
|
| if (context.payload.issue.state === 'closed') { |
| await updateIssueState('open'); |
| } |
|
|
| const routes = await parseBodyRoutes(body, core).catch((error) => { |
| core.warning(error); |
| }); |
|
|
| if (routes === null) { |
| return; |
| } |
|
|
| if (routes === undefined) { |
| await addLabels([parseFailTag]); |
| return; |
| } |
|
|
| const maintainers = await getMaintainersByRoutes(routes, core); |
|
|
| let successCount = 0; |
| let emptyCount = 0; |
| let failedCount = 0; |
| let comments = '##### Searching for maintainers: \n\n'; |
|
|
| for (const [i, route] of routes.entries()) { |
| const main = maintainers[i]; |
| if (main === undefined) { |
| comments += `- \`${route}\`: **Route not found**\n`; |
| failedCount += 1; |
| continue; |
| } |
|
|
| if (main.length === 0) { |
| comments += `- \`${route}\`: No maintainer listed, possibly a v1 or misconfigured route\n`; |
| emptyCount += 1; |
| continue; |
| } |
|
|
| if (main.length > 0) { |
| const pingStr = main |
| .map((e) => { |
| if (dndUsernames.has(e)) { |
| return `\`@${e}\``; |
| } |
| return `@${e}`; |
| }) |
| .join(' '); |
| comments += `- \`${route}\`: ${pingStr}\n`; |
| successCount += 1; |
| } |
| } |
|
|
| const labels = ['']; |
|
|
| if (failedCount > 0) { |
| labels.push(failTag); |
| } else { |
| labels.push(successTag); |
| } |
|
|
| if (emptyCount > 0) { |
| labels.push(deprecatedRoute); |
| } |
|
|
| if (successCount > 0) { |
| labels.push(route); |
| } |
|
|
| |
| await addLabels(labels); |
|
|
| |
| await github.rest.issues |
| .createComment({ |
| ...issueFacts, |
| body: `${comments} |
| |
| |
| > To maintainers: if you are not willing to be disturbed, list your username in \`scripts/workflow/test-issue/call-maintainer.js\`. In this way, your username will be wrapped in an inline code block when tagged so you will not be notified. |
| |
| If all routes can not be found, the issue will be closed automatically. Please use \`NOROUTE\` for a route-irrelevant issue or leave a comment if it is a mistake. |
| 如果所有路由都无法匹配,issue 将会被自动关闭。如果 issue 和路由无关,请使用 \`NOROUTE\` 关键词,或者留下评论。我们会重新审核。 |
| `, |
| }) |
| .catch((error) => { |
| core.warning(error); |
| }); |
|
|
| if (failedCount && emptyCount === 0 && successCount === 0) { |
| await updateIssueState('closed'); |
| } |
| } |
|
|