|
3 | 3 | # This workflow validates that Speakeasy generation can complete successfully |
4 | 4 | # and that the committed generated code matches what the generation pipeline produces. |
5 | 5 | # |
6 | | -# Jobs: |
7 | | -# 1. validate: Runs the full generation pipeline in dry-run mode |
8 | | -# 2. zero-diff: Compares the dry-run artifacts against the committed code to detect drift. |
9 | | -# If drift is detected, the check fails and posts a comment telling the author to run /generate. |
10 | | -# |
11 | | -# This workflow calls the main generation workflow with dry_run=true to ensure |
12 | | -# both workflows use the same generation logic. |
| 6 | +# It calls the main generation workflow with dry_run=true. After generation, |
| 7 | +# the workflow checks `git status` in-place — if any tracked files changed, |
| 8 | +# drift is detected and the check fails with a PR comment. |
13 | 9 | # |
14 | 10 | # Note: paths-ignore is NOT used at the workflow level because GitHub treats a |
15 | 11 | # workflow that never runs as "expected" (pending), which blocks required checks. |
|
25 | 21 | permissions: |
26 | 22 | contents: write |
27 | 23 | pull-requests: write |
28 | | - actions: read |
29 | 24 |
|
30 | 25 | jobs: |
31 | 26 | check-paths: |
@@ -54,116 +49,3 @@ jobs: |
54 | 49 | with: |
55 | 50 | dry_run: true |
56 | 51 | secrets: inherit |
57 | | - |
58 | | - zero-diff: |
59 | | - name: Zero-Diff Check (Generated Code) |
60 | | - needs: [check-paths, validate] |
61 | | - if: needs.check-paths.outputs.should_run == 'true' && github.event_name == 'pull_request' |
62 | | - runs-on: ubuntu-latest |
63 | | - permissions: |
64 | | - contents: read |
65 | | - pull-requests: write |
66 | | - steps: |
67 | | - - name: Checkout PR branch |
68 | | - uses: actions/checkout@v4 |
69 | | - |
70 | | - - name: Download generated SDK artifact |
71 | | - uses: actions/download-artifact@v8 |
72 | | - with: |
73 | | - name: generated_sdk_code |
74 | | - path: /tmp/generated/ |
75 | | - |
76 | | - - name: Compare generated code against committed code |
77 | | - id: diff-check |
78 | | - run: | |
79 | | - DIFF_SUMMARY="" |
80 | | -
|
81 | | - echo "=== Comparing generated SDK code ===" |
82 | | - # Compare src/ directory |
83 | | - if [ -d "src/" ] && [ -d "/tmp/generated/src/" ]; then |
84 | | - while IFS= read -r line; do |
85 | | - # Extract relative path from diff output |
86 | | - FILE=$(echo "$line" | sed 's|^Files ||; s| and /tmp/generated/.*||') |
87 | | - ADDED=$(diff -u "$FILE" "/tmp/generated/$FILE" 2>/dev/null | tail -n +3 | grep -c '^+' || echo "0") |
88 | | - REMOVED=$(diff -u "$FILE" "/tmp/generated/$FILE" 2>/dev/null | tail -n +3 | grep -c '^-' || echo "0") |
89 | | - DIFF_SUMMARY="${DIFF_SUMMARY}${FILE} (+${ADDED}/-${REMOVED})"$'\n' |
90 | | - done < <(diff -rq src/ /tmp/generated/src/ 2>&1 | grep "^Files" || true) |
91 | | -
|
92 | | - # Check for files only in one side |
93 | | - ONLY_LINES=$(diff -rq src/ /tmp/generated/src/ 2>&1 | grep "^Only" || true) |
94 | | - if [ -n "$ONLY_LINES" ]; then |
95 | | - while IFS= read -r line; do |
96 | | - DIR=$(echo "$line" | sed 's|^Only in /tmp/generated/||; s|^Only in ||; s|: |/|') |
97 | | - if echo "$line" | grep -q "^Only in /tmp/generated/"; then |
98 | | - DIFF_SUMMARY="${DIFF_SUMMARY}${DIR} (new file)"$'\n' |
99 | | - else |
100 | | - DIFF_SUMMARY="${DIFF_SUMMARY}${DIR} (deleted)"$'\n' |
101 | | - fi |
102 | | - done <<< "$ONLY_LINES" |
103 | | - fi |
104 | | - elif [ -d "/tmp/generated/src/" ]; then |
105 | | - DIFF_SUMMARY="src/ directory missing in committed code but present in generated output"$'\n' |
106 | | - fi |
107 | | -
|
108 | | - # Compare pyproject.toml |
109 | | - if [ -f "/tmp/generated/pyproject.toml" ]; then |
110 | | - TOML_DIFF=$(diff -q pyproject.toml /tmp/generated/pyproject.toml 2>&1 || true) |
111 | | - if [ -n "$TOML_DIFF" ]; then |
112 | | - ADDED=$(diff -u pyproject.toml /tmp/generated/pyproject.toml 2>/dev/null | tail -n +3 | grep -c '^+' || echo "0") |
113 | | - REMOVED=$(diff -u pyproject.toml /tmp/generated/pyproject.toml 2>/dev/null | tail -n +3 | grep -c '^-' || echo "0") |
114 | | - DIFF_SUMMARY="${DIFF_SUMMARY}pyproject.toml (+${ADDED}/-${REMOVED})"$'\n' |
115 | | - fi |
116 | | - fi |
117 | | -
|
118 | | - if [ -n "$DIFF_SUMMARY" ]; then |
119 | | - echo "has_diff=true" >> $GITHUB_OUTPUT |
120 | | - echo "::warning::Generated code drift detected. The committed code does not match what the generation pipeline produces." |
121 | | - echo "$DIFF_SUMMARY" |
122 | | - echo "$DIFF_SUMMARY" > /tmp/diff_summary.txt |
123 | | - else |
124 | | - echo "has_diff=false" >> $GITHUB_OUTPUT |
125 | | - echo "Zero-diff check passed. Committed code matches generation output." |
126 | | - fi |
127 | | -
|
128 | | - - name: Prepare diff summary |
129 | | - if: steps.diff-check.outputs.has_diff == 'true' |
130 | | - id: diff-summary |
131 | | - run: | |
132 | | - SUMMARY=$(cat /tmp/diff_summary.txt 2>/dev/null || echo "(see job logs for full details)") |
133 | | - EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) |
134 | | - echo "content<<$EOF" >> $GITHUB_OUTPUT |
135 | | - echo "$SUMMARY" >> $GITHUB_OUTPUT |
136 | | - echo "$EOF" >> $GITHUB_OUTPUT |
137 | | -
|
138 | | - - name: Find existing drift comment |
139 | | - if: steps.diff-check.outputs.has_diff == 'true' |
140 | | - uses: peter-evans/find-comment@v3 |
141 | | - id: find-drift-comment |
142 | | - with: |
143 | | - issue-number: ${{ github.event.pull_request.number }} |
144 | | - body-includes: '<!-- zero-diff-check -->' |
145 | | - |
146 | | - - name: Post drift comment on PR |
147 | | - if: steps.diff-check.outputs.has_diff == 'true' |
148 | | - uses: peter-evans/create-or-update-comment@v5 |
149 | | - with: |
150 | | - issue-number: ${{ github.event.pull_request.number }} |
151 | | - comment-id: ${{ steps.find-drift-comment.outputs.comment-id || '' }} |
152 | | - edit-mode: replace |
153 | | - body: | |
154 | | - <!-- zero-diff-check --> |
155 | | - **Generated Code Drift Detected** |
156 | | -
|
157 | | - The committed code does not match what the generation pipeline produces. |
158 | | -
|
159 | | - **To fix:** Comment `/generate` on this PR to regenerate. |
160 | | -
|
161 | | - ``` |
162 | | - ${{ steps.diff-summary.outputs.content }} |
163 | | - ``` |
164 | | -
|
165 | | - - name: Fail if drift detected |
166 | | - if: steps.diff-check.outputs.has_diff == 'true' |
167 | | - run: | |
168 | | - echo "::error::Generated code drift detected. Run /generate on this PR to fix." |
169 | | - exit 1 |
0 commit comments