From cf3f4ec32e6da7f93823b56b643012917d9cf101 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Fri, 21 Jun 2024 15:00:11 +0200
Subject: [PATCH] ci: enable Automatic Merge of Approved Pull Requests

Add automation that will merge approved PRs with passed CI checks automatically.

It uses following GitHub Actions:

* https://github.com/redhat-plumbers-in-action/gather-pull-request-metadata - to gather important data about PRs (used in following actions)
* https://github.com/redhat-plumbers-in-action/pull-request-validator - to check if PR is approved and has passed CI checks, and more
* https://github.com/redhat-plumbers-in-action/auto-merge - to merge approved PRs
* https://github.com/redhat-plumbers-in-action/issue-commentator - to comment on PR with status of PR and merge status
---
 .github/auto-merge.yml                     |  1 +
 .github/workflows/auto-merge-on-demand.yml | 95 ++++++++++++++++++++++
 .github/workflows/auto-merge.yml           | 61 ++++++++++++++
 .github/workflows/pr-metadata.yml          | 27 ++++++
 4 files changed, 184 insertions(+)
 create mode 100644 .github/auto-merge.yml
 create mode 100644 .github/workflows/auto-merge-on-demand.yml
 create mode 100644 .github/workflows/auto-merge.yml
 create mode 100644 .github/workflows/pr-metadata.yml

diff --git a/.github/auto-merge.yml b/.github/auto-merge.yml
new file mode 100644
index 00000000..d0bfc425
--- /dev/null
+++ b/.github/auto-merge.yml
@@ -0,0 +1 @@
+target-branch': ['master']
diff --git a/.github/workflows/auto-merge-on-demand.yml b/.github/workflows/auto-merge-on-demand.yml
new file mode 100644
index 00000000..9ef8134e
--- /dev/null
+++ b/.github/workflows/auto-merge-on-demand.yml
@@ -0,0 +1,95 @@
+name: Auto Merge On Demand
+on:
+  schedule:
+    # Workflow runs every 45 minutes
+    - cron: '*/45 * * * *'
+  workflow_dispatch:
+    inputs:
+      pr-number:
+        description: 'Pull Request number/s ; when not provided, the workflow will run for all open PRs'
+        required: true
+        default: '0'
+
+permissions:
+  contents: read
+
+jobs:
+  # Get all open PRs
+  gather-pull-requests:
+    if: github.repository_owner == 'sclorg'
+    runs-on: ubuntu-latest
+
+    outputs:
+      pr-numbers: ${{ steps.get-pr-numbers.outputs.result }}
+      pr-numbers-manual: ${{ steps.parse-manual-input.outputs.result }}
+
+    steps:
+      - id: get-pr-numbers
+        if: inputs.pr-number == '0'
+        name: Get all open PRs
+        uses: actions/github-script@v6
+        with:
+          # !FIXME: this is not working if there is more than 100 PRs opened
+          script: |
+            const { data: pullRequests } = await github.rest.pulls.list({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              state: 'open',
+              per_page: 100
+            });
+            return pullRequests.map(pr => pr.number);
+
+      - id: parse-manual-input
+        if: inputs.pr-number != '0'
+        name: Parse manual input
+        run: |
+          # shellcheck disable=SC2086
+          echo "result="[ ${{ inputs.pr-number }} ]"" >> $GITHUB_OUTPUT
+        shell: bash
+
+  validate-pr:
+    name: 'Validation of Pull Request #${{ matrix.pr-number }}'
+    needs: [ gather-pull-requests ]
+    runs-on: ubuntu-latest
+
+    strategy:
+      fail-fast: false
+      matrix:
+        pr-number: ${{ inputs.pr-number == 0 && fromJSON(needs.gather-pull-requests.outputs.pr-numbers) || fromJSON(needs.gather-pull-requests.outputs.pr-numbers-manual) }}
+
+    permissions:
+      # required for merging PRs
+      contents: write
+      # required for PR comments and setting labels
+      pull-requests: write
+
+    steps:
+      - id: metadata
+        name: Gather Pull Request Metadata
+        uses: redhat-plumbers-in-action/gather-pull-request-metadata@v1
+        with:
+          pr-number: ${{ matrix.pr-number }}
+
+      - id: pull-request-validator
+        name: Pull Request Validator
+        uses: redhat-plumbers-in-action/pull-request-validator@v2
+        with:
+          pr-metadata: ${{ steps.metadata.outputs.metadata }}
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - id: auto-merge
+        name: Auto Merge
+        uses: redhat-plumbers-in-action/auto-merge@v2
+        with:
+          pr-metadata: ${{ steps.metadata.outputs.metadata }}
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - if: ${{ !cancelled() }}
+        name: Show results in PR comment
+        uses: redhat-plumbers-in-action/issue-commentator@v1
+        with:
+          issue: ${{ fromJSON(steps.metadata.outputs.metadata).number }}
+          message: |
+            ${{ steps.pull-request-validator.outputs.status && steps.pull-request-validator.outputs.status || '' }}
+            ${{ steps.auto-merge.outputs.status && steps.auto-merge.outputs.status || '' }}
+          token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml
new file mode 100644
index 00000000..13c4d838
--- /dev/null
+++ b/.github/workflows/auto-merge.yml
@@ -0,0 +1,61 @@
+name: Auto Merge
+on:
+  workflow_run:
+    workflows: [ Gather Pull Request Metadata ]
+    types:
+      - completed
+
+permissions:
+  contents: read
+
+jobs:
+  download-metadata:
+    if: >
+      github.event.workflow_run.event == 'pull_request' &&
+      github.event.workflow_run.conclusion == 'success'
+    runs-on: ubuntu-latest
+
+    outputs:
+      pr-metadata: ${{ steps.Artifact.outputs.pr-metadata-json }}
+
+    steps:
+      - id: Artifact
+        name: Download Artifact
+        uses: redhat-plumbers-in-action/download-artifact@v1
+        with:
+          name: pr-metadata
+
+  auto-merge:
+    needs: [ download-metadata ]
+    runs-on: ubuntu-latest
+
+    permissions:
+      # required for ability to merge Pull Request
+      contents: write
+      # required for setting labels
+      pull-requests: write
+
+    steps:
+      - id: pull-request-validator
+        name: Pull Request Validator
+        uses: redhat-plumbers-in-action/pull-request-validator@v2
+        with:
+          pr-metadata: ${{ needs.download-metadata.outputs.pr-metadata }}
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - id: auto-merge
+        name: Auto Merge
+        uses: redhat-plumbers-in-action/auto-merge@v2
+        with:
+          pr-metadata: ${{ needs.download-metadata.outputs.pr-metadata }}
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+      - if: ${{ !cancelled() }}
+        name: Show results in PR comment
+        uses: redhat-plumbers-in-action/issue-commentator@v1
+        with:
+          issue: ${{ fromJSON(needs.download-metadata.outputs.pr-metadata).number }}
+          message: |
+            ${{ steps.pull-request-validator.outputs.status && steps.pull-request-validator.outputs.status || '' }}
+            ${{ steps.auto-merge.outputs.status && steps.auto-merge.outputs.status || '' }}
+          token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/pr-metadata.yml b/.github/workflows/pr-metadata.yml
new file mode 100644
index 00000000..e4c77dbe
--- /dev/null
+++ b/.github/workflows/pr-metadata.yml
@@ -0,0 +1,27 @@
+name: Gather Pull Request Metadata
+on:
+  pull_request:
+    types: [ opened, reopened, synchronize ]
+    branches: [ master ]
+
+permissions:
+  contents: read
+
+jobs:
+  gather-metadata:
+    if: github.repository_owner == 'sclorg'
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Repository checkout
+        uses: actions/checkout@v4
+
+      - id: Metadata
+        name: Gather Pull Request Metadata
+        uses: redhat-plumbers-in-action/gather-pull-request-metadata@v1
+
+      - name: Upload artifact with gathered metadata
+        uses: actions/upload-artifact@v4
+        with:
+          name: pr-metadata
+          path: ${{ steps.Metadata.outputs.metadata-file }}