-
Notifications
You must be signed in to change notification settings - Fork 27
169 lines (150 loc) · 6.63 KB
/
Copy pathtest-full.yml
File metadata and controls
169 lines (150 loc) · 6.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# 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