Add triage workflow.
This commit is contained in:
parent
570edcc0cc
commit
cbda2c0f98
195
.github/workflows/triage.yml
vendored
Normal file
195
.github/workflows/triage.yml
vendored
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
name: Triage
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- reopened
|
||||||
|
- closed
|
||||||
|
- labeled
|
||||||
|
- unlabeled
|
||||||
|
schedule:
|
||||||
|
- cron: '0 */3 * * *' # every 3 hours
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: startsWith(github.repository, 'Homebrew/')
|
||||||
|
steps:
|
||||||
|
- name: Re-run this workflow
|
||||||
|
if: github.event_name == 'schedule' || github.event.action == 'closed'
|
||||||
|
uses: reitermarkus/rerun-workflow@cf91bee6964dfde64eccbf5600c3ea206af11359
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
|
||||||
|
continuous-label: waiting for feedback
|
||||||
|
trigger-labels: critical
|
||||||
|
workflow: triage.yml
|
||||||
|
- name: Review pull request
|
||||||
|
if: >
|
||||||
|
(github.event_name == 'pull_request' || github.event_name == 'pull_request_target') &&
|
||||||
|
github.event.action != 'closed'
|
||||||
|
uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
|
||||||
|
script: |
|
||||||
|
async function approvePullRequest(pullRequestNumber) {
|
||||||
|
const reviews = await approvalsByAuthenticatedUser(pullRequestNumber)
|
||||||
|
|
||||||
|
if (reviews.length > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await github.pulls.createReview({
|
||||||
|
...context.repo,
|
||||||
|
pull_number: pullRequestNumber,
|
||||||
|
event: 'APPROVE',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function findComment(pullRequestNumber, id) {
|
||||||
|
const { data: comments } = await github.issues.listComments({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: pullRequestNumber,
|
||||||
|
})
|
||||||
|
|
||||||
|
const regex = new RegExp(`<!--\\s*#${id}\\s*-->`)
|
||||||
|
return comments.filter(comment => comment.body.match(regex))[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createOrUpdateComment(pullRequestNumber, id, message) {
|
||||||
|
const beginComment = await findComment(pullRequestNumber, id)
|
||||||
|
|
||||||
|
const body = `<!-- #${id} -->\n\n${message}`
|
||||||
|
if (beginComment) {
|
||||||
|
await github.issues.updateComment({
|
||||||
|
...context.repo,
|
||||||
|
comment_id: beginComment.id,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await github.issues.createComment({
|
||||||
|
...context.repo,
|
||||||
|
issue_number: pullRequestNumber,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function approvalsByAuthenticatedUser(pullRequestNumber) {
|
||||||
|
const { data: user } = await github.users.getAuthenticated()
|
||||||
|
|
||||||
|
const { data: reviews } = await github.pulls.listReviews({
|
||||||
|
...context.repo,
|
||||||
|
pull_number: pullRequestNumber,
|
||||||
|
})
|
||||||
|
|
||||||
|
const approvals = reviews.filter(review => review.state == 'APPROVED')
|
||||||
|
return approvals.filter(review => review.user.login == user.login)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dismissApprovals(pullRequestNumber, message) {
|
||||||
|
const reviews = await approvalsByAuthenticatedUser(pullRequestNumber)
|
||||||
|
for (const review of reviews) {
|
||||||
|
await github.pulls.dismissReview({
|
||||||
|
...context.repo,
|
||||||
|
pull_number: pullRequestNumber,
|
||||||
|
review_id: review.id,
|
||||||
|
message: message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reviewPullRequest(pullRequestNumber) {
|
||||||
|
const { data: pullRequest } = await github.pulls.get({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
pull_number: pullRequestNumber,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (pullRequest.author_association != 'MEMBER') {
|
||||||
|
core.warning('Pull request author is not a member.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const reviewLabel = 'waiting for feedback'
|
||||||
|
const criticalLabel = 'critical'
|
||||||
|
|
||||||
|
const labels = pullRequest.labels.map(label => label.name)
|
||||||
|
const hasReviewLabel = labels.includes(reviewLabel)
|
||||||
|
const hasCriticalLabel = labels.includes(criticalLabel)
|
||||||
|
|
||||||
|
const reviewStartDate = new Date(pullRequest.created_at)
|
||||||
|
const reviewEndDate = new Date(reviewStartDate)
|
||||||
|
switch (reviewStartDate.getDay()) {
|
||||||
|
// Skip from Friday to Monday and from Saturday to Tuesday.
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
reviewEndDate.setDate(reviewStartDate.getDate() + 3)
|
||||||
|
break
|
||||||
|
// Skip from Sunday to Tuesday.
|
||||||
|
case 0:
|
||||||
|
reviewEndDate.setDate(reviewStartDate.getDate() + 2)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
reviewEndDate.setDate(reviewStartDate.getDate() + 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDate = new Date()
|
||||||
|
const reviewEnded = currentDate > reviewEndDate
|
||||||
|
|
||||||
|
function formatDate(date) {
|
||||||
|
return date.toISOString().replace(/\.\d+Z$/, ' UTC').replace('T', ' at ')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reviewEnded || hasCriticalLabel) {
|
||||||
|
let message
|
||||||
|
if (hasCriticalLabel && !reviewEnded) {
|
||||||
|
if (hasReviewLabel) {
|
||||||
|
message = `Review period cancelled due to \`${criticalLabel}\` label.`
|
||||||
|
} else {
|
||||||
|
message = `Review period skipped due to \`${criticalLabel}\` label.`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message = 'Review period ended.'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasReviewLabel) {
|
||||||
|
await github.issues.removeLabel({
|
||||||
|
...context.repo,
|
||||||
|
issue_number: pullRequestNumber,
|
||||||
|
name: reviewLabel,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(message)
|
||||||
|
await createOrUpdateComment(pullRequestNumber, 'review-period-end', message)
|
||||||
|
await approvePullRequest(pullRequestNumber)
|
||||||
|
} else {
|
||||||
|
const message = `Review period will end on ${formatDate(reviewEndDate)}.`
|
||||||
|
core.warning(message)
|
||||||
|
|
||||||
|
await dismissApprovals(pullRequestNumber, 'Review period has not ended yet.')
|
||||||
|
await createOrUpdateComment(pullRequestNumber, 'review-period-begin', message)
|
||||||
|
|
||||||
|
const endComment = await findComment(pullRequestNumber, 'review-period-end')
|
||||||
|
if (endComment) {
|
||||||
|
await github.issues.deleteComment({
|
||||||
|
...context.repo,
|
||||||
|
comment_id: endComment.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await github.issues.addLabels({
|
||||||
|
...context.repo,
|
||||||
|
issue_number: pullRequestNumber,
|
||||||
|
labels: [reviewLabel],
|
||||||
|
})
|
||||||
|
|
||||||
|
core.setFailed('Review period has not ended yet.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await reviewPullRequest(context.issue.number)
|
||||||
Loading…
x
Reference in New Issue
Block a user