mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-06 07:26:43 -04:00
CI - Simplify PR approval check (#4578)
# Description of Changes The previous version ended up incurring two different entries on the PR (one for the `pull_request` event and one for the `pull_request_review` event). Both versions were marked "required", so PRs could end up in an unmergeable state if one check had succeeded but the other had failed (e.g. if you submitted a PR approval, the previous `pull_request` version of the check would still be failed since it didn't refresh). See the entries at top and bottom here: <img width="481" height="225" alt="image" src="https://github.com/user-attachments/assets/5b7a4302-6bc2-47e9-93c8-812cb9ece60b" /> This PR fixes it by only allowing the `pull_request_review` events. I _think_ this covers all the cases, but I'm not sure. # API and ABI breaking changes None. # Expected complexity level and risk 1 # Testing I don't know how to test it really 🤷 --------- Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
This commit is contained in:
@@ -1,45 +1,106 @@
|
||||
name: PR Approval Check
|
||||
name: Review Checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
types: [opened, synchronize, reopened]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
types: [submitted, dismissed]
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
statuses: write
|
||||
|
||||
concurrency:
|
||||
group: pr-approval-check-${{ github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check-approvals:
|
||||
publish-approval-status:
|
||||
name: Set approval status
|
||||
runs-on: ubuntu-latest
|
||||
# Skip this check if PR is in draft state
|
||||
if: github.event.pull_request.draft == false
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Evaluate and publish approval status
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const contextName = "PR approval check";
|
||||
|
||||
- name: Check PR approvals
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Check if PR author is clockwork-labs-bot
|
||||
if [[ "${{ github.event.pull_request.user.login }}" != "clockwork-labs-bot" ]]; then
|
||||
echo "PR opened by ${{ github.event.pull_request.user.login }}, not clockwork-labs-bot. Skipping check."
|
||||
exit 0
|
||||
fi
|
||||
let targetSha;
|
||||
let state;
|
||||
let description;
|
||||
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
# Get approval count
|
||||
APPROVALS=$(gh pr view $PR_NUMBER --json reviews -q '.reviews | map(select(.state == "APPROVED")) | length')
|
||||
if (context.eventName === "merge_group") {
|
||||
targetSha = process.env.GITHUB_SHA;
|
||||
state = "success";
|
||||
description = "Merge group entry; approvals already satisfied";
|
||||
} else {
|
||||
const pr = context.payload.pull_request;
|
||||
targetSha = pr.head.sha;
|
||||
|
||||
echo "PR has $APPROVALS approvals"
|
||||
if (pr.user.login !== "clockwork-labs-bot") {
|
||||
state = "success";
|
||||
description = "PR author is not clockwork-labs-bot";
|
||||
} else {
|
||||
const result = await github.graphql(
|
||||
`
|
||||
query($owner: String!, $repo: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $number) {
|
||||
latestOpinionatedReviews(first: 100, writersOnly: true) {
|
||||
nodes {
|
||||
state
|
||||
author {
|
||||
login
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
number: pr.number,
|
||||
}
|
||||
);
|
||||
|
||||
if [[ $APPROVALS -lt 2 ]]; then
|
||||
echo "Error: PRs from clockwork-labs-bot require at least 2 approvals"
|
||||
exit 1
|
||||
else
|
||||
echo "PR has the required number of approvals"
|
||||
fi
|
||||
const effectiveApprovers =
|
||||
result.repository.pullRequest.latestOpinionatedReviews.nodes
|
||||
.filter((review) => review.state === "APPROVED")
|
||||
.map((review) => review.author?.login)
|
||||
.filter(Boolean);
|
||||
|
||||
core.info(
|
||||
`Latest effective approvers (${effectiveApprovers.length}): ${effectiveApprovers.join(", ")}`
|
||||
);
|
||||
|
||||
if (effectiveApprovers.length < 2) {
|
||||
state = "failure";
|
||||
description = "PRs from clockwork-labs-bot require at least 2 approvals";
|
||||
} else {
|
||||
state = "success";
|
||||
description = "PR has the required number of approvals";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
core.info(`Publishing status ${state} for ${targetSha}: ${description}`);
|
||||
|
||||
// We need to set a separate commit status for this, because it runs on both
|
||||
// pull_request and pull_request_review events. If we don't set an explicit context,
|
||||
// what happens is that there are sometimes two separate statuses on the same commit -
|
||||
// one from each event type. This leads to weird cases where one copy of the check is failed,
|
||||
// and the other is successful, and the failed one blocks the PR from merging.
|
||||
await github.rest.repos.createCommitStatus({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
sha: targetSha,
|
||||
state,
|
||||
context: contextName,
|
||||
description,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user