diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4aecc583..c9aac5bc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -101,6 +101,9 @@ jobs: retention-days: 1 e2e-test: + # Skip on fork PRs (no access to azure-prod secrets); still runs on internal + # PRs, push, and in the merge queue, where fork code is e2e-tested before merge. + if: ${{ github.event.pull_request.head.repo.fork != true }} runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest @@ -162,6 +165,8 @@ jobs: coverage: needs: [unit-test, e2e-test] + # Runs when e2e passed OR was skipped (fork PRs); a real e2e failure still blocks it. + if: ${{ needs.unit-test.result == 'success' && (needs.e2e-test.result == 'success' || needs.e2e-test.result == 'skipped') }} runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest @@ -188,7 +193,10 @@ jobs: ls -1 coverage_*.tar | xargs -I '{}' -- tar -xvf '{}' rm coverage_*.tar - run: ls -la + # Skip the upload on forks — CODECOV_TOKEN is unavailable and + # fail_ci_if_error would fail the required coverage check. - name: Coverage + if: ${{ github.event.pull_request.head.repo.fork != true }} uses: codecov/codecov-action@ab904c41d6ece82784817410c45d8b8c02684457 # v3 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/skip-checks-reporter.yml b/.github/workflows/skip-checks-reporter.yml new file mode 100644 index 00000000..cc542b29 --- /dev/null +++ b/.github/workflows/skip-checks-reporter.yml @@ -0,0 +1,74 @@ +name: Report Integration Test Skip + +# Posts the PR-open "skipped" placeholder for the required +# `Node.js Integration Tests` check. +# +# Why a separate workflow (not an inline job in trigger-integration-tests.yml): +# - `Node.js Integration Tests` is a required status check. A fork PR runs its +# own `pull_request` workflows with a READ-ONLY GITHUB_TOKEN, so an inline +# `github.token` stub can't post the check on a fork head (checks.create +# 403s). That left fork PRs unable to satisfy the required check and stuck +# out of the merge queue. +# - `workflow_run` workflows always run in THIS (base) repo's context using the +# definition from the default branch, with a read-write token — even when the +# triggering run came from a fork. So this can post the placeholder on any PR +# head, fork or not, letting every PR auto-enqueue with no label. +# +# The required check is PINNED to the driver-test app's integration id in the +# ruleset, so the placeholder must be posted BY that app — hence this mints the +# INTEGRATION_TEST_APP token (scoped to this repo) and posts with it, rather than +# using `github.token` (github-actions app), which would not satisfy the pinned +# gate. Secrets are available here because this runs in the base-repo context. The +# real suite is unaffected: it runs as the required gate on the `merge_group` +# commit (also posted by the driver-test app) and as a label preview. +# +# SECURITY: runs with a write token in a privileged context. It MUST NOT check out +# or execute any PR/fork-controlled content — it only calls checks.create with a +# static body; the sole fork-controlled input is the opaque `workflow_run.head_sha` +# passed to the API. Do not add `actions/checkout` or a `run:` step that executes +# repo content here. + +on: + workflow_run: + workflows: ['Trigger Integration Tests'] + types: [requested] + +jobs: + report-skip: + # Only for PR-triggered runs; the merge_group run posts the real required check. + if: github.event.workflow_run.event == 'pull_request' + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + permissions: + checks: write + steps: + - name: Generate GitHub App token (this repo) + id: app-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + with: + app-id: ${{ secrets.INTEGRATION_TEST_APP_ID }} + private-key: ${{ secrets.INTEGRATION_TEST_PRIVATE_KEY }} + owner: databricks + repositories: databricks-sql-nodejs + + - name: Post skipped Node.js Integration Tests check + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + env: + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Node.js Integration Tests', + head_sha: process.env.HEAD_SHA, + status: 'completed', + conclusion: 'success', + completed_at: new Date().toISOString(), + output: { + title: 'Skipped on PR - runs in merge queue', + summary: 'Node.js integration tests are skipped on ordinary PR events and run as the required gate in the merge queue. Add the `integration-test` label to preview them on an internal PR.' + } + }); diff --git a/.github/workflows/trigger-integration-tests.yml b/.github/workflows/trigger-integration-tests.yml index 16fd1631..eae04c86 100644 --- a/.github/workflows/trigger-integration-tests.yml +++ b/.github/workflows/trigger-integration-tests.yml @@ -22,7 +22,7 @@ name: Trigger Integration Tests on: pull_request: - types: [opened, synchronize, reopened, labeled] + types: [opened, synchronize, reopened, labeled, closed] merge_group: jobs: @@ -92,32 +92,16 @@ jobs: body }); - skip-integration-tests-pr: - if: github.event_name == 'pull_request' && github.event.action != 'labeled' - runs-on: - group: databricks-protected-runner-group - labels: linux-ubuntu-latest - permissions: - checks: write - steps: - - name: Skip Node.js Integration Tests - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 - with: - github-token: ${{ github.token }} - script: | - await github.rest.checks.create({ - owner: context.repo.owner, - repo: context.repo.repo, - name: 'Node.js Integration Tests', - head_sha: context.payload.pull_request.head.sha, - status: 'completed', - conclusion: 'success', - completed_at: new Date().toISOString(), - output: { - title: 'Skipped on PR - runs in merge queue', - summary: 'Node.js integration tests are skipped on ordinary PR events and run as a required gate in the merge queue. Add the `integration-test` label to preview them on this PR.' - } - }); + # NOTE: the PR-open "skipped" placeholder for the required + # `Node.js Integration Tests` check is NOT posted here anymore. It is posted by + # the companion workflow `skip-checks-reporter.yml`, which runs on `workflow_run` + # in the base-repo context. That context has a read-write token even for + # fork-triggered runs, so it can post the check on EVERY PR head — including fork + # PRs, whose own `pull_request` run gets a read-only token and cannot post checks + # (the previous inline `github.token` stub 403'd on forks, so fork PRs could never + # satisfy the now-required check and were stuck out of the merge queue). See + # skip-checks-reporter.yml. The real suite still runs as the required gate on the + # merge_group commit and as a label preview. trigger-tests-pr: if: | @@ -175,7 +159,8 @@ jobs: "pr_repo": "${{ github.repository }}", "pr_url": "${{ github.event.pull_request.html_url }}", "pr_title": "${{ steps.sanitize.outputs.result }}", - "pr_author": "${{ github.event.pull_request.user.login }}" + "pr_author": "${{ github.event.pull_request.user.login }}", + "proxy_mode": "replay" } - name: Fail check on dispatch error @@ -207,7 +192,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: 'Node.js integration tests triggered. [View workflow run](https://github.com/databricks/databricks-driver-test/actions/workflows/databricks-nodejs-integration-tests.yml).' + body: 'Node.js integration tests triggered. [View workflow runs](https://github.com/databricks/databricks-driver-test/actions/workflows/databricks-sql-nodejs-integration-tests.yml). The result posts back here as the "Node.js Integration Tests" check.' }); merge-queue-nodejs: @@ -270,7 +255,8 @@ jobs: "pr_repo": "${{ github.repository }}", "pr_url": "${{ github.server_url }}/${{ github.repository }}/pull/${{ steps.extract-pr.outputs.pr_number }}", "pr_title": "Merge queue validation", - "pr_author": "merge-queue" + "pr_author": "merge-queue", + "proxy_mode": "replay" } - name: Fail check on dispatch error @@ -292,3 +278,66 @@ jobs: summary: 'An error occurred while dispatching Node.js integration tests. Check this workflow run for details.' } }); + + # ============================================================================= + # After merge: trigger the multi-language coverage fan-out. + # Fires when a PR lands on main (merge queue or direct merge) and touched + # driver source. Dispatches `coverage-fanout` to databricks-driver-test, whose + # coverage-fanout-tracker.yml opens a tracking issue and runs the + # language-agnostic fan-out (a spec authored from THIS PR's diff, conformed as + # tests across every driver) as peco-engineer-bot. + # ============================================================================= + trigger-coverage-fanout: + if: | + github.event_name == 'pull_request' && + github.event.action == 'closed' && + github.event.pull_request.merged == true && + github.event.pull_request.base.ref == 'main' + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + permissions: + contents: read + pull-requests: read + steps: + - name: Check if driver source changed + id: changed + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 + with: + script: | + const files = await github.paginate(github.rest.pulls.listFiles, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + per_page: 100, + }); + // The whole repo IS the driver. Count a merge as source-affecting when it changes driver + // source: lib/ (JS/TS), native/ (kernel bindings), thrift/ (Thrift defs), or the KERNEL_REV + // pin. Docs/CI/test-only merges do not warrant a full multi-language fan-out. (Matches the + // source roots called out in the "run on every change" comments above — a merge that only + // bumps KERNEL_REV or edits native/ or thrift/ still affects the Thrift/kernel backends.) + const isSource = (f) => + f.startsWith('lib/') || f.startsWith('native/') || f.startsWith('thrift/') || f === 'KERNEL_REV'; + const srcChanged = files.some((f) => isSource(f.filename)); + console.log(`driver source changed: ${srcChanged}`); + core.setOutput('source', srcChanged.toString()); + + - name: Generate GitHub App token (databricks-driver-test) + if: steps.changed.outputs.source == 'true' + id: app-token + uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + with: + app-id: ${{ secrets.INTEGRATION_TEST_APP_ID }} + private-key: ${{ secrets.INTEGRATION_TEST_PRIVATE_KEY }} + owner: databricks + repositories: databricks-driver-test + permission-contents: write + + - name: Dispatch coverage-fanout + if: steps.changed.outputs.source == 'true' + uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 + with: + token: ${{ steps.app-token.outputs.token }} + repository: databricks/databricks-driver-test + event-type: coverage-fanout + client-payload: '{"reference_repo": "${{ github.repository }}", "pr_number": "${{ github.event.pull_request.number }}", "pr_url": "${{ github.event.pull_request.html_url }}"}'