Skip to content

Commit e99f162

Browse files
committed
feat(ci): add cassette recording workflow for auto-sync PRs
Lightweight workflow that runs specific integration tests against staging with OVERWRITE=1 to record missing VCR cassettes, then commits them back to the PR branch. Triggered via repository_dispatch from gdc-nas after the implement agent creates a PR with integration tests that need cassettes, or manually with a PR number and test node IDs.
1 parent d7f50b7 commit e99f162

1 file changed

Lines changed: 144 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# =============================================================================
2+
# Record Cassettes for Auto-Generated SDK PRs
3+
#
4+
# Runs specific integration tests against staging with OVERWRITE=1 to record
5+
# missing VCR cassettes, then commits them back to the PR branch.
6+
#
7+
# HOW IT RUNS
8+
# -----------
9+
# 1. Automatically — triggered by repository_dispatch from gdc-nas after the
10+
# implement agent creates a PR with integration tests that need cassettes.
11+
#
12+
# 2. Manually — provide PR number and test node IDs:
13+
# gh workflow run sdk-py-cassette-record.yml \
14+
# -f pr_number=1530 \
15+
# -f test_nodes="packages/gooddata-sdk/tests/catalog/test_catalog_workspace_content.py::test_resolve_llm_providers_integration"
16+
#
17+
# =============================================================================
18+
name: Record Cassettes
19+
20+
on:
21+
repository_dispatch:
22+
types: [record-cassettes]
23+
workflow_dispatch:
24+
inputs:
25+
pr_number:
26+
description: 'PR number to record cassettes for'
27+
required: true
28+
test_nodes:
29+
description: 'Space-separated pytest node IDs to run against staging'
30+
required: true
31+
branch:
32+
description: 'PR branch name (auto-detected from PR if empty)'
33+
required: false
34+
default: ''
35+
36+
concurrency:
37+
group: cassette-record-${{ github.event.client_payload.pr_number || inputs.pr_number }}
38+
cancel-in-progress: true
39+
40+
jobs:
41+
record:
42+
name: "Record Cassettes"
43+
runs-on:
44+
group: infra1-runners-arc
45+
labels: runners-small
46+
timeout-minutes: 30
47+
permissions:
48+
contents: write
49+
pull-requests: read
50+
51+
steps:
52+
- name: Resolve inputs
53+
id: meta
54+
env:
55+
EVENT_NAME: ${{ github.event_name }}
56+
DISPATCH_PR: ${{ github.event.client_payload.pr_number }}
57+
DISPATCH_BRANCH: ${{ github.event.client_payload.branch }}
58+
DISPATCH_TESTS: ${{ github.event.client_payload.test_nodes }}
59+
INPUT_PR: ${{ inputs.pr_number }}
60+
INPUT_BRANCH: ${{ inputs.branch }}
61+
INPUT_TESTS: ${{ inputs.test_nodes }}
62+
run: |
63+
if [ "$EVENT_NAME" = "repository_dispatch" ]; then
64+
PR_NUMBER="$DISPATCH_PR"
65+
BRANCH="$DISPATCH_BRANCH"
66+
TEST_NODES="$DISPATCH_TESTS"
67+
else
68+
PR_NUMBER="$INPUT_PR"
69+
BRANCH="$INPUT_BRANCH"
70+
TEST_NODES="$INPUT_TESTS"
71+
fi
72+
73+
if [ -z "$PR_NUMBER" ] || [ -z "$TEST_NODES" ]; then
74+
echo "ERROR: pr_number and test_nodes are required"
75+
exit 1
76+
fi
77+
78+
# Auto-detect branch from PR if not provided
79+
if [ -z "$BRANCH" ]; then
80+
BRANCH=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER --jq '.head.ref')
81+
fi
82+
83+
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
84+
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
85+
echo "test_nodes=$TEST_NODES" >> "$GITHUB_OUTPUT"
86+
echo "PR: #$PR_NUMBER | Branch: $BRANCH | Tests: $TEST_NODES"
87+
env:
88+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
89+
90+
- name: Checkout PR branch
91+
uses: actions/checkout@v6
92+
with:
93+
ref: ${{ steps.meta.outputs.branch }}
94+
token: ${{ secrets.TOKEN_GITHUB_YENKINS_ADMIN }}
95+
96+
- name: Setup uv
97+
uses: astral-sh/setup-uv@v7
98+
99+
- name: Install dependencies
100+
run: uv sync --group test --locked
101+
102+
- name: Run targeted tests against staging
103+
env:
104+
OVERWRITE: "1"
105+
TOKEN: ${{ secrets.PYTHON_SDK_STG_API_KEY }}
106+
GD_TEST_ENV: staging
107+
run: |
108+
TEST_NODES="${{ steps.meta.outputs.test_nodes }}"
109+
echo "Recording cassettes for: $TEST_NODES"
110+
111+
# Run tests — allow failure (some tests may fail if endpoint
112+
# isn't deployed to staging yet, but cassettes still get recorded)
113+
uv run tox -e py312 -- $TEST_NODES || {
114+
echo "::warning::Some tests failed — cassettes may be partially recorded"
115+
}
116+
117+
- name: Commit and push cassettes
118+
run: |
119+
# Stage only cassette YAML files
120+
git add "packages/gooddata-sdk/tests/**/fixtures/**/*.yaml" \
121+
"packages/gooddata-sdk/tests/**/fixtures/*.yaml" || true
122+
123+
if git diff --cached --quiet; then
124+
echo "No cassettes were recorded"
125+
echo "::warning::No new cassette files found — tests may not have produced recordings"
126+
exit 0
127+
fi
128+
129+
CASSETTE_COUNT=$(git diff --cached --name-only | wc -l)
130+
echo "Committing $CASSETTE_COUNT cassette file(s)"
131+
132+
git config user.name "yenkins-admin"
133+
git config user.email "5391010+yenkins-admin@users.noreply.github.com"
134+
git commit -m "chore(cassettes): record cassettes for auto-sync tests"
135+
git push
136+
137+
- name: Write summary
138+
if: always()
139+
run: |
140+
echo "### Cassette Recording" >> "$GITHUB_STEP_SUMMARY"
141+
echo "" >> "$GITHUB_STEP_SUMMARY"
142+
echo "**PR:** #${{ steps.meta.outputs.pr_number }}" >> "$GITHUB_STEP_SUMMARY"
143+
echo "**Branch:** ${{ steps.meta.outputs.branch }}" >> "$GITHUB_STEP_SUMMARY"
144+
echo "**Tests:** \`${{ steps.meta.outputs.test_nodes }}\`" >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)