|
| 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