ci: use PYPI_TOKEN for publishing and fix infinite generate loop #67
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Validate Speakeasy Generation (Dry Run) + Zero-Diff Check | |
| # | |
| # This workflow validates that Speakeasy generation can complete successfully | |
| # and that the committed generated code matches what the generation pipeline produces. | |
| # | |
| # Jobs: | |
| # 1. validate: Runs the full generation pipeline in dry-run mode | |
| # 2. zero-diff: Compares the dry-run artifacts against the committed code to detect drift. | |
| # If drift is detected, the check fails and posts a comment telling the author to run /generate. | |
| # | |
| # This workflow calls the main generation workflow with dry_run=true to ensure | |
| # both workflows use the same generation logic. | |
| # | |
| # Note: paths-ignore is NOT used at the workflow level because GitHub treats a | |
| # workflow that never runs as "expected" (pending), which blocks required checks. | |
| # Instead, we filter paths at the job level so skipped jobs report as "skipped" | |
| # (equivalent to "passed" for required checks). | |
| name: Test (Full) | |
| on: | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| actions: read | |
| jobs: | |
| check-paths: | |
| name: Check Changed Paths | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Filter changed paths | |
| uses: dorny/paths-filter@v4 | |
| id: filter | |
| with: | |
| filters: | | |
| generation: | |
| - '**' | |
| - '!README.md' | |
| - '!docs/**' | |
| outputs: | |
| should_run: ${{ github.event_name == 'workflow_dispatch' || steps.filter.outputs.generation == 'true' }} | |
| validate: | |
| name: Validate Generation (Dry Run) | |
| needs: check-paths | |
| if: needs.check-paths.outputs.should_run == 'true' | |
| uses: ./.github/workflows/generate-command.yml | |
| with: | |
| dry_run: true | |
| secrets: inherit | |
| zero-diff: | |
| name: Zero-Diff Check (Generated Code) | |
| needs: [check-paths, validate] | |
| if: needs.check-paths.outputs.should_run == 'true' && github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - name: Checkout PR branch | |
| uses: actions/checkout@v4 | |
| - name: Download generated SDK artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: generated_sdk_code | |
| path: /tmp/generated/ | |
| - name: Compare generated code against committed code | |
| id: diff-check | |
| run: | | |
| DIFF_SUMMARY="" | |
| echo "=== Comparing generated SDK code ===" | |
| # Compare src/ directory | |
| if [ -d "src/" ] && [ -d "/tmp/generated/src/" ]; then | |
| while IFS= read -r line; do | |
| # Extract relative path from diff output | |
| FILE=$(echo "$line" | sed 's|^Files ||; s| and /tmp/generated/.*||') | |
| ADDED=$(diff -u "$FILE" "/tmp/generated/$FILE" 2>/dev/null | tail -n +3 | grep -c '^+' || echo "0") | |
| REMOVED=$(diff -u "$FILE" "/tmp/generated/$FILE" 2>/dev/null | tail -n +3 | grep -c '^-' || echo "0") | |
| DIFF_SUMMARY="${DIFF_SUMMARY}${FILE} (+${ADDED}/-${REMOVED})"$'\n' | |
| done < <(diff -rq src/ /tmp/generated/src/ 2>&1 | grep "^Files" || true) | |
| # Check for files only in one side | |
| ONLY_LINES=$(diff -rq src/ /tmp/generated/src/ 2>&1 | grep "^Only" || true) | |
| if [ -n "$ONLY_LINES" ]; then | |
| while IFS= read -r line; do | |
| DIR=$(echo "$line" | sed 's|^Only in /tmp/generated/||; s|^Only in ||; s|: |/|') | |
| if echo "$line" | grep -q "^Only in /tmp/generated/"; then | |
| DIFF_SUMMARY="${DIFF_SUMMARY}${DIR} (new file)"$'\n' | |
| else | |
| DIFF_SUMMARY="${DIFF_SUMMARY}${DIR} (deleted)"$'\n' | |
| fi | |
| done <<< "$ONLY_LINES" | |
| fi | |
| elif [ -d "/tmp/generated/src/" ]; then | |
| DIFF_SUMMARY="src/ directory missing in committed code but present in generated output"$'\n' | |
| fi | |
| # Compare pyproject.toml | |
| if [ -f "/tmp/generated/pyproject.toml" ]; then | |
| TOML_DIFF=$(diff -q pyproject.toml /tmp/generated/pyproject.toml 2>&1 || true) | |
| if [ -n "$TOML_DIFF" ]; then | |
| ADDED=$(diff -u pyproject.toml /tmp/generated/pyproject.toml 2>/dev/null | tail -n +3 | grep -c '^+' || echo "0") | |
| REMOVED=$(diff -u pyproject.toml /tmp/generated/pyproject.toml 2>/dev/null | tail -n +3 | grep -c '^-' || echo "0") | |
| DIFF_SUMMARY="${DIFF_SUMMARY}pyproject.toml (+${ADDED}/-${REMOVED})"$'\n' | |
| fi | |
| fi | |
| if [ -n "$DIFF_SUMMARY" ]; then | |
| echo "has_diff=true" >> $GITHUB_OUTPUT | |
| echo "::warning::Generated code drift detected. The committed code does not match what the generation pipeline produces." | |
| echo "$DIFF_SUMMARY" | |
| echo "$DIFF_SUMMARY" > /tmp/diff_summary.txt | |
| else | |
| echo "has_diff=false" >> $GITHUB_OUTPUT | |
| echo "Zero-diff check passed. Committed code matches generation output." | |
| fi | |
| - name: Prepare diff summary | |
| if: steps.diff-check.outputs.has_diff == 'true' | |
| id: diff-summary | |
| run: | | |
| SUMMARY=$(cat /tmp/diff_summary.txt 2>/dev/null || echo "(see job logs for full details)") | |
| EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) | |
| echo "content<<$EOF" >> $GITHUB_OUTPUT | |
| echo "$SUMMARY" >> $GITHUB_OUTPUT | |
| echo "$EOF" >> $GITHUB_OUTPUT | |
| - name: Find existing drift comment | |
| if: steps.diff-check.outputs.has_diff == 'true' | |
| uses: peter-evans/find-comment@v3 | |
| id: find-drift-comment | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| body-includes: '<!-- zero-diff-check -->' | |
| - name: Post drift comment on PR | |
| if: steps.diff-check.outputs.has_diff == 'true' | |
| uses: peter-evans/create-or-update-comment@v5 | |
| with: | |
| issue-number: ${{ github.event.pull_request.number }} | |
| comment-id: ${{ steps.find-drift-comment.outputs.comment-id || '' }} | |
| edit-mode: replace | |
| body: | | |
| <!-- zero-diff-check --> | |
| **Generated Code Drift Detected** | |
| The committed code does not match what the generation pipeline produces. | |
| **To fix:** Comment `/generate` on this PR to regenerate. | |
| ``` | |
| ${{ steps.diff-summary.outputs.content }} | |
| ``` | |
| - name: Fail if drift detected | |
| if: steps.diff-check.outputs.has_diff == 'true' | |
| run: | | |
| echo "::error::Generated code drift detected. Run /generate on this PR to fix." | |
| exit 1 |