-
Notifications
You must be signed in to change notification settings - Fork 1
165 lines (140 loc) · 6.89 KB
/
Copy pathapi-sync.yml
File metadata and controls
165 lines (140 loc) · 6.89 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
name: API Sync
on:
repository_dispatch:
types: [api-sync]
permissions:
contents: write
pull-requests: write
id-token: write
jobs:
sync:
name: Run deterministic spec-sync patcher
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Fetch the newly delivered spec + manifest from api-sync-data
run: |
git fetch origin api-sync-data
git show origin/api-sync-data:.api-sync/spec-current.json > .api-sync/spec-current.json
git show origin/api-sync-data:.api-sync/delivery.json > /tmp/delivery.json
- name: Verify delivery manifest
env:
DISPATCH_SPEC_SHA256: ${{ github.event.client_payload.spec_sha256 }}
run: |
set -euo pipefail
if [ ! -s .api-sync/spec-current.json ]; then
echo "::error::spec-current.json is missing or empty on api-sync-data. Aborting."
exit 1
fi
if [ ! -s /tmp/delivery.json ]; then
echo "::error::delivery.json is missing or empty on api-sync-data. Aborting."
exit 1
fi
MANIFEST_SHA256=$(jq -r '.spec_sha256 // empty' /tmp/delivery.json)
MANIFEST_SOURCE_REPO=$(jq -r '.source_repository // empty' /tmp/delivery.json)
MANIFEST_SOURCE_SHA=$(jq -r '.source_sha // empty' /tmp/delivery.json)
MANIFEST_GENERATED_AT=$(jq -r '.generated_at // empty' /tmp/delivery.json)
if [ -z "$MANIFEST_SHA256" ] || [ -z "$MANIFEST_SOURCE_REPO" ] || [ -z "$MANIFEST_SOURCE_SHA" ] || [ -z "$MANIFEST_GENERATED_AT" ]; then
echo "::error::delivery.json is missing one or more required fields (source_repository, source_sha, spec_sha256, generated_at)."
exit 1
fi
if [ "$MANIFEST_SOURCE_REPO" != "blindpaylabs/blindpay-v2" ]; then
echo "::error::delivery.json source_repository is '$MANIFEST_SOURCE_REPO', expected 'blindpaylabs/blindpay-v2'."
exit 1
fi
ACTUAL_SHA256=$(sha256sum .api-sync/spec-current.json | cut -d' ' -f1)
if [ "$ACTUAL_SHA256" != "$MANIFEST_SHA256" ]; then
echo "::error::spec-current.json sha256 ($ACTUAL_SHA256) does not match delivery.json's spec_sha256 ($MANIFEST_SHA256). The delivered spec is corrupt or does not match its manifest."
exit 1
fi
if [ -n "${DISPATCH_SPEC_SHA256:-}" ] && [ "$DISPATCH_SPEC_SHA256" != "$MANIFEST_SHA256" ]; then
echo "::error::Stale delivery: the repository_dispatch payload's spec_sha256 ($DISPATCH_SPEC_SHA256) does not match the manifest currently on api-sync-data ($MANIFEST_SHA256). A newer delivery has landed since this dispatch fired -- re-trigger."
exit 1
fi
echo "Delivery manifest verified: source_sha=$MANIFEST_SOURCE_SHA generated_at=$MANIFEST_GENERATED_AT spec_sha256=$MANIFEST_SHA256"
- name: Create or reset the api-sync branch from main
run: |
git fetch origin api-sync 2>/dev/null || true
if git rev-parse --verify origin/api-sync >/dev/null 2>&1; then
git checkout api-sync
git reset --hard origin/main
else
git checkout -b api-sync
fi
- name: Run the patcher
id: patch
continue-on-error: true
run: python3 .api-sync/sync.py --apply --report /tmp/api-sync-report.json
- name: Show report
if: always()
run: |
if [ -f /tmp/api-sync-report.json ]; then
cat /tmp/api-sync-report.json
else
echo "(no report written -- the patcher failed before producing one, see the 'Run the patcher' step)"
fi
- name: Print coverage report (non-blocking)
if: always()
run: python3 .api-sync/sync.py --coverage || true
- name: Fail loudly if the patcher could not apply cleanly
if: steps.patch.outcome == 'failure'
run: |
echo "::error::api-sync patcher could not apply the new spec cleanly. This needs a human: see the 'Run the patcher' and 'Show report' step output above for the exact NEEDS_HUMAN reason(s)."
exit 1
- name: Determine whether there is anything to commit
id: bump
run: |
python3 - <<'PY' >> "$GITHUB_OUTPUT"
import json
with open("/tmp/api-sync-report.json") as f:
report = json.load(f)
applied = report.get("applied", [])
bump = report.get("bump")
prefix = {"minor": "feat", "patch": "fix"}.get(bump, "chore")
print(f"has_changes={'true' if applied else 'false'}")
print(f"bump={bump or ''}")
print(f"prefix={prefix}")
PY
- name: Commit and push
if: steps.bump.outputs.has_changes == 'true'
run: |
git remote set-url origin "https://x-access-token:${{ secrets.SDK_SYNC_PAT }}@github.com/${{ github.repository }}.git"
# Defense in depth: the patcher's own spec-map.json never points at
# .github/workflows, but never let the automated flow commit changes
# there even if something upstream of this step somehow produced one.
git checkout -- .github/workflows/ 2>/dev/null || true
git add -A
git reset HEAD .github/workflows/ 2>/dev/null || true
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -m "${{ steps.bump.outputs.prefix }}: sync SDK with API changes"
git push --force-with-lease origin api-sync
- name: Check for existing api-sync PR
if: steps.bump.outputs.has_changes == 'true'
id: check-pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=$(gh pr list --head api-sync --json number --jq '.[0].number // empty')
echo "existing_pr=$PR_NUMBER" >> "$GITHUB_OUTPUT"
- name: Create or update PR and enable auto-merge
if: steps.bump.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ secrets.SDK_SYNC_PAT }}
run: |
set -e
EXISTING_PR="${{ steps.check-pr.outputs.existing_pr }}"
TITLE="${{ steps.bump.outputs.prefix }}: sync SDK with API changes"
BODY="Automated, deterministic SDK sync (.api-sync/sync.py --apply). Bump: ${{ steps.bump.outputs.bump }}. See the workflow run's 'Show report' step for the exact applied changes."
if [ -n "$EXISTING_PR" ]; then
gh pr edit "$EXISTING_PR" --title "$TITLE" --body "$BODY"
PR_NUMBER="$EXISTING_PR"
else
gh pr create --title "$TITLE" --body "$BODY" --base main --head api-sync --label api-sync
PR_NUMBER=$(gh pr list --head api-sync --json number --jq '.[0].number')
fi
gh pr merge "$PR_NUMBER" --auto --squash