Skip to content

Commit 7ec8e57

Browse files
committed
migrate to Checks API for integration tests
Use GitHub Checks API instead of Statuses API to report integration test results. This enables the use of GitHub App authentication and eliminates the need for monthly PAT rotation. Changes: - Generate a second GitHub App token for check creation - Create check run before triggering tests in eng-dev-ecosystem - Pass check_run_id to the workflow for status updates - Update get_status() to query Checks API instead of Statuses API
1 parent 819274c commit 7ec8e57

3 files changed

Lines changed: 202 additions & 25 deletions

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
name: Integration Tests
2+
3+
# Triggers integration tests on PRs and auto-approves for merge queue
4+
on:
5+
6+
pull_request:
7+
types: [opened, synchronize]
8+
9+
merge_group:
10+
11+
12+
jobs:
13+
check-token:
14+
name: Check secrets access
15+
16+
runs-on:
17+
group: databricks-deco-testing-runner-group
18+
labels: ubuntu-latest-deco
19+
20+
environment: "test-trigger-is"
21+
outputs:
22+
has_token: ${{ steps.set-token-status.outputs.has_token }}
23+
steps:
24+
- name: Check if required secrets are set
25+
id: set-token-status
26+
run: |
27+
if [ -z "${{ secrets.DECO_WORKFLOW_TRIGGER_APP_ID }}" ] || [ -z "${{ secrets.DECO_TEST_APPROVAL_APP_ID }}" ]; then
28+
echo "Required secrets are missing. User has no access to secrets."
29+
echo "has_token=false" >> $GITHUB_OUTPUT
30+
else
31+
echo "All required secrets are set. User has access to secrets."
32+
echo "has_token=true" >> $GITHUB_OUTPUT
33+
fi
34+
35+
trigger-tests:
36+
name: Trigger Tests
37+
38+
runs-on:
39+
group: databricks-deco-testing-runner-group
40+
labels: ubuntu-latest-deco
41+
42+
needs: check-token
43+
if: github.event_name == 'pull_request' && needs.check-token.outputs.has_token == 'true'
44+
environment: "test-trigger-is"
45+
46+
steps:
47+
- uses: actions/checkout@v4
48+
49+
- name: Generate GitHub App Token for Check Updates
50+
id: generate-check-token
51+
uses: actions/create-github-app-token@v1
52+
with:
53+
app-id: ${{ secrets.DECO_TEST_APPROVAL_APP_ID }}
54+
private-key: ${{ secrets.DECO_TEST_APPROVAL_PRIVATE_KEY }}
55+
owner: databricks
56+
57+
- name: Create Check Run
58+
id: create-check
59+
env:
60+
GH_TOKEN: ${{ steps.generate-check-token.outputs.token }}
61+
run: |
62+
response=$(gh api -X POST \
63+
/repos/${{ github.repository }}/check-runs \
64+
-f name="Integration Tests" \
65+
-f head_sha="${{ github.event.pull_request.head.sha }}" \
66+
-f status="queued" \
67+
-f output[title]="Integration Tests" \
68+
-f output[summary]="Tests queued and will be triggered shortly...")
69+
70+
check_run_id=$(echo "$response" | jq -r .id)
71+
echo "check_run_id=$check_run_id" >> $GITHUB_OUTPUT
72+
73+
- name: Generate GitHub App Token for Workflow Trigger
74+
id: generate-token
75+
uses: actions/create-github-app-token@v1
76+
with:
77+
app-id: ${{ secrets.DECO_WORKFLOW_TRIGGER_APP_ID }}
78+
private-key: ${{ secrets.DECO_WORKFLOW_TRIGGER_PRIVATE_KEY }}
79+
owner: ${{ secrets.ORG_NAME }}
80+
repositories: ${{secrets.REPO_NAME}}
81+
82+
- name: Trigger Workflow in Another Repo
83+
env:
84+
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
85+
run: |
86+
gh workflow run cli-isolated-pr.yml -R ${{ secrets.ORG_NAME }}/${{secrets.REPO_NAME}} \
87+
--ref omer-lachish_data/deco-26060-cli-checks-api \
88+
-f pull_request_number=${{ github.event.pull_request.number }} \
89+
-f commit_sha=${{ github.event.pull_request.head.sha }} \
90+
-f check_run_id=${{ steps.create-check.outputs.check_run_id }}
91+
92+
93+
# The hash for the merge queue may not be the same as the hash for the PR.
94+
# Auto approve the check for the merge queue to avoid running integration tests twice.
95+
auto-approve:
96+
if: github.event_name == 'merge_group'
97+
98+
runs-on:
99+
group: databricks-deco-testing-runner-group
100+
labels: ubuntu-latest-deco
101+
102+
permissions:
103+
checks: write
104+
contents: read
105+
106+
steps:
107+
- name: Auto-approve Check for Merge Queue
108+
uses: actions/github-script@v7
109+
with:
110+
script: |
111+
await github.rest.checks.create({
112+
owner: context.repo.owner,
113+
repo: context.repo.repo,
114+
name: 'Integration Tests',
115+
head_sha: context.sha,
116+
status: 'completed',
117+
conclusion: 'success',
118+
output: {
119+
title: 'Integration Tests',
120+
summary: 'Auto-approved for merge queue (tests already passed on PR)'
121+
}
122+
});

.github/workflows/start-integration-tests.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
if: "${{ !github.event.pull_request.head.repo.fork }}"
2323

2424
steps:
25-
- name: Generate GitHub App Token
25+
- name: Generate GitHub App Token for Workflow Trigger
2626
id: generate-token
2727
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
2828
with:
@@ -31,11 +31,20 @@ jobs:
3131
owner: ${{ secrets.ORG_NAME }}
3232
repositories: ${{secrets.REPO_NAME}}
3333

34+
- name: Generate GitHub App Token for Check Updates
35+
id: generate-check-token
36+
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6
37+
with:
38+
app-id: ${{ secrets.DECO_TEST_APPROVAL_APP_ID }}
39+
private-key: ${{ secrets.DECO_TEST_APPROVAL_PRIVATE_KEY }}
40+
owner: databricks
41+
3442
- name: Fetch start_integration_tests.py
3543
run: wget https://raw.githubusercontent.com/databricks/cli/refs/heads/main/tools/start_integration_tests.py
3644

3745
- name: Run start_integration_tests.py
3846
env:
3947
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
48+
GH_CHECK_TOKEN: ${{ steps.generate-check-token.outputs.token }}
4049
run: |-
4150
python3 ./start_integration_tests.py -R ${{ secrets.ORG_NAME }}/${{secrets.REPO_NAME}} --yes

tools/start_integration_tests.py

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88

99
import argparse
1010
import json
11+
import os
1112
import subprocess
1213
import sys
13-
from pathlib import Path
14-
import re
1514

1615

1716
CLI_REPO = "databricks/cli"
@@ -20,14 +19,17 @@
2019
ALLOWED_HEAD_OWNER = {"id": "MDEyOk9yZ2FuaXphdGlvbjQ5OTgwNTI=", "login": "databricks"}
2120

2221

23-
def run(cmd):
22+
def run(cmd, env=None):
2423
sys.stderr.write("+ " + " ".join(cmd) + "\n")
25-
return subprocess.run(cmd, check=True)
24+
return subprocess.run(cmd, check=True, env=env)
2625

2726

28-
def run_json(cmd):
27+
def run_json(cmd, env=None):
2928
sys.stderr.write("+ " + " ".join(cmd) + "\n")
30-
result = subprocess.run(cmd, stdout=subprocess.PIPE, encoding="utf-8", check=True)
29+
run_env = os.environ.copy()
30+
if env:
31+
run_env.update(env)
32+
result = subprocess.run(cmd, stdout=subprocess.PIPE, encoding="utf-8", check=True, env=run_env)
3133

3234
try:
3335
return json.loads(result.stdout)
@@ -36,6 +38,38 @@ def run_json(cmd):
3638
raise
3739

3840

41+
def create_check(commit_sha):
42+
"""Create a check run for the given commit and return the check_run_id."""
43+
check_token = os.environ.get("GH_CHECK_TOKEN")
44+
if not check_token:
45+
print("Warning: GH_CHECK_TOKEN not set, skipping check creation")
46+
return None
47+
48+
response = run_json(
49+
[
50+
"gh",
51+
"api",
52+
"-X",
53+
"POST",
54+
f"/repos/{CLI_REPO}/check-runs",
55+
"-f",
56+
"name=Integration Tests",
57+
"-f",
58+
f"head_sha={commit_sha}",
59+
"-f",
60+
"status=queued",
61+
"-f",
62+
"output[title]=Integration Tests",
63+
"-f",
64+
"output[summary]=Tests queued and will be triggered shortly...",
65+
],
66+
env={"GH_TOKEN": check_token},
67+
)
68+
check_run_id = response.get("id")
69+
print(f"Created check run: {check_run_id}")
70+
return check_run_id
71+
72+
3973
def get_approved_prs_by_non_team():
4074
prs = run_json(
4175
[
@@ -108,30 +142,42 @@ def start_job(pr_number, commit_sha, author, approved_by, workflow, repo, force=
108142
response = input("Start integration tests? (y/n): ")
109143

110144
if response.lower() == "y":
111-
result = run(
112-
[
113-
"gh",
114-
"workflow",
115-
"run",
116-
workflow,
117-
"-R",
118-
repo,
119-
"-F",
120-
f"pull_request_number={pr_number}",
121-
"-F",
122-
f"commit_sha={commit_sha}",
123-
],
124-
)
145+
check_run_id = create_check(commit_sha)
146+
147+
cmd = [
148+
"gh",
149+
"workflow",
150+
"run",
151+
workflow,
152+
"-R",
153+
repo,
154+
"--ref",
155+
"omer-lachish_data/deco-26060-cli-checks-api", # TODO: remove after testing
156+
"-F",
157+
f"pull_request_number={pr_number}",
158+
"-F",
159+
f"commit_sha={commit_sha}",
160+
]
161+
if check_run_id:
162+
cmd.extend(["-F", f"check_run_id={check_run_id}"])
163+
164+
run(cmd)
125165
print(f"Started integration tests for PR #{pr_number}")
126166

127167

128168
def get_status(commit_sha):
129-
statuses = run_json(["gh", "api", f"repos/{CLI_REPO}/commits/{commit_sha}/statuses"])
169+
response = run_json(["gh", "api", f"repos/{CLI_REPO}/commits/{commit_sha}/check-runs"])
130170
result = []
131-
for st in statuses:
132-
if st["context"] != "Integration Tests Check":
171+
for check in response.get("check_runs", []):
172+
if check["name"] != "Integration Tests":
133173
continue
134-
result.append(f"{st['state']} {st['target_url']}")
174+
status = check["status"]
175+
conclusion = check.get("conclusion", "")
176+
details_url = check.get("details_url", "")
177+
if conclusion:
178+
result.append(f"{conclusion} {details_url}")
179+
else:
180+
result.append(f"{status} {details_url}")
135181
return result
136182

137183

0 commit comments

Comments
 (0)