Skip to content

Commit 5cb1df2

Browse files
committed
fix: use cargo-public-api instead
1 parent fdc6038 commit 5cb1df2

File tree

1 file changed

+104
-93
lines changed

1 file changed

+104
-93
lines changed

.github/workflows/semver-check.yaml

Lines changed: 104 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ on:
1111

1212
env:
1313
CARGO_TERM_COLOR: always
14-
RUST_VERSION: 1.84.1
14+
RUST_VERSION: stable
1515

1616
jobs:
1717
detect-changes:
@@ -79,7 +79,6 @@ jobs:
7979
echo "No published crates changed in this PR"
8080
else
8181
echo "has_changes=true" >> "$GITHUB_OUTPUT"
82-
# Create JSON array
8382
CRATES_JSON=$(printf '%s\n' "${CHANGED_CRATES[@]}" | jq -R . | jq -s .)
8483
echo "crates=$CRATES_JSON" >> "$GITHUB_OUTPUT"
8584
echo "Changed published crates: ${CHANGED_CRATES[*]}"
@@ -92,7 +91,6 @@ jobs:
9291
outputs:
9392
semver_level: ${{ steps.aggregate.outputs.semver_level }}
9493
failed_crates: ${{ steps.aggregate.outputs.failed_crates }}
95-
skipped_crates: ${{ steps.aggregate.outputs.skipped_crates }}
9694
steps:
9795
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
9896
with:
@@ -109,7 +107,8 @@ jobs:
109107

110108
- name: Install cargo-semver-checks
111109
run: |
112-
cargo install cargo-semver-checks --locked --version 0.44.0
110+
apt install libssl-dev # cargo-public-api dependency
111+
cargo install cargo-public-api --locked
113112
114113
- name: Run semver checks on changed crates
115114
id: semver
@@ -129,38 +128,71 @@ jobs:
129128
echo "Checking semver for: $CRATE_NAME"
130129
echo "========================================"
131130
132-
# Try to run cargo-semver-checks
131+
# Get the current version from Cargo.toml
132+
CRATE_MANIFEST=$(find . -name Cargo.toml -exec grep -l "name = \"$CRATE_NAME\"" {} \; | head -1)
133+
echo "MANIFEST: $CRATE_MANIFEST"
134+
if [[ -z "$CRATE_MANIFEST" ]]; then
135+
echo "Could not find Cargo.toml for $CRATE_NAME"
136+
continue
137+
fi
138+
139+
CURRENT_VERSION=$(grep "^version" "$CRATE_MANIFEST" | head -1 | sed 's/version[[:space:]]*=[[:space:]]*"\(.*\)"/\1/')
140+
echo "Current version: $CURRENT_VERSION"
141+
142+
# Try to run cargo-public-api diff
133143
set +e
134-
cargo semver-checks check-release --package "$CRATE_NAME" --verbose 2>&1 | tee semver-output.txt
144+
cargo public-api --package "$CRATE_NAME" diff "$CURRENT_VERSION" 2>&1 | tee api-output.txt
135145
EXIT_CODE=$?
136146
set -e
137147
138148
if [[ $EXIT_CODE -ne 0 ]]; then
139-
# Check if failure is due to crate not being published
140-
if grep -qE "not found in registry|baseline not found|no baseline found|could not find .* in registry" semver-output.txt; then
141-
echo "$CRATE_NAME not published or no baseline available"
142-
continue
149+
echo "Unexpected error for $CRATE_NAME (exit code: $EXIT_CODE)"
150+
continue
151+
fi
152+
153+
# Analyze the diff output
154+
LEVEL="none"
155+
156+
# Check for removed items (major change)
157+
if grep -q "Removed items from the public API$" api-output.txt; then
158+
if ! grep -A 2 "^Removed items from the public API$" api-output.txt | grep -q "^(none)$"; then
159+
LEVEL="major"
160+
echo "Detected removed items (major change)"
161+
fi
162+
fi
163+
164+
# Check for changed items (major change)
165+
if grep -q "^Changed items in the public API$" api-output.txt; then
166+
if ! grep -A 2 "^Changed items in the public API$" api-output.txt | grep -q "^(none)$"; then
167+
LEVEL="major"
168+
echo "Detected changed items (major change)"
143169
fi
170+
fi
144171
145-
if grep -q "--- failure " semver-output.txt; then
146-
if grep -qE "major change|breaking change|function_missing|struct_missing|enum_variant_missing" semver-output.txt; then
147-
HIGHEST_LEVEL="major"
148-
CRATES_CHECKED+=("$CRATE_NAME:major")
149-
elif grep -qE "minor change|function_added|struct_added" semver-output.txt; then
150-
if [[ "$HIGHEST_LEVEL" != "major" ]]; then
151-
HIGHEST_LEVEL="minor"
152-
fi
153-
CRATES_CHECKED+=("$CRATE_NAME:minor")
154-
else
155-
if [[ "$HIGHEST_LEVEL" == "none" ]]; then
156-
HIGHEST_LEVEL="patch"
157-
fi
158-
CRATES_CHECKED+=("$CRATE_NAME:patch")
172+
# Check for added items (minor change) - only if not already major
173+
if [[ "$LEVEL" != "major" ]]; then
174+
if grep -q "Added items to the public API$" api-output.txt; then
175+
if ! grep -A 2 "^Added items to the public API$" api-output.txt | grep -q "^(none)"; then
176+
LEVEL="minor"
177+
echo "Detected added items (minor change)"
159178
fi
160-
else
161-
echo "Unexpected error for $CRATE_NAME (exit code: $EXIT_CODE)"
179+
fi
180+
fi
181+
182+
# If we detected changes, update the highest level
183+
if [[ "$LEVEL" != "none" ]]; then
184+
CRATES_CHECKED+=("$CRATE_NAME:$LEVEL")
185+
186+
# Update highest level
187+
if [[ "$LEVEL" == "major" ]]; then
188+
HIGHEST_LEVEL="major"
189+
elif [[ "$LEVEL" == "minor" ]] && [[ "$HIGHEST_LEVEL" != "major" ]]; then
190+
HIGHEST_LEVEL="minor"
191+
elif [[ "$HIGHEST_LEVEL" == "none" ]]; then
192+
HIGHEST_LEVEL="patch"
162193
fi
163194
else
195+
# No API changes detected, assume patch level
164196
if [[ "$HIGHEST_LEVEL" == "none" ]]; then
165197
HIGHEST_LEVEL="patch"
166198
fi
@@ -169,19 +201,19 @@ jobs:
169201
170202
# Save results to file for aggregate step
171203
echo "$HIGHEST_LEVEL" > semver-level.txt
172-
printf '%s\n' "${CRATES_CHECKED[@]}" > failed-crates.txt || true
204+
printf '%s\n' "${CRATES_CHECKED[@]}" > crates_checked.txt || true
173205
174206
- name: Aggregate results
175207
id: aggregate
176208
run: |
177209
HIGHEST_LEVEL=$(cat semver-level.txt)
178-
CRATES_CHECKED=$(cat failed-crates.txt | tr '\n' ' ' || echo "")
210+
CRATES_CHECKED=$(cat crates_checked.txt | tr '\n' ' ' || echo "")
179211
180212
echo "semver_level=$HIGHEST_LEVEL" >> "$GITHUB_OUTPUT"
181-
echo "failed_crates=$CRATES_CHECKED" >> "$GITHUB_OUTPUT"
213+
echo "crates_checked=$CRATES_CHECKED" >> "$GITHUB_OUTPUT"
182214
183215
echo "Final aggregate semver level: $HIGHEST_LEVEL"
184-
echo "Failed crates: $CRATES_CHECKED"
216+
echo "Crates checked: $CRATES_CHECKED"
185217
186218
validate:
187219
needs: [detect-changes, semver-check]
@@ -192,19 +224,18 @@ jobs:
192224
env:
193225
PR_TITLE: ${{ github.event.pull_request.title }}
194226
SEMVER_LEVEL: ${{ needs.semver-check.outputs.semver_level }}
195-
CRATES_CHECKED: ${{ needs.semver-check.outputs.failed_crates }}
196-
SKIPPED_CRATES: ${{ needs.semver-check.outputs.skipped_crates }}
227+
CRATES_CHECKED: ${{ needs.semver-check.outputs.crates_checked }}
197228
run: |
198229
set -euo pipefail
199230
200231
echo "PR Title: $PR_TITLE"
201232
echo "Detected semver level: $SEMVER_LEVEL"
202233
echo "Crates with changes: $CRATES_CHECKED"
203-
echo "Skipped crates: $SKIPPED_CRATES"
204234
205-
Format: type(optional-scope): description
206-
Breaking changes: type!: or type(scope)!:
207-
if [[ "$PR_TITLE" =~ ^([a-z]+)(\([^)]+\))?(!)?:\ .+ ]]; then
235+
# Format: type(optional-scope): description
236+
# Breaking changes: type!: or type(scope)!:
237+
REGEX='^([a-z]+)(\([^)]+\))?(!)?: .+'
238+
if [[ "$PR_TITLE" =~ $REGEX ]]; then
208239
TYPE="${BASH_REMATCH[1]}"
209240
HAS_BREAKING_MARKER="${BASH_REMATCH[3]}"
210241
else
@@ -221,79 +252,59 @@ jobs:
221252
222253
# Validation rules
223254
case "$TYPE" in
224-
fix)
225-
# fix: should only have patch-level changes
226-
if [[ "$SEMVER_LEVEL" == "major" ]] || [[ "$SEMVER_LEVEL" == "minor" ]]; then
227-
VALIDATION_FAILED="true"
228-
fi
229-
;;
255+
fix)
256+
if [[ "$SEMVER_LEVEL" == "major" ]] || [[ "$SEMVER_LEVEL" == "minor" ]] || [[ "$SEMVER_LEVEL" == "none" ]]; then
257+
VALIDATION_FAILED="true"
258+
fi
259+
;;
230260
231-
feat)
232-
if [[ "$SEMVER_LEVEL" == "major" ]] && [[ -z "$HAS_BREAKING_MARKER" ]]; then
233-
VALIDATION_FAILED="true"
234-
elif [[ "$SEMVER_LEVEL" == "patch" ]]; then
235-
VALIDATION_FAILED="true"
236-
fi
237-
;;
261+
feat)
262+
if [[ "$SEMVER_LEVEL" == "major" ]] && [[ -z "$HAS_BREAKING_MARKER" ]]; then
263+
VALIDATION_FAILED="true"
264+
elif [[ "$SEMVER_LEVEL" == "patch" ]] || [[ "$SEMVER_LEVEL" == "none" ]]; then
265+
VALIDATION_FAILED="true"
266+
fi
267+
;;
238268
239-
chore|docs|style|test|ci|build|perf)
240-
# These should not change public API
241-
if [[ "$SEMVER_LEVEL" == "major" ]] || [[ "$SEMVER_LEVEL" == "minor" ]]; then
242-
VALIDATION_FAILED="true"
243-
fi
244-
;;
269+
chore|ci|docs|style|test|build|perf)
270+
# Breaking change marker shouldn't be there.
271+
if [[ -z "$HAS_BREAKING_MARKER" ]]; then
272+
VALIDATION_FAILED="true"
273+
fi
245274
246-
refactor)
247-
# Should we allow refactors to introduce breaking changes.
248-
if [[ "$SEMVER_LEVEL" == "major" ]] && [[ -z "$HAS_BREAKING_MARKER" ]]; then
249-
VALIDATION_FAILED="true"
250-
fi
251-
;;
252-
253-
revert)
254-
# Revert commits are allowed to have any semver level
255-
echo "Revert commits are exempt from semver validation"
256-
;;
257-
258-
*)
259-
# "refactor" type fall through this category, should we be more specific about it?.
260-
echo "Commit type '$TYPE', applying lenient validation"
261-
# Only fail on undeclared breaking changes
262-
if [[ "$SEMVER_LEVEL" == "major" ]] && [[ -z "$HAS_BREAKING_MARKER" ]]; then
263-
VALIDATION_FAILED="true"
264-
fi
265-
;;
266-
esac
275+
# These should not change public API
276+
if [[ "$SEMVER_LEVEL" == "major" ]] || [[ "$SEMVER_LEVEL" == "minor" ]]; then
277+
VALIDATION_FAILED="true"
278+
fi
279+
;;
280+
281+
282+
refactor)
283+
if [[ "$SEMVER_LEVEL" == "major" ]] && [[ -z "$HAS_BREAKING_MARKER" ]]; then
284+
VALIDATION_FAILED="true"
285+
fi
286+
;;
287+
288+
revert)
289+
# Revert commits are allowed to have any semver level
290+
;;
267291
268-
# Check for breaking marker with non-breaking changes
269-
if [[ -n "$HAS_BREAKING_MARKER" ]] && [[ "$SEMVER_LEVEL" != "major" ]]; then
292+
*)
293+
echo "$TYPE not handled";
270294
VALIDATION_FAILED="true"
271-
fi
295+
;;
296+
esac
272297
273-
# Final result
274298
if [[ "$VALIDATION_FAILED" == "true" ]]; then
275299
echo ""
276300
echo "============================================"
277301
echo "❌ SEMVER VALIDATION FAILED"
278302
echo "============================================"
279303
echo ""
280-
echo "$ERROR_MESSAGE"
281-
echo ""
282304
echo "Details:"
283305
echo " PR Title: $PR_TITLE"
284306
echo " Detected semver level: $SEMVER_LEVEL"
285-
echo " Crates with API changes: $CRATES_CHECKED"
286-
echo ""
287-
echo "To fix this:"
288-
echo " 1. Update your PR title to match the detected changes, OR"
289-
echo " 2. Adjust your code changes to match the PR title intent"
290-
echo "============================================"
291307
exit 1
292308
else
293-
echo ""
294-
echo "============================================"
295-
echo "✅ SEMVER VALIDATION PASSED"
296-
echo "============================================"
297-
echo "PR title type matches detected API changes"
298309
exit 0
299310
fi

0 commit comments

Comments
 (0)