diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index cffeced2e501..577d5496c799 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -1,5 +1,5 @@ name: setup -description: "Setup Java" +description: "Verify checked out commits and setup Java" inputs: java-version: description: "Java version to setup" @@ -11,6 +11,22 @@ inputs: runs: using: composite steps: + - name: Verify args + shell: bash + if: github.event_name == 'repository_dispatch' && github.event.client_payload.slash_command.args.named.sha == '' + run: | + echo >&2 "Dispatched from a slash command but missing the 'sha' argument" + exit 1 + - name: Verify commit + shell: bash + run: | + expected=${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha || '' }} + actual=$(git log -1 --format='%H') + if [ -n "$expected" ] && [ "$actual" != "$expected" ]; then + echo >&2 "Expected to checkout $expected but got $actual" + exit 1 + fi - name: Fetch base ref to find merge-base for GIB shell: bash run: .github/bin/git-fetch-base-ref.sh diff --git a/.github/actions/update-check/action.yml b/.github/actions/update-check/action.yml new file mode 100644 index 000000000000..b19b63c1740f --- /dev/null +++ b/.github/actions/update-check/action.yml @@ -0,0 +1,103 @@ +name: "update-check-action" +description: "Creates or updates a check for a specific PR" +inputs: + pull_request_number: + description: "Number of the pull request to update checks in" + required: true + check_name: + description: "Name of the check to update" + required: true + conclusion: + description: "Conclusion to set for the check" + required: true + github_token: + description: "GitHub token to authenticate with" + default: ${{ github.token }} + run_id: + description: "Current workflow run id" + default: ${{ github.run_id }} + run_attempt: + description: "Current workflow run attempt number" + default: ${{ github.run_attempt }} + +runs: + using: composite + steps: + - uses: actions/github-script@v6 + id: update-check-run + if: ${{ always() }} + env: + number: ${{ inputs.pull_request_number }} + check_name: ${{ inputs.check_name }} + # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run + conclusion: ${{ inputs.conclusion }} + with: + github-token: ${{ inputs.github_token }} + script: | + const { data: pull } = await github.rest.pulls.get({ + ...context.repo, + pull_number: process.env.number + }); + const ref = pull.head.sha; + const { data: checks } = await github.rest.checks.listForRef({ + ...context.repo, + ref + }); + const check = checks.check_runs.filter(c => c.name === process.env.check_name); + if (check && check.length != 0) { + const { data: result } = await github.rest.checks.update({ + ...context.repo, + check_run_id: check[0].id, + status: 'completed', + conclusion: process.env.conclusion + }); + return result; + } + const { data: result } = await github.rest.checks.create({ + ...context.repo, + name: process.env.check_name, + head_sha: ref, + status: 'completed', + conclusion: process.env.conclusion + }); + return result; + - uses: actions/github-script@v6 + id: comment + if: ${{ always() }} + env: + number: ${{ inputs.pull_request_number }} + run_id: ${{ inputs.run_id }} + run_attempt: ${{ inputs.run_attempt }} || 1 + conclusion: ${{ inputs.conclusion }} + with: + github-token: ${{ inputs.github_token }} + script: | + const { data: pull } = await github.rest.pulls.get({ + ...context.repo, + pull_number: process.env.number + }); + const { data: run } = await github.rest.actions.getWorkflowRunAttempt({ + ...context.repo, + run_id: process.env.run_id, + attempt_number: process.env.run_attempt, + exclude_pull_requests: true + }); + + const message = "The CI workflow run with tests that require additional secrets finished as " + process.env.conclusion + ": " + run.html_url + const comments = await github.paginate(github.rest.issues.listComments.endpoint.merge({ + ...context.repo, + issue_number: process.env.number + })) + const exists = comments.filter(comment => comment.body === message).length != 0 + + if (exists) { + return; + } + + const { data: result } = await github.rest.issues.createComment({ + ...context.repo, + issue_number: process.env.number, + body: message + }); + + return result; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d343e8d0073..8880d46d8165 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,8 @@ on: pull_request: paths-ignore: - 'docs/**' + repository_dispatch: + types: [test-with-secrets-command] defaults: run: @@ -22,8 +24,8 @@ env: MAVEN_INSTALL_OPTS: "-Xmx3G -XX:+ExitOnOutOfMemoryError -Dmaven.wagon.rto=60000" MAVEN_FAST_INSTALL: "-B --strict-checksums -V --quiet -T C1 -DskipTests -Dmaven.source.skip=true -Dair.check.skip-all" MAVEN_COMPILE_COMMITS: "-B --strict-checksums --quiet -T C1 -DskipTests -Dmaven.source.skip=true -Dair.check.skip-all=true -Dmaven.javadoc.skip=true --no-snapshot-updates --no-transfer-progress -pl '!:trino-server-rpm'" - MAVEN_GIB: "-P gib -Dgib.referenceBranch=refs/remotes/origin/${{ github.event.pull_request.base.ref }}" - MAVEN_TEST: "-B --strict-checksums -Dmaven.source.skip=true -Dair.check.skip-all --fail-at-end -P gib -Dgib.referenceBranch=refs/remotes/origin/${{ github.event.pull_request.base.ref }}" + MAVEN_GIB: "-P gib -Dgib.referenceBranch=refs/remotes/origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || github.event.repository.default_branch }}" + MAVEN_TEST: "-B --strict-checksums -Dmaven.source.skip=true -Dair.check.skip-all --fail-at-end -P gib -Dgib.referenceBranch=refs/remotes/origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || github.event.repository.default_branch }}" RETRY: .github/bin/retry # Testcontainers kills image pulls if they don't make progress for > 30s and retries for 2m before failing. This means # that if an image doesn't download all it's layers within ~2m then any other concurrent pull will be killed because @@ -39,7 +41,8 @@ env: concurrency: # Cancel all workflow runs except latest within a concurrency group. This is achieved by defining a concurrency group for the PR. # Non-PR builds have singleton concurrency groups. - group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.number || github.sha }} + # When triggered by the repository_dispatch, add the expected SHA to avoid cancelling the run from the PR. + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.number || github.sha }}-${{ github.event_name == 'repository_dispatch' && github.event.client_payload.slash_command.args.named.sha || github.sha }} cancel-in-progress: true jobs: @@ -55,6 +58,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits, as the build result depends on `git describe` equivalent + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup with: java-version: ${{ matrix.java-version }} @@ -132,6 +139,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits to be able to determine merge base for GIB + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup - name: Maven Package run: | @@ -157,6 +168,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits: it's not needed here, but it's needed almost always, so let's do this for completeness + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - name: Web UI Checks run: core/trino-main/bin/check_webui.sh @@ -169,6 +184,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout tags so version in Manifest is set properly + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup - name: Maven Install run: | @@ -234,6 +253,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits to be able to determine merge base for GIB + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup - name: Install Hive Module run: | @@ -347,6 +370,18 @@ jobs: path: | **/*.hprof retention-days: ${{ env.HEAP_DUMP_RETENTION_DAYS }} + - name: Update PR check + uses: ./.github/actions/update-check + if: >- + failure() && + github.event_name == 'repository_dispatch' && + github.event.client_payload.slash_command.args.named.sha != '' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha + with: + pull_request_number: ${{ github.event.client_payload.pull_request.number }} + check_name: ${{ github.job }} (${{ matrix.config }}) with secrets + conclusion: ${{ job.status }} + github_token: ${{ secrets.GITHUB_TOKEN }} - name: Clean local Maven repo # Avoid creating a cache entry because this job doesn't download all dependencies if: steps.cache.outputs.cache-hit != 'true' @@ -361,6 +396,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits to be able to determine merge base for GIB + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup - name: Maven Install run: | @@ -440,6 +479,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits to be able to determine merge base for GIB + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup with: cache: false @@ -512,6 +555,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits to be able to determine merge base for GIB + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup - name: Cleanup node # This is required as a virtual environment update 20210219.1 left too little space for MemSQL to work @@ -629,6 +676,18 @@ jobs: path: | **/*.hprof retention-days: ${{ env.HEAP_DUMP_RETENTION_DAYS }} + - name: Update PR check + uses: ./.github/actions/update-check + if: >- + failure() && + github.event_name == 'repository_dispatch' && + github.event.client_payload.slash_command.args.named.sha != '' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha + with: + pull_request_number: ${{ github.event.client_payload.pull_request.number }} + check_name: ${{ github.job }} with secrets + conclusion: ${{ job.status }} + github_token: ${{ secrets.GITHUB_TOKEN }} - name: Clean local Maven repo # Avoid creating a cache entry because this job doesn't download all dependencies if: steps.cache.outputs.cache-hit != 'true' @@ -643,6 +702,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits to be able to determine merge base for GIB + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup - uses: dorny/paths-filter@v2 id: filter @@ -870,6 +933,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # checkout all commits, as the build result depends on `git describe` equivalent + ref: | + ${{ github.event_name == 'repository_dispatch' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha && + format('refs/pull/{0}/head', github.event.client_payload.pull_request.number) || '' }} - uses: ./.github/actions/setup with: cache: false @@ -932,3 +999,15 @@ jobs: name: test report pt (${{ matrix.config }}, ${{ matrix.suite }}, ${{ matrix.jdk }}) path: testing/trino-product-tests/target/reports/**/testng-results.xml retention-days: ${{ env.TEST_REPORT_RETENTION_DAYS }} + - name: Update PR check + uses: ./.github/actions/update-check + if: >- + failure() && + github.event_name == 'repository_dispatch' && + github.event.client_payload.slash_command.args.named.sha != '' && + github.event.client_payload.pull_request.head.sha == github.event.client_payload.slash_command.args.named.sha + with: + pull_request_number: ${{ github.event.client_payload.pull_request.number }} + check_name: ${{ github.job }} with secrets + conclusion: ${{ job.status }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml new file mode 100644 index 000000000000..4baa61f28de1 --- /dev/null +++ b/.github/workflows/ok-to-test.yml @@ -0,0 +1,36 @@ +# If someone with write access comments "/test-with-secrets" on a pull request, emit a repository_dispatch event +name: Ok To Test + +on: + issue_comment: + types: [created] + +jobs: + test-with-secrets: + runs-on: ubuntu-latest + # Only run for PRs, not issue comments + if: ${{ github.event.issue.pull_request }} + steps: + # Generate a GitHub App installation access token from an App ID and private key + # To create a new GitHub App: + # https://developer.github.com/apps/building-github-apps/creating-a-github-app/ + # See app.yml for an example app manifest + - name: Generate token + id: generate_token + # fork of tibdex/github-app-token + uses: trinodb/github-actions/github-app-token@2c3f458fcad343199d0e92badaaa6e9dd7993b2e + with: + app_id: ${{ secrets.TRINO_COMMENT_WATCHER_APP_ID }} + private_key: ${{ secrets.TRINO_COMMENT_WATCHER_APP_PRIVATE_KEY }} + + - name: Slash Command Dispatch + # fork of peter-evans/slash-command-dispatch + uses: trinodb/github-actions/slash-command-dispatch@2c3f458fcad343199d0e92badaaa6e9dd7993b2e + env: + TOKEN: ${{ steps.generate_token.outputs.token }} + with: + token: ${{ env.TOKEN }} # GitHub App installation access token + reaction-token: ${{ secrets.GITHUB_TOKEN }} + issue-type: pull-request + commands: test-with-secrets + permission: write