Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 57 additions & 3 deletions .github/workflows/api-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ jobs:

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

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down Expand Up @@ -110,9 +115,42 @@ jobs:
echo "${DELIM}"
} >> "$GITHUB_OUTPUT"

- name: Skip if an open api-sync PR already carries this spec
id: dedup
if: steps.diff.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
NEW_SHA=$(sha256sum /tmp/new-openapi.json | awk '{print $1}')
echo "new_sha=$NEW_SHA"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor: echo "new_sha=$NEW_SHA" logs to stdout but does not write to $GITHUB_OUTPUT. If this is intentional debug output, rename it to avoid looking like an output assignment (e.g. echo "# debug: new_sha=$NEW_SHA"). If it was meant to expose the value as a step output for later use, add >> "$GITHUB_OUTPUT".

Since new_sha is not consumed anywhere in the workflow this is non-blocking, but it will confuse future readers.


# For each open api-sync PR, fetch the spec from its head and compare.
PRS=$(gh pr list --state open --label api-sync --json number,headRefName --jq '.[] | "\(.number) \(.headRefName)"')
DUPLICATE=""
while IFS= read -r line; do
[ -z "$line" ] && continue
NUM=$(echo "$line" | awk '{print $1}')
REF=$(echo "$line" | cut -d' ' -f2-)
# Grab the spec at that branch's tip via the API (no extra clone needed).
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor: Without set -o pipefail, the if tests only base64 -d's exit code. If gh api fails (e.g. the file does not exist on the branch), base64 -d receives empty input, exits 0, and the block is entered with an empty /tmp/existing-openapi.json. The SHA then mismatches so there are no false positives in practice, but the failure is silently swallowed.

A cleaner pattern that preserves the intent:

CONTENT=$(gh api "repos/$GITHUB_REPOSITORY/contents/api/upstream-openapi.json?ref=$REF" --jq .content 2>/dev/null) \
  && echo "$CONTENT" | base64 -d > /tmp/existing-openapi.json \

This makes gh api failure propagate correctly.

EXISTING_SHA=$(sha256sum /tmp/existing-openapi.json | awk '{print $1}')
if [ "$EXISTING_SHA" = "$NEW_SHA" ]; then
DUPLICATE="$NUM"
break
fi
fi
done <<< "$PRS"

if [ -n "$DUPLICATE" ]; then
echo "Open PR #$DUPLICATE already carries this spec content - skipping"
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi

- name: Create branch and update spec
id: branch
if: steps.diff.outputs.has_changes == 'true'
if: steps.diff.outputs.has_changes == 'true' && steps.dedup.outputs.skip != 'true'
run: |
cp /tmp/new-openapi.json api/upstream-openapi.json
git add api/upstream-openapi.json
Expand Down Expand Up @@ -148,6 +186,7 @@ jobs:
- uses: actions/checkout@v6
with:
ref: ${{ needs.detect.outputs.branch }}
token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}

- name: Install uv
uses: astral-sh/setup-uv@v7
Expand Down Expand Up @@ -303,11 +342,26 @@ jobs:
- Commit your changes with a conventional commit message (e.g. "feat: add clone database command").
- Do NOT add co-author lines to commit messages.

- name: Determine commit type
id: commit_type
run: |
# If Claude added or changed code (anything under src/ or tests/),
# this PR ships a user-visible capability and should be a feat.
# If only the spec file moved, nothing user-facing changed, so it's
# a chore and should not trigger a release.
if git diff --name-only origin/main...HEAD | grep -qE '^(src/|tests/)'; then
echo "prefix=feat" >> "$GITHUB_OUTPUT"
else
echo "prefix=chore" >> "$GITHUB_OUTPUT"
fi

- name: Create pull request
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Use RELEASE_TOKEN so PR creation triggers required CI checks.
GH_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
DIFF_SUMMARY: ${{ needs.detect.outputs.diff_summary }}
COVERAGE_REPORT: ${{ needs.detect.outputs.coverage_report }}
PREFIX: ${{ steps.commit_type.outputs.prefix }}
run: |
DATE=$(date +%Y-%m-%d)

Expand All @@ -323,7 +377,7 @@ jobs:
> /tmp/pr-body.md

gh pr create \
--title "feat: sync with upstream API changes ${DATE}" \
--title "${PREFIX}: sync with upstream API changes ${DATE}" \
--body-file /tmp/pr-body.md \
--label "api-sync,automated" \
--base main
Expand Down
Loading