From f86a53a84ce7e967b9f3a1f2786e9e461f7ad173 Mon Sep 17 00:00:00 2001 From: James Frost Date: Thu, 18 Jan 2024 14:48:57 +0000 Subject: [PATCH] Add review checking script --- .github/workflows/check-required-reviews.yml | 47 ++++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/.github/workflows/check-required-reviews.yml b/.github/workflows/check-required-reviews.yml index ba2de98dc..5630c5a70 100644 --- a/.github/workflows/check-required-reviews.yml +++ b/.github/workflows/check-required-reviews.yml @@ -20,19 +20,50 @@ jobs: - name: Check reviews env: - PR_REF: github.ref + REF: ${{ github.ref }} + REPO: ${{ github.repository }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} shell: python run: | + import json import os + import re + import subprocess import sys - ref = os.getenv("PR_REF") - print(f"Ref: {ref}") + REQUIRED_REVIEWS = 3 + ref = os.getenv("REF") + repo = os.getenv("REPO") - # Get info with gh pr -R MetOffice/CSET view $PR_NUMBER --json - # labels,reviews If full_review label exists parse with program to see - # if enough reviews. Newer reviews by the same person should - # invalidate older reviews. + # Get PR number from ref. Will crash if doesn't match -> failed exit code. + pr_number = re.fullmatch(r"refs/pulls/([0-9]+)/merge", ref).group(1) - sys.exit(0) + # Get info on PR labels and reviews. + pr_info = json.loads( + subprocess.run( + ("gh", "pr", "--json", "labels,reviews", "--repo", repo, "view", pr_number), + check=True, + capture_output=True, + timeout=60, + text=True, + ).stdout + ) + + # Exit early if PR not labeled with full_review. + if not any(label["name"] == "full_review" for label in pr_info["labels"]): + print('Skipping PR without "full_review" label.') + sys.exit(0) + + # Check for enough reviews. Newer reviews invalidate older ones. + approving_reviewers = set() + for review in sorted(pr_info["reviews"], key=lambda r: r["submittedAt"]): + if review["state"] == "APPROVED": + approving_reviewers.add(review["author"]["login"]) + elif review["state"] == "CHANGES_REQUESTED": + approving_reviewers.discard(review["author"]["login"]) + + if len(approving_reviewers) < REQUIRED_REVIEWS: + print(f"Not enough reviews: {len(approving_reviewers)}/{REQUIRED_REVIEWS}") + sys.exit(1) + else: + print(f"Sufficient reviews: {len(approving_reviewers)}/{REQUIRED_REVIEWS}")