2222 contract-check :
2323 name : API Contract Check
2424 runs-on : ubuntu-latest
25+ outputs :
26+ has_changes : ${{ steps.contract-diff.outputs.has_changes }}
27+ breaking_changes : ${{ steps.contract-diff.outputs.breaking_changes }}
2528 steps :
2629 - name : Checkout PR code
2730 uses : actions/checkout@v5
3538 check-latest : true
3639 cache : true
3740
38- - name : Build modcli
41+ - name : Build modcli (PR workspace)
3942 run : |
4043 cd cmd/modcli
4144 go build -o modcli
5053 echo "Main commit: $MAIN_SHA"
5154 git worktree add --quiet main-worktree "$MAIN_SHA"
5255
56+ echo "==> Building modcli in main worktree"
57+ ( cd main-worktree/cmd/modcli && go build -o modcli )
58+
5359 echo "==> Extracting contracts from origin/main snapshot"
5460 ( cd main-worktree && ./cmd/modcli/modcli contract extract . -o ../artifacts/contracts/main/core.json || echo "Failed core framework extraction (main)" )
5561 for module_dir in main-worktree/modules/*/; do
6066 fi
6167 done
6268
69+ echo "==> Rebuilding modcli in PR workspace"
70+ ( cd cmd/modcli && go build -o modcli )
71+
6372 echo "==> Extracting contracts from PR (current) workspace"
6473 ./cmd/modcli/modcli contract extract . -o artifacts/contracts/pr/core.json || echo "Failed core framework extraction (pr)"
6574 for module_dir in modules/*/; do
7786 id : contract-diff
7887 run : |
7988 mkdir -p artifacts/diffs
80-
8189 breaking_changes=false
8290 has_changes=false
83-
91+
8492 # Compare core framework
8593 if [ -f "artifacts/contracts/main/core.json" ] && [ -f "artifacts/contracts/pr/core.json" ]; then
8694 echo "Comparing core framework contract..."
92100 has_changes=true
93101 fi
94102 fi
95-
103+
96104 # Compare all modules
97105 for module_dir in modules/*/; do
98106 module_name=$(basename "$module_dir")
@@ -107,7 +115,7 @@ jobs:
107115 fi
108116 fi
109117 done
110-
118+
111119 echo "breaking_changes=$breaking_changes" >> $GITHUB_OUTPUT
112120 echo "has_changes=$has_changes" >> $GITHUB_OUTPUT
113121
@@ -125,27 +133,21 @@ jobs:
125133 run : |
126134 echo "## 📋 API Contract Changes Summary" > contract-summary.md
127135 echo "" >> contract-summary.md
128-
129136 if [ "${{ steps.contract-diff.outputs.breaking_changes }}" == "true" ]; then
130137 echo "⚠️ **WARNING: This PR contains breaking API changes!**" >> contract-summary.md
131138 echo "" >> contract-summary.md
132139 else
133140 echo "✅ **No breaking changes detected - only additions and non-breaking modifications**" >> contract-summary.md
134141 echo "" >> contract-summary.md
135142 fi
136-
137143 echo "### Changed Components:" >> contract-summary.md
138144 echo "" >> contract-summary.md
139-
140- # Add core framework diff if it exists
141145 if [ -f "artifacts/diffs/core.md" ] && [ -s "artifacts/diffs/core.md" ]; then
142146 echo "#### Core Framework" >> contract-summary.md
143147 echo "" >> contract-summary.md
144148 cat artifacts/diffs/core.md >> contract-summary.md
145149 echo "" >> contract-summary.md
146150 fi
147-
148- # Add module diffs
149151 for diff_file in artifacts/diffs/*.md; do
150152 if [ -f "$diff_file" ] && [ -s "$diff_file" ]; then
151153 module_name=$(basename "$diff_file" .md)
@@ -157,9 +159,8 @@ jobs:
157159 fi
158160 fi
159161 done
160-
161162 echo "### Artifacts" >> contract-summary.md
162- echo "" >> contract-summary.md
163+ echo "" >> contract-summary.md
163164 echo "📁 Full contract diffs and JSON artifacts are available in the [workflow artifacts](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})." >> contract-summary.md
164165
165166 - name : Comment PR with contract changes
@@ -169,32 +170,25 @@ jobs:
169170 script : |
170171 const fs = require('fs');
171172 const path = 'contract-summary.md';
172-
173173 if (fs.existsSync(path)) {
174174 const summary = fs.readFileSync(path, 'utf8');
175-
176- // Find existing contract comment
177175 const { data: comments } = await github.rest.issues.listComments({
178176 owner: context.repo.owner,
179177 repo: context.repo.repo,
180178 issue_number: context.payload.pull_request.number,
181179 });
182-
183- const botComment = comments.find(comment =>
184- comment.user.type === 'Bot' &&
180+ const botComment = comments.find(comment =>
181+ comment.user.type === 'Bot' &&
185182 comment.body.includes('📋 API Contract Changes Summary')
186183 );
187-
188184 if (botComment) {
189- // Update existing comment
190185 await github.rest.issues.updateComment({
191186 owner: context.repo.owner,
192187 repo: context.repo.repo,
193188 comment_id: botComment.id,
194189 body: summary
195190 });
196191 } else {
197- // Create new comment
198192 await github.rest.issues.createComment({
199193 owner: context.repo.owner,
200194 repo: context.repo.repo,
@@ -215,7 +209,6 @@ jobs:
215209 echo "4. Communicating breaking changes to users"
216210 exit 1
217211
218- # Success job that only runs if contract check passes or no changes
219212 contract-passed :
220213 name : API Contract Passed
221214 runs-on : ubuntu-latest
0 commit comments