From 6ee7570e156849167e30f07eabcdd977bcdcfee6 Mon Sep 17 00:00:00 2001 From: sofisl <55454395+sofisl@users.noreply.github.com> Date: Fri, 31 May 2024 11:39:59 -0700 Subject: [PATCH] build: automatically close stale issues that have not responded an info for request (#5297) * build: automatically close stale issues that have not responded an info for request --- .github/scripts/close-unresponse.js | 69 ++++++++++++++++++++++++ .github/scripts/remove-response-label.js | 33 ++++++++++++ .github/workflows/response.yml | 35 ++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 .github/scripts/close-unresponse.js create mode 100644 .github/scripts/remove-response-label.js create mode 100644 .github/workflows/response.yml diff --git a/.github/scripts/close-unresponse.js b/.github/scripts/close-unresponse.js new file mode 100644 index 00000000000..d972763346e --- /dev/null +++ b/.github/scripts/close-unresponse.js @@ -0,0 +1,69 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +function labeledEvent(data) { + return data.event === "labeled" && data.label.name === "needs more info"; + } + + const numberOfDaysLimit = 15; + const close_message = `This has been closed since a request for information has \ + not been answered for ${numberOfDaysLimit} days. It can be reopened when the \ + requested information is provided.`; + + module.exports = async ({ github, context }) => { + const owner = context.repo.owner; + const repo = context.repo.repo; + + const issues = await github.rest.issues.listForRepo({ + owner: owner, + repo: repo, + labels: "needs more info", + }); + const numbers = issues.data.map((e) => e.number); + + for (const number of numbers) { + const events = await github.paginate( + github.rest.issues.listEventsForTimeline, + { + owner: owner, + repo: repo, + issue_number: number, + }, + (response) => response.data.filter(labeledEvent) + ); + + const latest_response_label = events[events.length - 1]; + + const created_at = new Date(latest_response_label.created_at); + const now = new Date(); + const diff = now - created_at; + const diffDays = diff / (1000 * 60 * 60 * 24); + + if (diffDays > numberOfDaysLimit) { + await github.rest.issues.update({ + owner: owner, + repo: repo, + issue_number: number, + state: "closed", + }); + + await github.rest.issues.createComment({ + owner: owner, + repo: repo, + issue_number: number, + body: close_message, + }); + } + } + }; diff --git a/.github/scripts/remove-response-label.js b/.github/scripts/remove-response-label.js new file mode 100644 index 00000000000..b501cc89d5f --- /dev/null +++ b/.github/scripts/remove-response-label.js @@ -0,0 +1,33 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +module.exports = async ({ github, context }) => { + const commenter = context.actor; + const issue = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const author = issue.data.user.login; + const labels = issue.data.labels.map((e) => e.name); + + if (author === commenter && labels.includes("needs more info")) { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + name: "needs more info", + }); + } + }; diff --git a/.github/workflows/response.yml b/.github/workflows/response.yml new file mode 100644 index 00000000000..7f4e3141d62 --- /dev/null +++ b/.github/workflows/response.yml @@ -0,0 +1,35 @@ +name: no_response +on: + schedule: + - cron: '30 1 * * *' # Run every day at 01:30 + workflow_dispatch: + issue_comment: + +jobs: + close: + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: actions/github-script@v7 + with: + script: | + const script = require('./.github/scripts/close-unresponsive.js') + await script({github, context}) + + remove_label: + if: github.event_name == 'issue_comment' + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: actions/github-script@v7 + with: + script: | + const script = require('./.github/scripts/remove-response-label.js') + await script({github, context}) \ No newline at end of file