From d622fd6e5927c3c6579a0c788ba82654f56409ca Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Thu, 23 Jul 2026 13:17:45 -0700 Subject: [PATCH 1/5] ci: dispatch coverage-fanout to databricks-driver-test on merged source PRs Wires databricks-sql-go into the multi-language coverage fan-out. When a PR merges to main and touched Go driver source (a non-test .go file outside examples/ and testdata/), dispatch a `coverage-fanout` repository_dispatch to databricks/databricks-driver-test. Its coverage-fanout-tracker.yml then opens a tracking issue and runs the language-agnostic fan-out (a spec authored from this PR's diff, conformed across every driver incl. go). - Adds `closed` to the pull_request trigger types; the new trigger-coverage-fanout job gates on `pull_request.merged == true`. - Source-path filter: docs/CI/test-only merges don't warrant a full 7-leg fan-out. - Reuses the existing INTEGRATION_TEST App token (scoped to driver-test) and the same peter-evans/repository-dispatch pin adbc-drivers/databricks already uses. - Tightens skip-integration-tests-pr's guard to exclude `closed` so it doesn't re-stamp a check on merged PRs now that `closed` is a trigger. Co-authored-by: Isaac Signed-off-by: Eric Wang --- .../workflows/trigger-integration-tests.yml | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/.github/workflows/trigger-integration-tests.yml b/.github/workflows/trigger-integration-tests.yml index db1bcf9f..be0f0fd9 100644 --- a/.github/workflows/trigger-integration-tests.yml +++ b/.github/workflows/trigger-integration-tests.yml @@ -33,7 +33,7 @@ name: Trigger Integration Tests on: pull_request: - types: [opened, synchronize, reopened, labeled] + types: [opened, synchronize, reopened, labeled, closed] merge_group: # the required gate runs here jobs: @@ -98,7 +98,7 @@ jobs: # "Go Integration Tests" check is satisfied on the PR. The real run happens in # the merge queue; the `integration-test` label previews it on the PR. skip-integration-tests-pr: - if: github.event_name == 'pull_request' && github.event.action != 'labeled' + if: github.event_name == 'pull_request' && github.event.action != 'labeled' && github.event.action != 'closed' runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest @@ -314,3 +314,62 @@ jobs: summary: 'An error occurred while dispatching Go 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 Go + # driver source. Dispatches `coverage-fanout` to databricks-driver-test, whose + # coverage-fanout-tracker.yml creates a tracking issue and kicks off the + # language-agnostic fan-out (spec authored from THIS PR's diff, then conformed + # 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 + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + steps: + - name: Check if Go driver source changed + id: changed + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.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 Go driver. Count a merge as source-affecting + // when it changes a NON-test .go file outside examples/ and testdata/. + // Docs/CI/test-only merges do not warrant a full multi-language fan-out. + const isSource = (f) => + f.endsWith('.go') && + !f.endsWith('_test.go') && + !f.startsWith('examples/') && + !f.startsWith('testdata/'); + const srcChanged = files.some((f) => isSource(f.filename)); + console.log(`Go 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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.INTEGRATION_TEST_APP_ID }} + private-key: ${{ secrets.INTEGRATION_TEST_PRIVATE_KEY }} + owner: databricks + repositories: databricks-driver-test + + - 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 }}"}' From fc026908dd4834f7262a1aa02f831a261b3ed774 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Thu, 23 Jul 2026 15:20:32 -0700 Subject: [PATCH 2/5] =?UTF-8?q?ci:=20address=20review=20=E2=80=94=20guard?= =?UTF-8?q?=20coverage-fanout=20to=20base=20main=20+=20scope=20job=20permi?= =?UTF-8?q?ssions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit peco-review-bot findings on the coverage-fanout sender (apply to all driver repos — the job is identical everywhere): - F1 (Medium): the merged-PR guard didn't constrain the base branch, so a PR merged into a release/feature branch that touched source would also dispatch a full fan-out authoring a spec from a diff that never reached main. Add `github.event.pull_request.base.ref == 'main'` to match the stated intent. - F2 (Low): the job declared no permissions block, relying on the default GITHUB_TOKEN read scope for github.rest.pulls.listFiles; if org defaults tighten to none it 403s silently. Scope it explicitly: contents: read + pull-requests: read. Co-authored-by: Isaac Signed-off-by: Eric Wang --- .github/workflows/trigger-integration-tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/trigger-integration-tests.yml b/.github/workflows/trigger-integration-tests.yml index be0f0fd9..22670e91 100644 --- a/.github/workflows/trigger-integration-tests.yml +++ b/.github/workflows/trigger-integration-tests.yml @@ -327,10 +327,14 @@ jobs: if: | github.event_name == 'pull_request' && github.event.action == 'closed' && - github.event.pull_request.merged == true + 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 Go driver source changed id: changed From 3cb405967a7b706196317e5542298e330bfce7ef Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Thu, 23 Jul 2026 15:37:39 -0700 Subject: [PATCH 3/5] =?UTF-8?q?ci:=20address=20review=20=E2=80=94=20narrow?= =?UTF-8?q?=20minted=20token=20+=20fix=20pin=20version=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Further peco-review-bot findings on the coverage-fanout sender: - Narrow the minted App installation token with `permission-contents: write` (all coverage_fanout needs is repository_dispatch → contents:write), matching the defense-in-depth the other dispatch jobs in these repos already use — so a leaked token can only fire dispatches, not exercise the App's full scope. - Restore the version tag in two action-pin comments (`# pinned` → the exact `# vX.Y.Z` the SHA corresponds to, per repo convention) for auditability. Co-authored-by: Isaac Signed-off-by: Eric Wang --- .github/workflows/trigger-integration-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/trigger-integration-tests.yml b/.github/workflows/trigger-integration-tests.yml index 22670e91..03d75fda 100644 --- a/.github/workflows/trigger-integration-tests.yml +++ b/.github/workflows/trigger-integration-tests.yml @@ -368,6 +368,7 @@ jobs: 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' From 43afc2935f35ce462fd906e0cfd5b360934a109a Mon Sep 17 00:00:00 2001 From: eric-wang-1990 <115501094+eric-wang-1990@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:35:31 -0700 Subject: [PATCH 4/5] ci: post required Go Integration Tests skip via workflow_run reporter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `Go Integration Tests` required check is pinned (in the branch ruleset) to the driver-test GitHub App. The old PR-open stub posted a same-named check via `github.token` (github-actions app), which (a) can't satisfy an app-pinned gate and (b) can't post at all on fork PRs, whose `pull_request` runs get a read-only token and no secrets — so fork PRs couldn't enter the merge queue without a maintainer label (and a labeled fork PR still can't run the real suite, since fork `pull_request` events have no secret access). Replace the inline stub with a companion `skip-checks-reporter.yml` triggered by `workflow_run`. That runs in the base-repo context with full secret access even for fork-triggered runs, mints the driver-test App token, and posts `Go Integration Tests`=success on the PR head as the app — so every PR (internal or fork) auto-enqueues, and the placeholder satisfies the app-pinned gate. The real suite is unchanged: it runs as the required gate on the merge_group commit and as a label preview on internal PRs. Mirrors adbc-drivers/databricks skip-checks-reporter.yml. The reporter never checks out or executes PR/fork content (its only fork-controlled input is the opaque head SHA), so it does not expose secrets to untrusted code. Co-authored-by: Isaac Signed-off-by: eric-wang-1990 <115501094+eric-wang-1990@users.noreply.github.com> --- .github/workflows/skip-checks-reporter.yml | 72 +++++++++++++++++++ .../workflows/trigger-integration-tests.yml | 38 +++------- 2 files changed, 81 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/skip-checks-reporter.yml diff --git a/.github/workflows/skip-checks-reporter.yml b/.github/workflows/skip-checks-reporter.yml new file mode 100644 index 00000000..cf0dfa41 --- /dev/null +++ b/.github/workflows/skip-checks-reporter.yml @@ -0,0 +1,72 @@ +name: Report Integration Test Skip + +# Posts the PR-open "skipped" placeholder for the required `Go Integration Tests` +# check, as the driver-test GitHub App. +# +# Why a separate workflow (and not an inline job in trigger-integration-tests.yml): +# - The branch ruleset pins the required `Go Integration Tests` check to the +# driver-test app's integration id. Only a check posted BY that app satisfies the +# gate — a `github.token` (github-actions) check of the same name does not. +# - A PR from a fork runs its `pull_request` workflows with a READ-ONLY GITHUB_TOKEN +# and no access to secrets, so it cannot mint the app token and cannot post any +# check on its own head. That would leave fork PRs unable to enter the merge queue +# without a maintainer label. +# - `workflow_run` workflows always execute in THIS (base) repo's context using the +# workflow definition from the default branch, with full secret access — even when +# the run that triggered them came from a fork. That lets us post the app-attributed +# placeholder on any PR head, fork or not, so every PR can auto-enqueue. +# +# SECURITY: this workflow runs with secrets 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 `workflow_run.head_sha`, an opaque +# commit SHA passed to the API. Do not add `actions/checkout` or a `run:` step that +# executes repo content here. +# +# The real integration suite is unaffected: it runs as the required gate on the +# `merge_group` commit (and as a label preview on internal PRs), dispatched by +# trigger-integration-tests.yml. This workflow only posts the pre-merge placeholder. + +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@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.INTEGRATION_TEST_APP_ID }} + private-key: ${{ secrets.INTEGRATION_TEST_PRIVATE_KEY }} + owner: databricks + repositories: databricks-sql-go + + - name: Post skipped Go Integration Tests check + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.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: 'Go 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: 'Go integration tests are skipped on ordinary PR events and run as the required gate in the merge queue. Preview on an internal PR by adding a label: `integration-test` (Thrift, replay) or `integration-test-full` (Thrift, passthrough); `integration-test-kernel` runs the SEA-via-kernel backend against the real warehouse (passthrough). (Label previews cannot run on fork PRs, which lack secret access; fork PRs are exercised by the required merge-queue run.)', + }, + }); diff --git a/.github/workflows/trigger-integration-tests.yml b/.github/workflows/trigger-integration-tests.yml index 03d75fda..90873dcb 100644 --- a/.github/workflows/trigger-integration-tests.yml +++ b/.github/workflows/trigger-integration-tests.yml @@ -94,35 +94,15 @@ jobs: }); } - # Ordinary PR (not a label event): post a green stub so the required - # "Go Integration Tests" check is satisfied on the PR. The real run happens in - # the merge queue; the `integration-test` label previews it on the PR. - skip-integration-tests-pr: - if: github.event_name == 'pull_request' && github.event.action != 'labeled' && github.event.action != 'closed' - runs-on: - group: databricks-protected-runner-group - labels: linux-ubuntu-latest - permissions: - checks: write - steps: - - name: Skip Go Integration Tests (stub) - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - github-token: ${{ github.token }} - script: | - await github.rest.checks.create({ - owner: context.repo.owner, - repo: context.repo.repo, - name: 'Go 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: 'Go integration tests are skipped on ordinary PR events and run as a required gate in the merge queue. Preview on this PR by adding a label: `integration-test` (Thrift, replay) or `integration-test-full` (Thrift, passthrough); `integration-test-kernel` runs the SEA-via-kernel backend against the real warehouse (passthrough).', - }, - }); + # NOTE: the PR-open "skipped" placeholder for the required `Go Integration Tests` + # check is NOT posted here. It is posted by the companion workflow + # `skip-checks-reporter.yml`, which runs on `workflow_run` in the base-repo context + # so it can post the check as the driver-test app (the app the branch ruleset pins + # the required check to) on EVERY PR head — including fork PRs, whose own + # `pull_request` run gets a read-only token and cannot post checks at all. A + # `pull_request`-triggered stub here could only post via `github.token` + # (github-actions app), which would neither satisfy the app-pinned gate nor work on + # forks. See skip-checks-reporter.yml. # Labeled PR: preview on demand. `integration-test` → replay, # `integration-test-full` → full passthrough. From e321c9ec93d8e08db8468a9aedb52f14d8900848 Mon Sep 17 00:00:00 2001 From: eric-wang-1990 <115501094+eric-wang-1990@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:59:18 -0700 Subject: [PATCH 5/5] ci: run Go lint/test on merge_group so the merge queue can pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge queue evaluates every required status check on the queue's transient merge commit. `Lint` and `Test and Build (1.25.x/1.26.x)` are required (branch protection) but go.yml only triggered on push/pull_request, so they never ran on the merge commit — the required contexts stayed pending and the queue entry was evicted at the check-response timeout. Add a `merge_group` trigger so these jobs also run on the queue's merge commit and report there, matching the sql-python workflows. Co-authored-by: Isaac Signed-off-by: eric-wang-1990 <115501094+eric-wang-1990@users.noreply.github.com> --- .github/workflows/go.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 64972b44..e60f5e76 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -5,6 +5,12 @@ on: branches: [main] pull_request: branches: [main] + # The merge queue requires every required status check (Lint, Test and Build) + # to report on the queue's transient merge commit before it merges. Without a + # merge_group trigger these jobs never run there, so the required checks stay + # pending and the entry is evicted at the check-response timeout. See the + # sql-python go/py workflows for the same pattern. + merge_group: permissions: contents: read