Created
April 22, 2024 16:00
-
-
Save copyleftdev/b1f9ad1ef65a4fce42ff8685c94b9839 to your computer and use it in GitHub Desktop.
Advanced GitHub Action Script for Stale Pull Request Management
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
name: Mark stale pull requests in Shepherd repo | |
on: | |
schedule: | |
- cron: '0 0 * * 1' # Runs every Monday at midnight | |
jobs: | |
stale: | |
runs-on: ubuntu-latest | |
timeout-minutes: 5 | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 | |
- name: Check PRs and compile report | |
id: compile_report | |
uses: actions/github-script@211cb3fefb35a799baa5156f9321bb774fe56294 | |
with: | |
script: | | |
const FIVE_DAYS_AGO = new Date(new Date().setDate(new Date().getDate() - 5)); | |
const THREE_DAYS_AGO = new Date(new Date().setDate(new Date().getDate() - 3)); | |
const STATES_COMPARE = ["COMMENTED", "CHANGES_REQUESTED"]; | |
// Function to fetch pull requests | |
async function fetchPullRequests() { | |
return await github.paginate(github.rest.pulls.list, { | |
owner: 'rancher', | |
repo: 'shepherd', | |
state: 'open', | |
per_page: 100, | |
}); | |
} | |
// Function to fetch reviews for a pull request | |
async function fetchReviews(owner, repo, pull_number) { | |
return await github.rest.pulls.listReviews({ | |
owner, | |
repo, | |
pull_number, | |
per_page: 100, | |
}); | |
} | |
// Process pull requests and compile report | |
async function processPullRequests(prs) { | |
let report = { | |
noReviewsReport: '', | |
needsReviews: '', | |
needsChanges: '', | |
needsMerge: '', | |
hasErrors: '' | |
}; | |
for (const pr of prs) { | |
const createdDate = new Date(pr.created_at); | |
let lastReviewDate = null; | |
let lastReviewStateOne = ""; | |
let lastReviewStateTwo = ""; | |
let numberOfApprovedState = 0; | |
try { | |
const reviews = await fetchReviews('rancher', 'shepherd', pr.number); | |
if (reviews.data.length == 0 && createdDate < FIVE_DAYS_AGO) { | |
report.noReviewsReport += `${pr.title} #${pr.number} ${pr.html_url} ${pr.user.login}\n`; | |
} else { | |
processReviews(reviews.data, &lastReviewDate, &lastReviewStateOne, &lastReviewStateTwo, &numberOfApprovedState); | |
} | |
} catch (error) { | |
report.hasErrors += `${pr.title} (#${pr.number}) ${pr.html_url} - Error: ${error} \n`; | |
} | |
classifyPR(pr, lastReviewDate, lastReviewStateOne, lastReviewStateTwo, numberOfApprovedState, report); | |
} | |
return compileFullReport(report); | |
} | |
// Process reviews to determine the latest review states | |
function processReviews(reviews, lastReviewDate, lastReviewStateOne, lastReviewStateTwo, numberOfApprovedState) { | |
for (const review of reviews) { | |
const reviewDate = new Date(review.submitted_at); | |
if (!lastReviewDate || reviewDate > lastReviewDate) { | |
lastReviewDate = reviewDate; | |
lastReviewStateTwo = lastReviewStateOne; | |
lastReviewStateOne = review.state; | |
if (review.state === "APPROVED") { | |
numberOfApprovedState++; | |
} | |
} | |
} | |
} | |
// Classify pull requests based on review states and times | |
function classifyPR(pr, lastReviewDate, lastReviewStateOne, lastReviewStateTwo, numberOfApprovedState, report) { | |
const updatedDate = new Date(pr.updated_at); | |
if (lastReviewDate) { | |
if (lastReviewDate < THREE_DAYS_AGO && updatedDate <= lastReviewDate && STATES_COMPARE.includes(lastReviewStateOne) && STATES_COMPARE.includes(lastReviewStateTwo)) { | |
report.needsChanges += `${pr.title} (#${pr.number}) ${pr.html_url} ${pr.user.login} \n`; | |
} else if (updatedDate > lastReviewDate && updatedDate < THREE_DAYS_AGO) { | |
report.needsReviews += `${pr.title} (#${pr.number}) ${pr.html_url} ${pr.user.login} \n`; | |
} else if (numberOfApprovedState >= 2 && !STATES_COMPARE.includes(lastReviewStateOne)) { | |
report.needsMerge += `${pr.title} (#${pr.number}) ${pr.html_url} ${pr.user.login} \n`; | |
} | |
} | |
} | |
// Compile the full report from segments | |
function compileFullReport(report) { | |
let fullReport = ''; | |
if (report.noReviewsReport) { | |
fullReport += `PRs with no reviews yet: \n` + report.noReviewsReport +`\n\n`; | |
} | |
if (report.needsReviews) { | |
fullReport += `PRs that need reviews: \n` + report.needsReviews +`\n\n`; | |
} | |
if (report.needsChanges) { | |
fullReport += `PRs with changes requested: \n` + report.needsChanges +`\n\n`; | |
} | |
if (report.needsMerge) { | |
fullReport += `PRs that need to be merged: \n` + report.needsMerge; | |
} | |
if (report.hasErrors) { | |
fullReport += `PRs that have errors when fetching: \n` + report.hasErrors; | |
} | |
return fullReport; | |
} | |
const prs = await fetchPullRequests(); | |
return processPullRequests(prs); | |
result-encoding: string | |
- name: Notify on Slack | |
run: | | |
result="${{ steps.compile_report.outputs.result }}" | |
if [[ -n "$result" ]]; then | |
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"PR Status Report:\n$result\"}" ${{ secrets.SLACK_WEBHOOK_URL }} | |
else | |
curl -X POST -H 'Content-type: application/json' --data '{"text":"PR Status Report: There are errors please check the GH actions logs"}' ${{ secrets.SLACK_WEBHOOK_URL }} | |
fi | |
shell: bash |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment