diff options
-rw-r--r-- | .github/CODEOWNERS | 9 | ||||
-rw-r--r-- | .github/workflows/review-check.yaml | 166 |
2 files changed, 169 insertions, 6 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4a702e0c..6afbfb31 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,10 +1,7 @@ -# Request Dennis for any PR -* @Den4200 - # CI & Docker -.github/workflows/** @Akarys42 @SebastiaanZ -Dockerfile @Akarys42 -docker-compose.yml @Akarys42 +.github/workflows/** @Akarys42 @SebastiaanZ @Den4200 +Dockerfile @Akarys42 @Den4200 +docker-compose.yml @Akarys42 @Den4200 # Tools Pipfile* @Akarys42 diff --git a/.github/workflows/review-check.yaml b/.github/workflows/review-check.yaml new file mode 100644 index 00000000..3e45a4b5 --- /dev/null +++ b/.github/workflows/review-check.yaml @@ -0,0 +1,166 @@ +name: Review Check + +# This workflow needs to trigger in two situations: +# +# 1. When a pull request is opened, reopened, or synchronized (new commit) +# This is accomplished using the `pull_request_target` event that triggers in +# precisely those situations by default. I've opted for `pull_request_target` +# as we don't need to have access to the PR's code and it's safer to make the +# secrets we need available to the workflow compared to `pull_request`. +# +# The reason we need to run the workflow for this event is because we need to +# make sure that our check is part of the check suite for the current commit. +# +# 2. When a review is added or dismissed. +# Whenever reviews are submitted or dismissed, the number of Core Developer +# approvals may obviously change. +# +# --- +# +# Unfortunately, having two different event triggers means that can't let +# this workflow fail on its own, as GitHub actions registers a separate check +# run result per event trigger. As both triggers need to share the success/fail +# state, we get around that by registering a custom "status". +on: + pull_request_review: + types: + - submitted + - dismissed + pull_request_target: + + +jobs: + review-check: + name: Check Core Dev Reviews + runs-on: ubuntu-latest + + steps: + # Fetch the latest Opinionated reviews from users with write + # access. We can't narrow it down using a specific team here + # yet, so we'll do that later. + - uses: octokit/[email protected] + id: reviews + with: + query: | + query ($repository: String!, $pr: Int!) { + repository(owner: "python-discord", name: $repository) { + pullRequest(number: $pr) { + latestOpinionatedReviews(last: 100, writersOnly: true) { + nodes{ + author{ + login + } + state + } + } + } + } + } + repository: ${{ github.event.repository.name }} + pr: ${{ github.event.pull_request.number }} + env: + GITHUB_TOKEN: ${{ secrets.REPO_TOKEN }} + + # Fetch the members of the Core Developers team so we can + # check if any of them actually approved this PR. + - uses: octokit/[email protected] + id: core_developers + with: + query: | + query { + organization(login: "python-discord") { + team(slug: "core-developers") { + members(first: 100) { + nodes { + login + } + } + } + } + } + env: + GITHUB_TOKEN: ${{ secrets.TEAM_TOKEN }} + + # I've opted for a Python script, as that's what most of us + # are familiar with. We do need to setup Python for that. + - name: Setup python + id: python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + + # This is a small, inline Python script that looks for the + # intersection between approving reviewers and the core dev + # team. If that intersection exists, we have at least one + # approving Core Developer. + # + # I've opted to keep this inline as it's relatively small + # and this workflow will be added to multiple repositories. + - name: Check for Accepting Core Developers + id: core_dev_reviews + run: | + python -c 'import json + reviews = json.loads("""${{ steps.reviews.outputs.data }}""") + reviewers = { + review["author"]["login"] + for review in reviews["repository"]["pullRequest"]["latestOpinionatedReviews"]["nodes"] + if review["state"] == "APPROVED" + } + core_devs = json.loads("""${{ steps.core_developers.outputs.data }}""") + core_devs = { + member["login"] for member in core_devs["organization"]["team"]["members"]["nodes"] + } + approving_core_devs = reviewers & core_devs + approval_check = "success" if approving_core_devs else "failure" + print(f"::set-output name=approval_check::{approval_check}") + ' + + # This step registers a a new status for the head commit of the pull + # request. If a status with the same context and description already + # exists, it will be overwritten. The reason we have to do this is + # because workflows run for the separate `pull_request_target` and + #`pull_request_review` events need to share a single result state. + - name: Add Core Dev Approval status check + uses: octokit/[email protected] + with: + route: POST /repos/:repository/statuses/:sha + repository: ${{ github.repository }} + sha: ${{ github.event.pull_request.head.sha }} + state: ${{ steps.core_dev_reviews.outputs.approval_check }} + description: At least one core developer needs to approve this PR + context: Core Dev Approval + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # If we have at least one Core Developer approval, this step + # removes the 'waiting for core dev approval' label if it's + # still present for the PR. + - name: Remove "waiting for core dev approval" if a core dev approved this PR + if: >- + steps.core_dev_reviews.outputs.approval_check == 'success' && + contains(github.event.pull_request.labels.*.name, 'waiting for core dev approval') + uses: octokit/[email protected] + with: + route: DELETE /repos/:repository/issues/:number/labels/:label + repository: ${{ github.repository }} + number: ${{ github.event.pull_request.number }} + label: needs core dev approval + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # If we have do not have one Core Developer approval, this step + # adds the 'waiting for core dev approval' label if it's not + # already present for the PR. + - name: Add "waiting for core dev approval" if no core dev has approved yet + if: >- + steps.core_dev_reviews.outputs.approval_check == 'failure' && + !contains(github.event.pull_request.labels.*.name, 'waiting for core dev approval') + uses: octokit/[email protected] + with: + route: POST /repos/:repository/issues/:number/labels + repository: ${{ github.repository }} + number: ${{ github.event.pull_request.number }} + labels: | + - needs core dev approval + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |