Skip to content

Commit

Permalink
[ci] custom stale action
Browse files Browse the repository at this point in the history
See comment for description. actions/stale wasn't scaling for us, so a
simpler approach works better.

Pull Request resolved: pytorch#75675
Approved by: https://github.com/seemethere
  • Loading branch information
suo authored and pytorchmergebot committed Apr 12, 2022
1 parent a4126e5 commit 692f1b9
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 46 deletions.
133 changes: 133 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# A workflow that implements similar logic to actions/stale.
#
# Compared to actions/stale, it is implemented to make API requests proportional
# to the number of stale PRs, not the total number of issues in the repo. This
# is because PyTorch has a lot of issues/PRs, so the actions/stale runs into
# rate limits way too quickly.
#
# The behavior is:
# - If a PR is not labeled stale, after 60 days inactivity label the PR as stale and comment about it.
# - If a PR is labeled stale, after 30 days inactivity close the PR.
# - `high priority` and `no-stale` PRs are exempt.

name: Close stale pull requests

on:
schedule:
# Run hourly.
- cron: 30 * * * *

jobs:
stale:
if: ${{ github.repository == 'pytorch/pytorch' }}
runs-on: ubuntu-latest

steps:
- uses: actions/github-script@v6
with:
script: |
const MAX_API_REQUESTS = 100;
// If a PRs not labeled stale, label them stale after no update for 60 days.
const STALE_LABEL_THRESHOLD_MS = 1000 * 60 * 60 * 24 * 60;
// For PRs already labeled stale, close after not update for 30 days.
const STALE_CLOSE_THRESHOLD_MS = 1000 * 60 * 60 * 24 * 30;
const STALE_MESSAGE =
"Looks like this PR hasn't been updated in a while so we're going to go ahead and mark this as `Stale`. <br>" +
"Feel free to remove the `Stale` label if you feel this was a mistake. <br>" +
"If you are unable to remove the `Stale` label please contact a maintainer in order to do so. <br>" +
"If you want the bot to never mark this PR stale again, add the `no-stale` label.<br>" +
"`Stale` pull requests will automatically be closed after 30 days of inactivity.<br>";
let numAPIRequests = 0;
let numProcessed = 0;
async function processPull(pull) {
core.info(`[${pull.number}] URL: ${pull.html_url}`);
numProcessed += 1;
const labels = pull.labels.map((label) => label.name);
// Skip if certain labels are present.
if (labels.includes("no-stale") || labels.includes("high priority")) {
core.info(`[${pull.number}] Skipping because PR has an exempting label.`);
return false;
}
// Check if the PR is stale, according to our configured thresholds.
let staleThresholdMillis;
if (labels.includes("Stale")) {
core.info(`[${pull.number}] PR is labeled stale, checking whether we should close it.`);
staleThresholdMillis = STALE_CLOSE_THRESHOLD_MS;
} else {
core.info(`[${pull.number}] Checking whether to label PR as stale.`);
staleThresholdMillis = STALE_LABEL_THRESHOLD_MS;
}
const millisSinceLastUpdated =
new Date().getTime() - new Date(pull.updated_at).getTime();
if (millisSinceLastUpdated < staleThresholdMillis) {
core.info(`[${pull.number}] Skipping because PR was updated recently`);
return false;
}
// At this point, we know we should do something.
// For PRs already labeled stale, close them.
if (labels.includes("Stale")) {
core.info(`[${pull.number}] Closing PR.`);
numAPIRequests += 1;
await github.rest.issues.update({
owner: "pytorch",
repo: "pytorch",
issue_number: pull.number,
state: "closed",
});
} else {
// For PRs not labeled stale, label them stale.
core.info(`[${pull.number}] Labeling PR as stale.`);
numAPIRequests += 1;
await github.rest.issues.createComment({
owner: "pytorch",
repo: "pytorch",
issue_number: pull.number,
body: STALE_MESSAGE,
});
numAPIRequests += 1;
await github.rest.issues.addLabels({
owner: "pytorch",
repo: "pytorch",
issue_number: pull.number,
labels: ["Stale"],
});
}
}
for await (const response of github.paginate.iterator(
github.rest.pulls.list,
{
owner: "pytorch",
repo: "pytorch",
state: "open",
sort: "created",
direction: "asc",
per_page: 100,
}
)) {
numAPIRequests += 1;
const pulls = response.data;
// Awaiting in a loop is intentional here. We want to serialize execution so
// that log groups are printed correctl
for (const pull of pulls) {
if (numAPIRequests > MAX_API_REQUESTS) {
core.warning("Max API requests exceeded, exiting.");
process.exit(0);
}
await core.group(`Processing PR #${pull.number}`, async () => {
await processPull(pull);
});
}
}
core.info(`Processed ${numProcessed} PRs total.`);
46 changes: 0 additions & 46 deletions .github/workflows/stale_pull_requests.yml

This file was deleted.

0 comments on commit 692f1b9

Please sign in to comment.