| module.exports = async ({ github, context }) => { | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const labelName = 'waiting for response'; | |
| // TODO(chunhtai): Remove this once all issues have applied or reapplied the | |
| // label. This should happen after 21 days. | |
| const oldLabelName = 'waiting for customer response'; | |
| const daysUntilClose = 21; | |
| const issueCloseComment = `Without additional information, we are unfortunately not sure how to resolve this issue. We are therefore reluctantly going to close this bug for now. | |
| If you find this problem please file a new issue with the same description, what happens, logs and the output of 'flutter doctor -v'. All system setups can be slightly different so it's always better to open new issues and reference the related ones. | |
| Thanks for your contribution.`; | |
| const prCloseComment = `This pull request is being closed because it has not been updated in the last 21 days after a request for more information. | |
| If you are still working on this, please feel free to reopen it or file a new pull request. | |
| Thanks for your contribution.`; | |
| const now = new Date(); | |
| const closeDate = new Date(now.getTime() - (daysUntilClose * 24 * 60 * 60 * 1000)); | |
| // Fetch issues and PRs with label | |
| const issues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner, | |
| repo, | |
| state: 'open', | |
| labels: labelName, | |
| }); | |
| for (const issue of issues) { | |
| const isPr = issue.pull_request !== undefined; | |
| // Fetch timeline events to find label date | |
| const events = await github.paginate(github.rest.issues.listEvents, { | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| }); | |
| const labelEvent = events.reverse().find( | |
| event => | |
| event.event === 'labeled' && | |
| // Issues that applied the label before the renaming still use oldLabelName. | |
| (event.label.name === labelName || event.label.name === oldLabelName) | |
| ); | |
| if (!labelEvent) { | |
| continue; // Skip if label not found in events | |
| } | |
| const labeledAt = new Date(labelEvent.created_at); | |
| let isResponse = false; | |
| // Check for author comments after label | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| since: labeledAt.toISOString(), | |
| }); | |
| for (const comment of comments) { | |
| if (comment.user.id === issue.user.id && new Date(comment.created_at) > labeledAt) { | |
| isResponse = true; | |
| break; | |
| } | |
| } | |
| // Check for commits after label if it's a PR | |
| if (!isResponse && isPr) { | |
| const commits = await github.paginate(github.rest.pulls.listCommits, { | |
| owner, | |
| repo, | |
| pull_number: issue.number, | |
| }); | |
| for (const commit of commits) { | |
| const commitDate = new Date(commit.commit.committer.date); | |
| if (commitDate > labeledAt) { | |
| isResponse = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (isResponse) { | |
| console.log(`Removing label from #${issue.number} as author responded.`); | |
| await github.rest.issues.removeLabel({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| name: labelName, | |
| }); | |
| continue; // Skip stale check | |
| } | |
| // Stale check | |
| if (issue.state === 'open') { | |
| if (labeledAt < closeDate) { | |
| if (issue.locked) { | |
| console.log(`Skipping #${issue.number} because the conversation is locked.`); | |
| continue; | |
| } | |
| console.log(`Closing #${issue.number} due to no response.`); | |
| const body = isPr ? prCloseComment : issueCloseComment; | |
| if (body) { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| body: body, | |
| }); | |
| } | |
| await github.rest.issues.update({ | |
| owner, | |
| repo, | |
| issue_number: issue.number, | |
| state: 'closed', | |
| }); | |
| } | |
| } | |
| } | |
| }; |