From cbe0f067b7435d58b168aa214b8dc0f5a8f07584 Mon Sep 17 00:00:00 2001 From: Adam Dyess Date: Mon, 16 Sep 2024 15:33:03 -0500 Subject: [PATCH 1/6] Automerge every 15-min any PR with passing tests labeled with 'automerge' --- .../workflows/auto-merge-successful-prs.yaml | 29 +++++++++++++++++++ .github/workflows/update-components.yaml | 4 +++ build-scripts/auto-merge-successful-pr.sh | 25 ++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 .github/workflows/auto-merge-successful-prs.yaml create mode 100755 build-scripts/auto-merge-successful-pr.sh diff --git a/.github/workflows/auto-merge-successful-prs.yaml b/.github/workflows/auto-merge-successful-prs.yaml new file mode 100644 index 000000000..957d4bad7 --- /dev/null +++ b/.github/workflows/auto-merge-successful-prs.yaml @@ -0,0 +1,29 @@ +name: Auto-merge Successful PRs + +on: + workflow_dispatch: + schedule: + - cron: "*/15 * * * *" # Every 15 minutes + +permissions: + contents: read + pull-requests: write + +jobs: + find_and_merge: + runs-on: ubuntu-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@v2 + with: + egress-policy: audit + - name: Checking out repo + uses: actions/checkout@v4 + + # Fetch open pull requests and check for status checks on automerge PRs + - name: Auto-merge pull requests if all status checks pass + env: + GITHUB_TOKEN: ${{ secrets.DEPLOY_KEY_TO_UPDATE_STRICT_BRANCH }} + run: | + build-scripts/hack/auto-merge-successful-prs.sh \ No newline at end of file diff --git a/.github/workflows/update-components.yaml b/.github/workflows/update-components.yaml index 7f9e43745..3793d83bf 100644 --- a/.github/workflows/update-components.yaml +++ b/.github/workflows/update-components.yaml @@ -56,5 +56,9 @@ jobs: title: "[${{ matrix.branch }}] Update component versions" body: "[${{ matrix.branch }}] Update component versions" branch: "autoupdate/sync/${{ matrix.branch }}" + labels: | + automerge + automated pr + component update delete-branch: true base: ${{ matrix.branch }} diff --git a/build-scripts/auto-merge-successful-pr.sh b/build-scripts/auto-merge-successful-pr.sh new file mode 100755 index 000000000..30a9a1261 --- /dev/null +++ b/build-scripts/auto-merge-successful-pr.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e + +# Fetch the open pull requests +prs=$(gh pr list --state open --json number,headRefName,labels | jq '[.[] | select(.labels | any(.name == "automerge"))]') + +for pr in $(echo "$prs" | jq -r '.[] | @base64'); do + _jq() { + echo ${pr} | base64 --decode | jq -r ${1} + } + + pr_number=$(_jq '.number') + head_branch=$(_jq '.headRefName') + + # Check status checks for each PR + checks_passed=$(gh pr checks $pr_number --json bucket | jq -r '.[].bucket == "pass"' | sort | uniq) + +if [[ "$checks_passed" == "true" ]]; then + echo "All status checks passed for PR #$pr_number. Proceeding with merge..." + gh pr merge $pr_number --auto --squash +else + echo "Status checks have not passed for PR #$pr_number. Skipping merge." +fi +done From 29f1a6e5c0dcdd6f7de1ffdea6ac50dadf691a48 Mon Sep 17 00:00:00 2001 From: Adam Dyess Date: Mon, 16 Sep 2024 16:28:34 -0500 Subject: [PATCH 2/6] Make sure the bot can approve the PRs too --- build-scripts/auto-merge-successful-pr.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build-scripts/auto-merge-successful-pr.sh b/build-scripts/auto-merge-successful-pr.sh index 30a9a1261..e526e7b98 100755 --- a/build-scripts/auto-merge-successful-pr.sh +++ b/build-scripts/auto-merge-successful-pr.sh @@ -18,6 +18,7 @@ for pr in $(echo "$prs" | jq -r '.[] | @base64'); do if [[ "$checks_passed" == "true" ]]; then echo "All status checks passed for PR #$pr_number. Proceeding with merge..." + gh pr review $pr_number --approve -b "All status checks passed for PR #$pr_number." gh pr merge $pr_number --auto --squash else echo "Status checks have not passed for PR #$pr_number. Skipping merge." From fec52d53ed1beb660917bfd03528934ecfa2141b Mon Sep 17 00:00:00 2001 From: Adam Dyess Date: Mon, 16 Sep 2024 16:59:21 -0500 Subject: [PATCH 3/6] Update Bot information only if git email currently unset --- build-scripts/patches/moonray/apply | 9 ++++++--- build-scripts/patches/strict/apply | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/build-scripts/patches/moonray/apply b/build-scripts/patches/moonray/apply index 1233dae42..32a2f8510 100755 --- a/build-scripts/patches/moonray/apply +++ b/build-scripts/patches/moonray/apply @@ -2,9 +2,12 @@ DIR="$(realpath "$(dirname "${0}")")" -# Configure git author -git config user.email k8s-bot@canonical.com -git config user.name k8s-bot +# Configure git author if unset +git_email=$(git config --default "" user.email) +if [ -z "${git_email}" ]; then + git config user.email k8s-team-ci@canonical.com + git config user.name 'k8s-team-ci (CDK Bot)' +fi # Remove unrelated tests rm "${DIR}/../../../tests/integration/tests/test_cilium_e2e.py" diff --git a/build-scripts/patches/strict/apply b/build-scripts/patches/strict/apply index 1729742e2..3f6f7de14 100755 --- a/build-scripts/patches/strict/apply +++ b/build-scripts/patches/strict/apply @@ -3,8 +3,11 @@ DIR="$(realpath "$(dirname "${0}")")" # Configure git author -git config user.email k8s-bot@canonical.com -git config user.name k8s-bot +git_email=$(git config --default "" user.email) +if [ -z "${git_email}" ]; then + git config user.email k8s-team-ci@canonical.com + git config user.name 'k8s-team-ci (CDK Bot)' +fi # Apply strict patch git am "${DIR}/0001-Strict-patch.patch" From 4a4bac8ee47a729e1c4c292df058b8452c7e5073 Mon Sep 17 00:00:00 2001 From: Adam Dyess Date: Mon, 16 Sep 2024 22:16:08 -0500 Subject: [PATCH 4/6] consistently use private key secret to setup ssh git-remote --- .github/workflows/auto-merge-successful-prs.yaml | 6 +++--- .github/workflows/update-components.yaml | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/auto-merge-successful-prs.yaml b/.github/workflows/auto-merge-successful-prs.yaml index 957d4bad7..cd4b42fae 100644 --- a/.github/workflows/auto-merge-successful-prs.yaml +++ b/.github/workflows/auto-merge-successful-prs.yaml @@ -20,10 +20,10 @@ jobs: egress-policy: audit - name: Checking out repo uses: actions/checkout@v4 + with: + ssh-key: ${{ secrets.DEPLOY_KEY_TO_UPDATE_STRICT_BRANCH }} # Fetch open pull requests and check for status checks on automerge PRs - name: Auto-merge pull requests if all status checks pass - env: - GITHUB_TOKEN: ${{ secrets.DEPLOY_KEY_TO_UPDATE_STRICT_BRANCH }} run: | - build-scripts/hack/auto-merge-successful-prs.sh \ No newline at end of file + build-scripts/hack/auto-merge-successful-prs.sh diff --git a/.github/workflows/update-components.yaml b/.github/workflows/update-components.yaml index 3793d83bf..c8cf17d66 100644 --- a/.github/workflows/update-components.yaml +++ b/.github/workflows/update-components.yaml @@ -51,7 +51,6 @@ jobs: - name: Create pull request uses: peter-evans/create-pull-request@v6 with: - git-token: ${{ secrets.DEPLOY_KEY_TO_UPDATE_STRICT_BRANCH }} commit-message: "[${{ matrix.branch }}] Update component versions" title: "[${{ matrix.branch }}] Update component versions" body: "[${{ matrix.branch }}] Update component versions" From 1abd2ec82aa935af80282562e2cf6080d00355c5 Mon Sep 17 00:00:00 2001 From: Adam Dyess Date: Mon, 16 Sep 2024 22:22:50 -0500 Subject: [PATCH 5/6] Rename secret to BOT_SSH_KEY --- .github/workflows/auto-merge-successful-prs.yaml | 2 +- .github/workflows/update-branches.yaml | 2 +- .github/workflows/update-components.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-merge-successful-prs.yaml b/.github/workflows/auto-merge-successful-prs.yaml index cd4b42fae..222a1a737 100644 --- a/.github/workflows/auto-merge-successful-prs.yaml +++ b/.github/workflows/auto-merge-successful-prs.yaml @@ -21,7 +21,7 @@ jobs: - name: Checking out repo uses: actions/checkout@v4 with: - ssh-key: ${{ secrets.DEPLOY_KEY_TO_UPDATE_STRICT_BRANCH }} + ssh-key: ${{ secrets.BOT_SSH_KEY }} # Fetch open pull requests and check for status checks on automerge PRs - name: Auto-merge pull requests if all status checks pass diff --git a/.github/workflows/update-branches.yaml b/.github/workflows/update-branches.yaml index 356bbce5b..b6ed8f38e 100644 --- a/.github/workflows/update-branches.yaml +++ b/.github/workflows/update-branches.yaml @@ -41,7 +41,7 @@ jobs: - name: Sync ${{ github.ref }} to ${{ steps.determine.outputs.branch }} uses: actions/checkout@v4 with: - ssh-key: ${{ secrets.DEPLOY_KEY_TO_UPDATE_STRICT_BRANCH }} + ssh-key: ${{ secrets.BOT_SSH_KEY }} - name: Apply ${{ matrix.patch }} patch run: | git checkout -b ${{ steps.determine.outputs.branch }} diff --git a/.github/workflows/update-components.yaml b/.github/workflows/update-components.yaml index c8cf17d66..6aa962f19 100644 --- a/.github/workflows/update-components.yaml +++ b/.github/workflows/update-components.yaml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ matrix.branch }} - ssh-key: ${{ secrets.DEPLOY_KEY_TO_UPDATE_STRICT_BRANCH }} + ssh-key: ${{ secrets.BOT_SSH_KEY }} - name: Setup Python uses: actions/setup-python@v5 From 8a74c29eb31477a3b2c1bf7b39608e037fafcfe5 Mon Sep 17 00:00:00 2001 From: Adam Dyess Date: Tue, 17 Sep 2024 08:57:37 -0500 Subject: [PATCH 6/6] Reimagine auto-merge scripts as python --- .../workflows/auto-merge-successful-prs.yaml | 11 ++-- .github/workflows/update-components.yaml | 2 - build-scripts/auto-merge-successful-pr.sh | 26 --------- .../hack/auto-merge-successful-pr.py | 55 +++++++++++++++++++ 4 files changed, 61 insertions(+), 33 deletions(-) delete mode 100755 build-scripts/auto-merge-successful-pr.sh create mode 100755 build-scripts/hack/auto-merge-successful-pr.py diff --git a/.github/workflows/auto-merge-successful-prs.yaml b/.github/workflows/auto-merge-successful-prs.yaml index 222a1a737..ed9b6afd3 100644 --- a/.github/workflows/auto-merge-successful-prs.yaml +++ b/.github/workflows/auto-merge-successful-prs.yaml @@ -3,14 +3,14 @@ name: Auto-merge Successful PRs on: workflow_dispatch: schedule: - - cron: "*/15 * * * *" # Every 15 minutes + - cron: "0 */4 * * *" # Every 4 hours permissions: contents: read pull-requests: write jobs: - find_and_merge: + merge-successful-prs: runs-on: ubuntu-latest steps: @@ -22,8 +22,9 @@ jobs: uses: actions/checkout@v4 with: ssh-key: ${{ secrets.BOT_SSH_KEY }} - - # Fetch open pull requests and check for status checks on automerge PRs + - uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Auto-merge pull requests if all status checks pass run: | - build-scripts/hack/auto-merge-successful-prs.sh + build-scripts/hack/auto-merge-successful-prs.py diff --git a/.github/workflows/update-components.yaml b/.github/workflows/update-components.yaml index 6aa962f19..23aa952a4 100644 --- a/.github/workflows/update-components.yaml +++ b/.github/workflows/update-components.yaml @@ -57,7 +57,5 @@ jobs: branch: "autoupdate/sync/${{ matrix.branch }}" labels: | automerge - automated pr - component update delete-branch: true base: ${{ matrix.branch }} diff --git a/build-scripts/auto-merge-successful-pr.sh b/build-scripts/auto-merge-successful-pr.sh deleted file mode 100755 index e526e7b98..000000000 --- a/build-scripts/auto-merge-successful-pr.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -set -e - -# Fetch the open pull requests -prs=$(gh pr list --state open --json number,headRefName,labels | jq '[.[] | select(.labels | any(.name == "automerge"))]') - -for pr in $(echo "$prs" | jq -r '.[] | @base64'); do - _jq() { - echo ${pr} | base64 --decode | jq -r ${1} - } - - pr_number=$(_jq '.number') - head_branch=$(_jq '.headRefName') - - # Check status checks for each PR - checks_passed=$(gh pr checks $pr_number --json bucket | jq -r '.[].bucket == "pass"' | sort | uniq) - -if [[ "$checks_passed" == "true" ]]; then - echo "All status checks passed for PR #$pr_number. Proceeding with merge..." - gh pr review $pr_number --approve -b "All status checks passed for PR #$pr_number." - gh pr merge $pr_number --auto --squash -else - echo "Status checks have not passed for PR #$pr_number. Skipping merge." -fi -done diff --git a/build-scripts/hack/auto-merge-successful-pr.py b/build-scripts/hack/auto-merge-successful-pr.py new file mode 100755 index 000000000..5716bc478 --- /dev/null +++ b/build-scripts/hack/auto-merge-successful-pr.py @@ -0,0 +1,55 @@ +#!/bin/env python3 + +import shlex +import subprocess +import json + +LABEL = "automerge" +APPROVE_MSG = "All status checks passed for PR #{}." + + +def sh(cmd: str) -> str: + """Run a shell command and return its output.""" + _pipe = subprocess.PIPE + result = subprocess.run(shlex.split(cmd), stdout=_pipe, stderr=_pipe, text=True) + if result.returncode != 0: + raise Exception(f"Error running command: {cmd}\nError: {result.stderr}") + return result.stdout.strip() + + +def get_pull_requests() -> list: + """Fetch open pull requests matching some label.""" + prs_json = sh("gh pr list --state open --json number,labels") + prs = json.loads(prs_json) + return [pr for pr in prs if any(label["name"] == LABEL for label in pr["labels"])] + + +def check_pr_passed(pr_number) -> bool: + """Check if all status checks passed for the given PR.""" + checks_json = sh(f"gh pr checks {pr_number} --json bucket") + checks = json.loads(checks_json) + return all(check["bucket"] == "pass" for check in checks) + + +def approve_and_merge_pr(pr_number) -> None: + """Approve and merge the PR.""" + print(APPROVE_MSG.format(pr_number) + "Proceeding with merge...") + sh(f'gh pr review {pr_number} --approve -b "{APPROVE_MSG.format(pr_number)}"') + sh(f"gh pr merge {pr_number} --auto --squash") + + +def process_pull_requests(): + """Process the PRs and merge if checks have passed.""" + prs = get_pull_requests() + + for pr in prs: + pr_number: int = pr["number"] + + if check_pr_passed(pr_number): + approve_and_merge_pr(pr_number) + else: + print(f"Status checks have not passed for PR #{pr_number}. Skipping merge.") + + +if __name__ == "__main__": + process_pull_requests()