From 60aae35ee32cd34e99511e2f70979bb479a0a38f Mon Sep 17 00:00:00 2001 From: Freeman <105403280+F-WRunTime@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:50:59 -0700 Subject: [PATCH] modified: src/automerge.py (#5) ithub/workflows/test.yaml - Updating workflow test to use the GH APP credentials modified: README.md - Modifying instructions and information needed for github and what permissions are needed for the github App --- .github/workflows/test.yaml | 26 +++++++++++++++++++++++--- README.md | 26 ++++++++++++++++++++++---- src/automerge.py | 30 +++++++++++------------------- 3 files changed, 56 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7bcab26..8ba6005 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,13 +12,23 @@ jobs: runs-on: [ubuntu-latest] outputs: matrix: ${{ steps.list.outputs.value }} + token: ${{ steps.automerge_token.outputs.token }} steps: + - name: 'Generate GitHub App Token' + id: automerge_token + uses: actions/create-github-app-token@v1.11.0 + with: + app-id: ${{ secrets.AUTOMERGE_APP_ID }} + private-key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} - name: 'Check out devops repo' + env: + token: ${{ steps.automerge_token.outputs.token }} uses: actions/checkout@v4.0.0 - id: list name: 'List automerge repos' run: echo "value=$(cat test/automerge.json | tr -d '\n')" >> $GITHUB_OUTPUT - + automerge-test: name: 'Automerge' runs-on: [ubuntu-latest] @@ -28,7 +38,17 @@ jobs: matrix: value: ${{fromJson(needs.list.outputs.matrix)}} steps: + - name: 'Generate GitHub App Token' + id: automerge_token + uses: actions/create-github-app-token@v1.11.0 + with: + app-id: ${{ secrets.AUTOMERGE_APP_ID }} + private-key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + - name: 'Check Automerge Repo to Test' + env: + token: ${{ steps.automerge_token.outputs.token }} uses: actions/checkout@v4 - name: 'Automerge runtimeverification/${{ matrix.value }}' @@ -37,7 +57,7 @@ jobs: with: org: 'runtimeverification' repo: ${{ matrix.value }} - token: ${{ secrets.JENKINS_GITHUB_PAT }} + token: ${{ steps.automerge_token.outputs.token }} debug: --dry-run - name: 'Automerge runtimeverification/${{ matrix.value }}' @@ -46,7 +66,7 @@ jobs: with: org: 'runtimeverification' repo: ${{ matrix.value }} - token: ${{ secrets.JENKINS_GITHUB_PAT }} + token: ${{ steps.automerge_token.outputs.token }} debug: --dry-run comment: 'true' \ No newline at end of file diff --git a/README.md b/README.md index 51508a7..1d4cea4 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,17 @@ Any PR with the following criteria will be updated and test will be run before m - PR is passing PR Tests - PR is out-of-date +A Github App is required to generate the appropriate Permissions for Automerge to work. +- The Github App is not public and requires each org to generate their own. + Adding a token generation step to their automerge workflow. See Example Workflow below. +- Specific repositories can be granted access using the app instead of ALL repositories under the org. + +Github App Permissions: +- Content Read/Write -- For Updating PRs +- Pull Request Read/Write -- For Updating PRs +- Repository Administration Read -- For read access to repositories under the Org. +- Checks Read -- For reading the check statuses of the PR + ## Table of Contents - [Automerge PR Action](#automerge-pr-action) - [Table of Contents](#table-of-contents) @@ -62,7 +73,13 @@ jobs: - id: list name: 'List automerge repos' run: echo "value=$(cat test/automerge.json | tr -d '\n')" >> $GITHUB_OUTPUT - + - name: 'Generate GitHub App Token' + id: automerge_token + uses: actions/create-github-app-token@v1.11.0 + with: + app_id: ${{ secrets.AUTOMERGE_APP_ID }} + private_key: ${{ secrets.AUTOMERGE_APP_PRIVATE_KEY }} + automerge-test: name: 'Automerge' runs-on: [ubuntu-latest] @@ -73,7 +90,7 @@ jobs: value: ${{fromJson(needs.list.outputs.matrix)}} steps: - name: 'Automerge runtimeverification/${{ matrix.value }}' - uses: ./ # This uses the action in the root directory + uses: runtimeverification/automerge@v1.0.4 # This uses the action in the root directory with: org: 'runtimeverification' # As long as the token you use has access, any org is valid here repo: ${{ matrix.value }} @@ -112,8 +129,9 @@ Checkout the repository you wish to run automerge on to a local directory. git clone git@github.com:org/automerge.git cd automerge ``` - -Now you need to run the command from this new directory +Setup `GITHUB_TOKEN` with the appropriate permissions: Content Read/Write, Pull Request Read/Write, Adminstration Read, Checks Read. +Now you need to run the command from this new directory. +RV setup a test repository with Pull Requests in Known States to validate the action is working as expected. ```bash $(pwd)/../src/automerge.py --org runtimeverification --repo automerger-test --dry-run ``` diff --git a/src/automerge.py b/src/automerge.py index 0d9e245..4171007 100755 --- a/src/automerge.py +++ b/src/automerge.py @@ -19,8 +19,10 @@ _LOGGER: Final = logging.getLogger(__name__) _LOG_FORMAT: Final = '%(levelname)s %(asctime)s %(name)s - %(message)s' -logging.basicConfig(level=logging.INFO) - +if args.dry_run: + logging.basicConfig(level=logging.DEBUG) +else: + logging.basicConfig(level=logging.INFO) def pr_to_display_string(pr): return f'- {pr.number}: {pr.title}\n\t\t{pr.html_url}' @@ -89,24 +91,14 @@ def run_git_command(command_args: str) -> subprocess.CompletedProcess: out_of_date_passing_prs = [] for pr in automerge_prs: base_branch = repo.get_branch(pr.base.ref) - if base_branch.protected: - required_status_checks = base_branch.get_required_status_checks() - latest_commit = pr.get_commits().reversed[0] - latest_commit_checks = {check_run.name: check_run for check_run in latest_commit.get_check_runs()} - all_checks_passed = True - for required_check in required_status_checks.contexts: - if required_check not in latest_commit_checks: - print(f"Required check {required_check} is missing in the latest commit.") - all_checks_passed = False - else: - check_run = latest_commit_checks[required_check] - if check_run.conclusion == 'success': - print(f"Required check {required_check} passed on PR#{pr.number}") - else: - print(f"Required check {required_check} failed or is pending on PR#{pr.number}") - all_checks_passed = False commit = [c for c in pr.get_commits() if c.sha == pr.head.sha][0] - combined_status = commit.get_combined_status().state + commit_check_runs = commit.get_check_runs() + all_checks_passed = all(run.conclusion == "success" for run in commit_check_runs) + _LOGGER.debug(f'HEAD Commit: {commit}') + _LOGGER.debug(f'All Checks Passed? {all_checks_passed}') + if args.dry_run: + for check_run in commit_check_runs: + _LOGGER.debug(f'Check Run: {check_run}') if pr.mergeable_state == 'clean' and all_checks_passed: up_to_date_passing_prs.append(pr) elif pr.mergeable_state == 'behind' or pr.mergeable_state == 'blocked':