Skip to content

Trigger Integration Tests #146

Trigger Integration Tests

Trigger Integration Tests #146

name: Trigger Integration Tests
# Dispatches the JDBC proxy suite in databricks/databricks-driver-test to run
# against THIS PR's driver commit, and reports the result back as the "JDBC
# Integration Tests" check.
#
# REPLAY ONLY — deliberately. A dispatched run always uses proxy_mode=replay:
# driver-test builds the driver at the PR commit and serves committed recordings
# (no credentials, no live warehouse). We never trigger a passthrough / live E2E
# run from a PR — those are slow and flaky and would block PRs from landing;
# passthrough remains a manual/nightly-only path in driver-test.
#
# Pattern mirrors the databricks-sql-go / adbc-csharp senders: ordinary PR events
# get an immediate green stub for the required "JDBC Integration Tests" check;
# maintainers preview on demand with a label; the merge queue runs the real
# required replay gate.
#
# ONE request per PR: the sender does NOT pick a backend. driver-test's receiver
# (jdbc-integration-tests.yml) decides which backends to run (thrift + sea) from
# its recordings and fans them out into one "JDBC Integration Tests" check. A
# single `integration-test` label previews the full replay run.
#
# Required external setup:
# 1. `INTEGRATION_TEST_APP_ID` / `INTEGRATION_TEST_PRIVATE_KEY` repo secrets for
# the dispatcher GitHub App are installed in this repo.
# 2. The App is installed on databricks-driver-test (actions: write, to send
# repository_dispatch) and on databricks-jdbc (checks: write, so driver-test
# can report the "JDBC Integration Tests" check back here).
# 3. The `integration-test` trigger label exists.
# 4. Merge-queue branch protection lists `JDBC Integration Tests` as required.
on:
pull_request:
types: [opened, synchronize, reopened, labeled, closed]
merge_group: # the required gate runs here
permissions: {}
jobs:
# Drop the trigger labels when new commits are pushed, forcing a maintainer to
# re-review before re-running (security: a labeled PR that gets new commits
# must be re-approved).
remove-label-on-new-commit:
if: github.event_name == 'pull_request' && github.event.action == 'synchronize'
runs-on:
group: databricks-protected-runner-group
labels: linux-ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Remove integration-test labels
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const present = context.payload.pull_request.labels.map((l) => l.name);
const triggerLabels = ['integration-test'];
const removed = [];
for (const name of triggerLabels) {
if (!present.includes(name)) continue;
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
name,
});
removed.push(name);
} catch (error) {
if (error.status !== 404) throw error;
}
}
if (removed.length) {
const pr = context.payload.pull_request;
const isFromFork = pr.head.repo?.full_name !== pr.base.repo.full_name;
const repoType = isFromFork ? '**fork PR**' : 'PR';
const body = [
'Integration test approval reset.',
'',
`New commits were pushed to this ${repoType}. Label(s) \`${removed.join('`, `')}\` were removed for security.`,
'',
'**A maintainer must re-review and re-add a label to preview tests again.** (The real gate runs in the merge queue.)',
'',
`Latest commit: ${pr.head.sha.substring(0, 7)}`,
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
# Ordinary PR (not a label event): the green stub for the required "JDBC
# Integration Tests" check is posted by skip-checks-reporter.yml, which runs on
# `workflow_run` in the base-repo context (with secret access) so it can post the
# check AS THE App — required because branch protection pins the check to the App,
# and a fork PR's `pull_request` run has no secrets to mint the App token itself.
# See skip-checks-reporter.yml. The real run happens in the merge queue; a label
# previews it on the PR.
# Labeled PR: preview on demand. One `integration-test` label → full replay
# (driver-test runs both thrift + sea). No per-backend label — the receiver
# owns backend selection.
trigger-tests-pr:
if: |
github.event_name == 'pull_request' &&
github.event.action == 'labeled' &&
github.event.label.name == 'integration-test'
runs-on:
group: databricks-protected-runner-group
labels: linux-ubuntu-latest
permissions:
# Commenting on a PR needs pull-requests:write; checks:write for the failure
# check. The dispatch itself uses the App token, not GITHUB_TOKEN.
issues: write
pull-requests: write
checks: write
steps:
- name: Generate GitHub App token (driver-test repo)
id: app-token
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.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 jdbc-pr-test (replay) to driver-test
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const pr = context.payload.pull_request;
await github.rest.repos.createDispatchEvent({
owner: 'databricks',
repo: 'databricks-driver-test',
event_type: 'jdbc-pr-test',
client_payload: {
pr_number: `${pr.number}`,
commit_sha: pr.head.sha,
pr_repo: context.repo.owner + '/' + context.repo.repo,
pr_url: pr.html_url,
proxy_mode: 'replay',
},
});
core.info(`Dispatched jdbc-pr-test (replay, all backends) for PR #${pr.number} @ ${pr.head.sha}`);
- name: Fail check on dispatch error
if: failure()
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ github.token }}
script: |
await github.rest.checks.create({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'JDBC Integration Tests',
head_sha: context.payload.pull_request.head.sha,
status: 'completed',
conclusion: 'failure',
completed_at: new Date().toISOString(),
output: {
title: 'Failed - error dispatching tests',
summary: 'An error occurred while dispatching JDBC integration tests. Check this workflow run for details.',
},
});
- name: Comment on PR
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `JDBC integration tests triggered (\`replay\`, thrift + sea). [View workflow runs](https://github.com/databricks/databricks-driver-test/actions).`,
});
# Merge queue: the required gate. Runs the full replay matrix (thrift + sea).
merge-queue-jdbc:
if: github.event_name == 'merge_group'
runs-on:
group: databricks-protected-runner-group
labels: linux-ubuntu-latest
permissions:
contents: read
checks: write
steps:
- name: Extract PR number from merge queue ref
id: extract-pr
env:
MERGE_QUEUE_REF: ${{ github.event.merge_group.head_ref }}
run: |
# Merge-queue refs are refs/heads/gh-readonly-queue/<base>/pr-<n>-<sha>. Anchor
# to the trailing pr-<n>-<sha> so a base branch name that itself contains a
# "pr-<digits>" segment can't be matched instead of the real, last PR number.
if [[ "$MERGE_QUEUE_REF" =~ /pr-([0-9]+)-[0-9a-f]+$ ]]; then
echo "pr_number=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT"
else
echo "Error: failed to extract PR number from merge group ref: '$MERGE_QUEUE_REF'" >&2
exit 1
fi
- name: Generate GitHub App token (driver-test repo)
id: app-token
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.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 jdbc-pr-test (thrift replay) for merge queue
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
PR_NUMBER: ${{ steps.extract-pr.outputs.pr_number }}
HEAD_SHA: ${{ github.event.merge_group.head_sha }}
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const prNumber = process.env.PR_NUMBER;
await github.rest.repos.createDispatchEvent({
owner: 'databricks',
repo: 'databricks-driver-test',
event_type: 'jdbc-pr-test',
client_payload: {
pr_number: prNumber,
commit_sha: process.env.HEAD_SHA,
pr_repo: context.repo.owner + '/' + context.repo.repo,
pr_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/pull/${prNumber}`,
proxy_mode: 'replay',
// No backend field — the receiver runs the full replay matrix
// (thrift + sea), both required at merge.
},
});
core.info(`Merge-queue dispatch jdbc-pr-test (replay, all backends) for PR #${prNumber} @ ${process.env.HEAD_SHA}`);
- name: Fail check on dispatch error
if: failure()
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
HEAD_SHA: ${{ github.event.merge_group.head_sha }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.checks.create({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'JDBC Integration Tests',
head_sha: process.env.HEAD_SHA,
status: 'completed',
conclusion: 'failure',
completed_at: new Date().toISOString(),
output: {
title: 'Failed - error dispatching tests',
summary: 'An error occurred while dispatching JDBC integration tests. Check this workflow run for details.',
},
});
# =============================================================================
# After merge: trigger the multi-language coverage fan-out.
# Fires when a PR lands on main and touched JDBC driver source (src/main/).
# 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
# 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 JDBC driver source changed
id: changed
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
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 JDBC driver. Count a merge as source-affecting
// when it changes a main (non-test) Java/resources source file under
// src/main/. Docs/CI/test-only merges (src/test/, docs/, .github/, poms)
// do not warrant a full multi-language fan-out.
const isSource = (f) => f.startsWith('src/main/');
const srcChanged = files.some((f) => isSource(f.filename));
console.log(`JDBC 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@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.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 }}"}'