From d34bf0c168c4d571de678a3b32b42d9dcbcf94f9 Mon Sep 17 00:00:00 2001 From: Stuart Leeks Date: Thu, 24 Feb 2022 08:24:04 +0000 Subject: [PATCH] Add pr_comment_bot workflow (#1356) * Add pr_comment_bot workflow Allow running tests by commenting "/test" on a PR as OWNER/COLLABORATOR * Revert TRE_ID to git ref Based on discussion with @tamirkamara, changing this ID would require changes in numerous places in the scripts etc * Switch to standard checkout action for PR code * Add filter to skip full build on MD-only changes * Update references to CI github ref * fix quotes * Update comment/name * Update job name to align with manual check status --- .../devcontainer_run_command/action.yml | 1 - .github/workflows/deploy_tre.yml | 4 + .github/workflows/deploy_tre_reusable.yml | 27 ++- .github/workflows/pr_comment_bot.yml | 156 ++++++++++++++++++ 4 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/pr_comment_bot.yml diff --git a/.github/actions/devcontainer_run_command/action.yml b/.github/actions/devcontainer_run_command/action.yml index a191089f8..2072aa1d2 100644 --- a/.github/actions/devcontainer_run_command/action.yml +++ b/.github/actions/devcontainer_run_command/action.yml @@ -200,7 +200,6 @@ runs: -e TRE_URL \ -e TEST_WORKSPACE_APP_ID \ -e IS_API_SECURED \ - -e TF_VAR_ci_git_ref="${{ github.ref }}" \ -e DOCKER_BUILDKIT \ -e TF_VAR_keyvault_purge_protection_enabled=${{ inputs.TF_VAR_keyvault_purge_protection_enabled }} \ -e TF_VAR_stateful_resources_locked=${{ inputs.TF_VAR_stateful_resources_locked }} \ diff --git a/.github/workflows/deploy_tre.yml b/.github/workflows/deploy_tre.yml index 2d8d18a50..edae7c91f 100644 --- a/.github/workflows/deploy_tre.yml +++ b/.github/workflows/deploy_tre.yml @@ -22,6 +22,8 @@ jobs: name: "Deploy main" if: github.ref == 'refs/heads/main' uses: ./.github/workflows/deploy_tre_reusable.yml + with: + ciGitRef: ${{ github.ref }} secrets: AAD_TENANT_ID: ${{ secrets.AAD_TENANT_ID }} ACR_NAME: ${{ secrets.ACR_NAME }} @@ -110,6 +112,8 @@ jobs: if: ${{ github.ref != 'refs/heads/main' && needs.prepare-not-main.outputs.not-md == 'true' }} needs: [prepare-not-main] uses: ./.github/workflows/deploy_tre_reusable.yml + with: + ciGitRef: ${{ github.ref }} secrets: AAD_TENANT_ID: ${{ secrets.AAD_TENANT_ID }} ACR_NAME: ${{ format('tre{0}', needs.prepare-not-main.outputs.refid) }} diff --git a/.github/workflows/deploy_tre_reusable.yml b/.github/workflows/deploy_tre_reusable.yml index 3714c0c9f..a3270a991 100644 --- a/.github/workflows/deploy_tre_reusable.yml +++ b/.github/workflows/deploy_tre_reusable.yml @@ -3,6 +3,19 @@ name: Deploy Azure TRE Resuable on: workflow_call: + inputs: + prRepo: + description: Name of the repo GitHub repo containing the ref (as org/repo ) + type: string + required: false + prRef: + description: The git ref to checkout + type: string + required: false + ciGitRef: + description: The git ref to use in annotations to associate a deployment with the code that triggered it + type: string + required: true secrets: AAD_TENANT_ID: required: true @@ -59,7 +72,7 @@ on: # This will prevent multiple runs of this entire workflow. # We should NOT cancel in progress runs as that can destabilize the environment. -concurrency: "deploy-${{ github.ref }}" +concurrency: "deploy-${{ inputs.ciGitRef }}" jobs: deploy_management: @@ -67,9 +80,17 @@ jobs: runs-on: ubuntu-latest environment: Dev steps: - - name: Checkout + - name: Checkout (default) + if: ${{ inputs.prRepo == '' }} # if not running for a PR, checkout the default ref for the workflow run uses: actions/checkout@v2 + - name: Checkout (PR) + if: ${{ inputs.prRepo != '' }} # if running for a PR, checkout the PR commit + uses: actions/checkout@v2 + with: + repository: ${{ inputs.prRepo }} + ref: ${{ inputs.prRef }} + - name: Set up Docker BuildKit uses: docker/setup-buildx-action@v1 @@ -200,7 +221,7 @@ jobs: - name: Deploy Trusted Research Environment uses: ./.github/actions/devcontainer_run_command with: - COMMAND: "make tre-deploy" + COMMAND: "TF_VAR_ci_git_ref=${{ inputs.ciGitRef }} make tre-deploy" ACTIONS_ACR_NAME: ${{ secrets.ACTIONS_ACR_NAME }} ACTIONS_ACR_URI: ${{ secrets.ACTIONS_ACR_URI }} ACTIONS_ACR_PASSWORD: ${{ secrets.ACTIONS_ACR_PASSWORD }} diff --git a/.github/workflows/pr_comment_bot.yml b/.github/workflows/pr_comment_bot.yml new file mode 100644 index 000000000..3010b92d8 --- /dev/null +++ b/.github/workflows/pr_comment_bot.yml @@ -0,0 +1,156 @@ +name: pr_comment_bot + +on: + issue_comment: + types: [created] # only run on new comments + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment + # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#issue_comment + +jobs: + + pr_comment: + name: PR comment + # https://docs.github.com/en/graphql/reference/enums#commentauthorassociation + # (and https://docs.github.com/en/rest/reference/issues#comments) + + # only allow commands where: + # - the comment is on a PR + # - the commenting user has write permissions (i.e. is OWNER or COLLABORATOR) + if: ${{ github.event.issue.pull_request && (github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'COLLABORATOR') }} + runs-on: ubuntu-latest + outputs: + command: ${{ steps.check_command.outputs.result }} + prRepo: ${{ steps.get_pr_details.outputs.prRepo }} + prRef: ${{ steps.get_pr_details.outputs.prRef }} + refid: ${{ steps.get_pr_details.outputs.refid }} + ciGitRef: ${{ steps.get_pr_details.outputs.ciGitRef }} + not-md: ${{ steps.filter.outputs.not-md }} + steps: + # Determine whether the comment is a command + - id: check_command + name: Check for a command using GitHub script + uses: actions/github-script@v6 + env: + SOMETHING_SECRET: ${{ secrets.SOMETHING_SECRET }} + with: + result-encoding: string + script: | + const commentBody = context.payload.comment.body; + + switch (commentBody.trim()){ + case "/test": + return "run-tests"; + case "/help": + return "show-help"; + default: + return "none"; + } + + # Add comment with help text in response to help command + - name: Show Help + if: ${{ steps.check_command.outputs.result == 'show-help' }} + env: + PR_NUMBER: ${{ github.event.issue.number }} + REPO: ${{ github.event.repository.full_name }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "Showing help on PR ${PR_NUMBER}" + gh pr comment ${PR_NUMBER} --repo $REPO --body "Hello

You can use the following commands:
/test - run tests on a PR
/help - show this help" + + # Check if the PR build/test needs to run + - name: Checkout + if: ${{ steps.check_command.outputs.result == 'run-tests' }} + uses: actions/checkout@v2 + with: + persist-credentials: false + - uses: dorny/paths-filter@v2 + id: filter + if: ${{ steps.check_command.outputs.result == 'run-tests' }} + with: + filters: | + not-md: + # we need to check for changes in files other than *.md + - '**/!(*.md)' + + # If we don't run the actual deploy (below) we won't receive a check-run status, + # and will have to send it "manually" + - name: Bypass E2E check-runs status + if: ${{ steps.check_command.outputs.result == 'run-tests' && steps.filter.outputs.not-md == 'false' }} + uses: LouisBrunner/checks-action@v1.1.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + # the name must be identical to the one received by the real job + name: "Deploy PR / Run E2E Tests (Smoke)" + status: "completed" + conclusion: "success" + + # Get PR commit details for running tests + - id: get_pr_details + name: Get PR details + if: ${{ steps.check_command.outputs.result == 'run-tests' && steps.filter.outputs.not-md == 'true' }} + env: + PR_NUMBER: ${{ github.event.issue.number }} + REPO: ${{ github.event.repository.full_name }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Could look at moving this to GitHub Script action as well + + echo "Getting PR repo..." + pr_owner=$(gh pr view $PR_NUMBER --repo $REPO --json headRepositoryOwner | jq -r .headRepositoryOwner.login) + pr_repo=$(gh pr view $PR_NUMBER --repo $REPO --json headRepository | jq -r .headRepository.name) + echo -e "\tPR from $pr_owner/$pr_repo" + + echo "Getting PR ref..." + ref=$(gh pr view $PR_NUMBER --repo $REPO --json commits | jq -r ".[] | last | .oid") + echo -e "\tLatest commit ref: $ref" + echo + + echo "Setting outputs" + echo "::set-output name=prRef::${ref}" + echo "::set-output name=prRepo::${pr_owner}/${pr_repo}" + echo "Done" + + github_pr_ref="refs/pull/${PR_NUMBER}/merge" + echo "::set-output name=ciGitRef::${github_pr_ref}" + + REFID=$(echo ${github_pr_ref} | shasum | cut -c1-8) + echo "using id of: ${REFID} for GitHub Ref: ${github_pr_ref}" + echo "::set-output name=refid::${REFID}" + + run_test: + # Run the tests with the re-usable workflow + needs: [pr_comment] + if: ${{ needs.pr_comment.outputs.command == 'run-tests' && steps.filter.outputs.not-md == 'true' }} + name: Deploy PR + uses: ./.github/workflows/deploy_tre_reusable.yml + with: + prRef: ${{ needs.pr_comment.outputs.prRef }} + prRepo: ${{ needs.pr_comment.outputs.prRepo }} + ciGitRef: ${{ needs.pr_comment.outputs.ciGitRef }} + secrets: + AAD_TENANT_ID: ${{ secrets.AAD_TENANT_ID }} + ACR_NAME: ${{ format('tre{0}', needs.pr_comment.outputs.refid) }} + ACTIONS_ACR_NAME: ${{ secrets.ACTIONS_ACR_NAME }} + ACTIONS_ACR_URI: ${{ secrets.ACTIONS_ACR_NAME }}.azurecr.io/ + ACTIONS_ACR_PASSWORD: ${{ secrets.ACTIONS_ACR_PASSWORD }} + ACTIONS_DEVCONTAINER_TAG: ${{ needs.pr_comment.outputs.refid }} + API_CLIENT_ID: ${{ secrets.API_CLIENT_ID }} + API_CLIENT_SECRET: ${{ secrets.API_CLIENT_SECRET }} + ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} + ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }} + ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} + ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} + CORE_ADDRESS_SPACE: ${{ secrets.CORE_ADDRESS_SPACE }} + LOCATION: ${{ secrets.LOCATION }} + MGMT_RESOURCE_GROUP: ${{ format('rg-tre{0}-mgmt', needs.pr_comment.outputs.refid) }} + MS_TEAMS_WEBHOOK_URI: ${{ secrets.MS_TEAMS_WEBHOOK_URI }} + STATE_STORAGE_ACCOUNT_NAME: ${{ format('tre{0}mgmt', needs.pr_comment.outputs.refid) }} + SWAGGER_UI_CLIENT_ID: ${{ secrets.SWAGGER_UI_CLIENT_ID }} + TEST_APP_ID: ${{ secrets.TEST_APP_ID }} + TEST_USER_NAME: ${{ secrets.TEST_USER_NAME }} + TEST_USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }} + TEST_WORKSPACE_APP_ID: ${{ secrets.TEST_WORKSPACE_APP_ID }} + TF_STATE_CONTAINER: ${{ secrets.TF_STATE_CONTAINER }} + TRE_ADDRESS_SPACE: ${{ secrets.TRE_ADDRESS_SPACE }} + TRE_ID: ${{ format('tre{0}', needs.pr_comment.outputs.refid) }} + CI_CACHE_ACR_NAME: ${{ secrets.ACR_NAME }}