This is an automated email from the ASF dual-hosted git repository.

davidarthur pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/kafka.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 00b1b1a1445 KAFKA-17628 New workflows for automating run approvals 
(#17290)
00b1b1a1445 is described below

commit 00b1b1a1445c4c1c2594b12042a1ff1a63d9a7cc
Author: David Arthur <[email protected]>
AuthorDate: Thu Sep 26 20:17:25 2024 -0400

    KAFKA-17628 New workflows for automating run approvals (#17290)
    
    This patch allows workflow runs to be controlled by the ci-approved label 
on Pull Requests. Rather than manually approving each workflow run explicitly, 
committers can now add the appropriate label and the new "CI Requested" and "PR 
Labeled" workflows will auto-approve the requested run.
    
    Reviewers: Chia-Ping Tsai <[email protected]>
---
 .github/actions/gh-api-approve-run/action.yml |  51 +++++++++++
 .github/workflows/ci-requested.yml            |  83 ++++++++++++++++++
 .github/workflows/pr-labeled.yml              |  66 +++++++++++++++
 committer-tools/README.md                     |  14 ----
 committer-tools/approve-workflows.py          | 116 --------------------------
 5 files changed, 200 insertions(+), 130 deletions(-)

diff --git a/.github/actions/gh-api-approve-run/action.yml 
b/.github/actions/gh-api-approve-run/action.yml
new file mode 100644
index 00000000000..c124674132c
--- /dev/null
+++ b/.github/actions/gh-api-approve-run/action.yml
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+---
+name: "Approve Workflow Run"
+description: "Approve a Workflow run that has been submitted by a 
non-committer"
+inputs:
+  gh-token:
+    description: "The GitHub token for use with the CLI"
+    required: true
+  repository:
+    description: "The GitHub repository"
+    required: true
+    default: "apache/kafka"
+  run_id:
+    description: "The Workflow Run ID"
+    required: true
+  pr_number:
+    description: "The Pull Request number"
+    required: true
+  commit_sha:
+    description: "The SHA of the commit the run is for"
+    required: true
+
+runs:
+  using: "composite"
+  steps:
+    - name: Approve Workflow Run
+      shell: bash
+      env:
+        GH_TOKEN: ${{ inputs.gh-token }}
+      run: |
+        echo "Approving workflow run ${{ inputs.run_id }} for PR ${{ 
inputs.pr_number }} at SHA ${{ inputs.commit_sha }}";
+        gh api --method POST \
+              -H 'Accept: application/vnd.github+json' \
+              -H 'X-GitHub-Api-Version: 2022-11-28' \
+              /repos/${{ inputs.repository }}/actions/runs/${{ inputs.run_id 
}}/approve
diff --git a/.github/workflows/ci-requested.yml 
b/.github/workflows/ci-requested.yml
new file mode 100644
index 00000000000..cb1c7bdd0a9
--- /dev/null
+++ b/.github/workflows/ci-requested.yml
@@ -0,0 +1,83 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: CI Requested
+
+on:
+  workflow_run:
+    workflows: [CI]
+    types:
+      - requested
+
+jobs:
+  check-pr-labels:
+    # Even though job conditionals are difficult to debug, this will reduce 
the number of unnecessary runs
+    if: |
+      github.event_name == 'workflow_run' && 
+      github.event.workflow_run.event == 'pull_request' &&
+      github.event.workflow_run.status == 'completed' && 
+      github.event.workflow_run.conclusion == 'action_required'
+    runs-on: ubuntu-latest
+    steps:
+      - name: Env
+        run: printenv
+        env:
+          GITHUB_CONTEXT: ${{ toJson(github) }}
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          persist-credentials:
+            false
+      - name: Check PR Labels
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          RUN_ID: ${{ github.event.workflow_run.id }}
+          HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
+          HEAD_REPO: ${{ github.event.workflow_run.head_repository.owner.login 
}}
+        # Caution! This is a bit hacky. The GH documentation shows that the 
workflow_run event should include a list
+        # of referencing pull_requests. I think this might only be the case 
for pull requests originating from the
+        # base repository. To deal with fork PRs, we need to query the API for 
PRs for the owner's branch. This
+        # code assumes that the fork repo owner is the same as the 
organization for the "org:branch" syntax used
+        # in the query. Also, only the first matching PR from that org will be 
considered.
+        run: |
+          set +e
+          PR_NUMBER=$(gh api \
+            -H "Accept: application/vnd.github+json" \
+            -H "X-GitHub-Api-Version: 2022-11-28" \
+            /repos/${{ github.repository }}/pulls?head=$HEAD_REPO:$HEAD_BRANCH 
\
+            --jq '.[0].number')
+          if [ -z "$PR_NUMBER" ]; then
+            echo "Could not find the PR that triggered this workflow request";
+            exit 1;
+          fi
+          gh pr view $PR_NUMBER --json labels -q '.labels[].name' | grep -q 
'ci-approved'
+          exitcode="$?"
+          if [ $exitcode -ne 0 ]; then
+            echo "No ci-approved label set on PR #$PR_NUMBER. Will not 
auto-approve.";
+            exit 0;
+          else
+            echo "Found 'ci-approved' label on PR #$PR_NUMBER. Auto-approving 
workflow run $RUN_ID.";
+          fi
+          echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV"
+          echo "RUN_ID=$RUN_ID" >> "$GITHUB_ENV"
+      - name: Approve Workflow Run
+        if: env.RUN_ID != ''
+        uses: ./.github/actions/gh-api-approve-run
+        with:
+          gh-token: ${{ secrets.GITHUB_TOKEN }}
+          repository: ${{ github.repository }}
+          run_id: ${{ env.RUN_ID }}
+          pr_number: ${{ env.PR_NUMBER }}
+          commit_sha: ${{ github.event.workflow_run.head_sha }}
diff --git a/.github/workflows/pr-labeled.yml b/.github/workflows/pr-labeled.yml
new file mode 100644
index 00000000000..6773034f93d
--- /dev/null
+++ b/.github/workflows/pr-labeled.yml
@@ -0,0 +1,66 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+name: PR Labeled
+
+on:
+  pull_request_target:
+    types:
+      - labeled
+
+jobs:
+  approve-workflow-run:
+    # Even though job conditionals are difficult to debug, this will reduce 
the number of unnecessary runs
+    if:
+      github.event_name == 'pull_request_target' &&
+      github.event.action == 'labeled' &&
+      github.event.label.name == 'ci-approved'
+    runs-on: ubuntu-latest
+    steps:
+      - name: Env
+        run: printenv
+        env:
+          GITHUB_CONTEXT: ${{ toJson(github) }}
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          persist-credentials:
+            false
+      - name: Find Workflow Run ID
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          PR_NUMBER: ${{ github.event.number }}
+          SHA: ${{ github.event.pull_request.head.sha }}
+        run: |
+          set +e
+          echo "Found 'ci-approved' label on PR #$PR_NUMBER."
+          RUN_ID=$(gh run list -L 1 -c $SHA -s action_required --json 
databaseId --jq '.[].databaseId')
+          if [ -z "$RUN_ID" ]; then
+            echo "No workflow run found for SHA $SHA";
+            exit 0;
+          else
+            echo "Auto-approving workflow run $RUN_ID.";
+          fi;
+          echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV"
+          echo "RUN_ID=$RUN_ID" >> "$GITHUB_ENV"
+      - name: Approve Workflow Run
+        if: env.RUN_ID != ''
+        uses: ./.github/actions/gh-api-approve-run
+        with:
+          gh-token: ${{ secrets.GITHUB_TOKEN }}
+          repository: ${{ github.repository }}
+          run_id: ${{ env.RUN_ID }}
+          pr_number: ${{ env.PR_NUMBER }}
+          commit_sha: ${{ github.event.workflow_run.head_sha }}
diff --git a/committer-tools/README.md b/committer-tools/README.md
index 8052a3b2a6f..92558d4a296 100644
--- a/committer-tools/README.md
+++ b/committer-tools/README.md
@@ -87,20 +87,6 @@ Usage:
 python refresh_collaborators.py
 ```
 
-## Approve GitHub Action Workflows
-
-This script allows a committer to approve GitHub Action workflow runs from 
-non-committers. It fetches the latest 20 workflow runs that are in the 
-`action_required` state and prompts the user to approve the run.
-
-> This script requires the `gh` tool
-
-Usage:
-
-```bash
-python approve-workflows.py
-```
-
 ## Find Hanging Tests
 
 This script is used to infer hanging tests from the Gradle output. It looks for
diff --git a/committer-tools/approve-workflows.py 
b/committer-tools/approve-workflows.py
deleted file mode 100644
index 1f5a09efb47..00000000000
--- a/committer-tools/approve-workflows.py
+++ /dev/null
@@ -1,116 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-import subprocess
-import shlex
-import webbrowser
-
-
-def prompt_user() -> str:
-    choices = "ynw?"
-    while True:
-        try:
-            user_input = input(f"Make a selection [{','.join(choices)}]: ")
-        except (KeyboardInterrupt, EOFError):
-            exit(0)
-        clean_input = user_input.strip().lower()
-        if clean_input == "?":
-            print("\ny: approve the run\nn: do nothing\nw: open the URL for 
this run")
-            continue
-        if clean_input not in choices:
-            print(f"\nInvalid selection '{clean_input}'. Valid choices are:")
-            print("y: approve the run\nn: do nothing\nw: open the URL for this 
run")
-            continue
-        else:
-            return clean_input
-
-
-def run_gh_command(cmd: str) -> str:
-    "Run a gh CLI command and return the stdout"
-    p = subprocess.run(
-        shlex.split(cmd),
-        capture_output=True
-    )
-    if p.returncode == 4:
-        # Not auth'd
-        print(p.stderr.decode())
-        exit(1)
-    elif p.returncode == 0:
-        return p.stdout.decode()
-    else:
-        print(f"Had an error running '{cmd}'.\nSTDOUT: 
{p.stdout.decode()}\nSTDERR: {p.stderr.decode()}")
-
-
-def list_pending_workflow_runs():
-    command = r"gh run list --limit 20 --repo apache/kafka --workflow CI 
--status action_required --json 'databaseId,headBranch'"
-    out = run_gh_command(command)
-    pending_workflows = json.loads(out)
-    runs_by_branch = dict()
-    for workflow in pending_workflows:
-        run_id = workflow.get("databaseId")
-        branch = workflow.get("headBranch")
-        if branch in runs_by_branch:
-            print(f"Ignoring run {run_id} since there is a newer one: 
{runs_by_branch[branch]}")
-            continue
-        runs_by_branch[branch] = run_id
-    return runs_by_branch.values()
-
-def load_workflow_run(run_id: str):
-    command = rf"gh api --method GET -H 'Accept: application/vnd.github+json' 
-H 'X-GitHub-Api-Version: 2022-11-28' /repos/apache/kafka/actions/runs/{run_id}"
-    out = run_gh_command(command)
-    workflow_run = json.loads(out)
-    return workflow_run
-
-
-def approve_workflow_run(run_id: str):
-    command = rf"gh api --method POST -H 'Accept: application/vnd.github+json' 
-H 'X-GitHub-Api-Version: 2022-11-28' 
/repos/apache/kafka/actions/runs/{run_id}/approve"
-    out = run_gh_command(command)
-    workflow_run = json.loads(out)
-    return workflow_run
-
-
-if __name__ == "__main__":
-    """
-    Interactive script to approve pending CI workflow runs. This script can 
only be used by committers.
-    
-    Requires the GitHub CLI. See https://cli.github.com/ for installation 
instructions. 
-    
-    Once installed authenticate with: gh auth login 
-    """
-    pending_runs = list_pending_workflow_runs()
-    for run_id in pending_runs:
-        run = load_workflow_run(run_id)
-        branch = run.get("head_branch")
-        run_id = run.get("id")
-        title = run.get("display_title")
-        repo = run.get("head_repository", {}).get("full_name")
-        actor = run.get("actor", {}).get("login")
-        url = run.get("html_url")
-        updated = run.get("updated_at")
-        print("-"*80)
-        print(f"PR: {title}")
-        print(f"Actor: {actor}")
-        print(f"Branch: {repo} {branch}")
-        print(f"Updated: {updated}")
-        print(f"URL: {url}")
-        print("")
-        selection = prompt_user()
-        if selection == "y":
-            approve_workflow_run(run_id)
-        elif selection == "n":
-            continue
-        elif selection == "w":
-            webbrowser.open(url)

Reply via email to