forked from pytorch/pytorch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
1 parent
a4126e5
commit 692f1b9
Showing
2 changed files
with
133 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.`); |
This file was deleted.
Oops, something went wrong.