Skip to content

Commit d0b9e6f

Browse files
authored
ci: clean up api-sync workflow (dedup + CI triggers + commit type) (#19)
* ci: dedup api-sync PRs and trigger CI via RELEASE_TOKEN Open api-sync PRs were accumulating daily because the workflow did not check whether a prior PR already carried the current upstream spec. Compounding this, GITHUB_TOKEN-authored pushes and PRs do not trigger further workflow runs, so required CI checks never ran and the PRs sat BLOCKED by branch protection. - Add a dedup step that compares the new spec SHA against the spec on each open api-sync PR head; skip branch creation if a match is found. - Check out with RELEASE_TOKEN in detect and implement jobs so pushed commits trigger CI. - Create the PR with RELEASE_TOKEN so required checks run on it. * ci: use chore prefix for spec-only api-sync PRs Spec-only upstream changes (e.g. an internal schema class rename) should not bump the zad-cli version, but squash-merging the PR propagates its title to main, and a feat: prefix triggers a minor release. Detect whether Claude's implement step actually added code under src/ or tests/ and set the PR title prefix accordingly: feat when new CLI surface landed, chore when only api/upstream-openapi.json moved.
1 parent d6260ab commit d0b9e6f

1 file changed

Lines changed: 57 additions & 3 deletions

File tree

.github/workflows/api-sync.yml

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ jobs:
2222

2323
steps:
2424
- uses: actions/checkout@v6
25+
with:
26+
# Use a PAT so pushed commits and created PRs trigger required CI
27+
# checks. GITHUB_TOKEN-authored events deliberately do not trigger
28+
# further workflow runs, which leaves api-sync PRs stuck in BLOCKED.
29+
token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
2530

2631
- name: Install uv
2732
uses: astral-sh/setup-uv@v7
@@ -110,9 +115,42 @@ jobs:
110115
echo "${DELIM}"
111116
} >> "$GITHUB_OUTPUT"
112117
118+
- name: Skip if an open api-sync PR already carries this spec
119+
id: dedup
120+
if: steps.diff.outputs.has_changes == 'true'
121+
env:
122+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
123+
run: |
124+
NEW_SHA=$(sha256sum /tmp/new-openapi.json | awk '{print $1}')
125+
echo "new_sha=$NEW_SHA"
126+
127+
# For each open api-sync PR, fetch the spec from its head and compare.
128+
PRS=$(gh pr list --state open --label api-sync --json number,headRefName --jq '.[] | "\(.number) \(.headRefName)"')
129+
DUPLICATE=""
130+
while IFS= read -r line; do
131+
[ -z "$line" ] && continue
132+
NUM=$(echo "$line" | awk '{print $1}')
133+
REF=$(echo "$line" | cut -d' ' -f2-)
134+
# Grab the spec at that branch's tip via the API (no extra clone needed).
135+
if gh api "repos/$GITHUB_REPOSITORY/contents/api/upstream-openapi.json?ref=$REF" --jq .content 2>/dev/null | base64 -d > /tmp/existing-openapi.json; then
136+
EXISTING_SHA=$(sha256sum /tmp/existing-openapi.json | awk '{print $1}')
137+
if [ "$EXISTING_SHA" = "$NEW_SHA" ]; then
138+
DUPLICATE="$NUM"
139+
break
140+
fi
141+
fi
142+
done <<< "$PRS"
143+
144+
if [ -n "$DUPLICATE" ]; then
145+
echo "Open PR #$DUPLICATE already carries this spec content - skipping"
146+
echo "skip=true" >> "$GITHUB_OUTPUT"
147+
else
148+
echo "skip=false" >> "$GITHUB_OUTPUT"
149+
fi
150+
113151
- name: Create branch and update spec
114152
id: branch
115-
if: steps.diff.outputs.has_changes == 'true'
153+
if: steps.diff.outputs.has_changes == 'true' && steps.dedup.outputs.skip != 'true'
116154
run: |
117155
cp /tmp/new-openapi.json api/upstream-openapi.json
118156
git add api/upstream-openapi.json
@@ -148,6 +186,7 @@ jobs:
148186
- uses: actions/checkout@v6
149187
with:
150188
ref: ${{ needs.detect.outputs.branch }}
189+
token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
151190

152191
- name: Install uv
153192
uses: astral-sh/setup-uv@v7
@@ -303,11 +342,26 @@ jobs:
303342
- Commit your changes with a conventional commit message (e.g. "feat: add clone database command").
304343
- Do NOT add co-author lines to commit messages.
305344
345+
- name: Determine commit type
346+
id: commit_type
347+
run: |
348+
# If Claude added or changed code (anything under src/ or tests/),
349+
# this PR ships a user-visible capability and should be a feat.
350+
# If only the spec file moved, nothing user-facing changed, so it's
351+
# a chore and should not trigger a release.
352+
if git diff --name-only origin/main...HEAD | grep -qE '^(src/|tests/)'; then
353+
echo "prefix=feat" >> "$GITHUB_OUTPUT"
354+
else
355+
echo "prefix=chore" >> "$GITHUB_OUTPUT"
356+
fi
357+
306358
- name: Create pull request
307359
env:
308-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
360+
# Use RELEASE_TOKEN so PR creation triggers required CI checks.
361+
GH_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
309362
DIFF_SUMMARY: ${{ needs.detect.outputs.diff_summary }}
310363
COVERAGE_REPORT: ${{ needs.detect.outputs.coverage_report }}
364+
PREFIX: ${{ steps.commit_type.outputs.prefix }}
311365
run: |
312366
DATE=$(date +%Y-%m-%d)
313367
@@ -323,7 +377,7 @@ jobs:
323377
> /tmp/pr-body.md
324378
325379
gh pr create \
326-
--title "feat: sync with upstream API changes ${DATE}" \
380+
--title "${PREFIX}: sync with upstream API changes ${DATE}" \
327381
--body-file /tmp/pr-body.md \
328382
--label "api-sync,automated" \
329383
--base main

0 commit comments

Comments
 (0)