Skip to content

Commit 1ca83d5

Browse files
KeyCode17claude
andcommitted
ci(release): rate-limit aware publish loop
crates.io caps new-crate names at ~1 per 10 minutes (burst of 5). The prior loop blasted them at ~15s intervals and the v1.2.0 run hit 429 after publishing 5 of 16 crates. Replace the three layered batches with one combined loop that: 1. Queries the crates.io API for each name+version before uploading and skips if already present (idempotent recovery). 2. Sleeps 615s after every successful new-crate upload. Dep ordering is preserved by the loop ordering (layer 0 → 1 → 2). Job timeout-minutes raised to 240 because 16 fresh names × ~10min plus verify time runs close to 3h. Re-run via workflow_dispatch (force_publish=true) after merge to drain the 11 remaining unpublished crates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b90ffe5 commit 1ca83d5

1 file changed

Lines changed: 37 additions & 29 deletions

File tree

.github/workflows/release.yml

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -195,54 +195,62 @@ jobs:
195195
toolchain: "1.95"
196196
- uses: Swatinem/rust-cache@v2
197197

198-
- name: Publish layer 0 (no internal deps)
198+
# crates.io rate-limits NEW crate names at ~1 per 10 minutes (a
199+
# short burst of 5 is allowed). When publishing 16 fresh names in a
200+
# single run we therefore have to:
201+
# 1. Skip names that are already at this version (idempotent re-run).
202+
# 2. Sleep 600s between every NEW upload to stay under the cap.
203+
# The two layered loops below preserve dep ordering, but the inner
204+
# loop is now a per-crate rate-aware step.
205+
- name: Publish all 16 lib crates (rate-limit aware, idempotent)
199206
env:
200207
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
208+
PUBLISH_SLEEP: "615" # 10 min + buffer; new-crate cap is 1/10min
201209
shell: bash
210+
timeout-minutes: 240
202211
run: |
203212
set -euo pipefail
204-
for c in pxsolver-core pxsolver-types pxsolver-errors; do
205-
echo "::group::cargo publish -p ${c}"
206-
cargo publish -p "${c}"
213+
VERSION="${{ needs.guard.outputs.version }}"
214+
215+
publish_one() {
216+
local crate="$1"
217+
# crates.io API returns 200 for known version, 404 if not.
218+
local code
219+
code=$(curl -sS -o /dev/null -w '%{http_code}' \
220+
"https://crates.io/api/v1/crates/${crate}/${VERSION}" || echo "000")
221+
if [[ "${code}" == "200" ]]; then
222+
echo "::notice::${crate} ${VERSION} already on crates.io — skipping"
223+
return 0
224+
fi
225+
echo "::group::cargo publish -p ${crate}"
226+
cargo publish -p "${crate}"
207227
echo "::endgroup::"
208-
done
228+
echo "Sleeping ${PUBLISH_SLEEP}s to stay under new-crate rate limit"
229+
sleep "${PUBLISH_SLEEP}"
230+
}
209231
210-
- name: Wait for crates.io index (layer 0)
211-
run: sleep 45
232+
# Layer 0 (no internal deps)
233+
for c in pxsolver-core pxsolver-types pxsolver-errors; do
234+
publish_one "${c}"
235+
done
212236
213-
- name: Publish layer 1 (deps on layer 0 only)
214-
env:
215-
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
216-
shell: bash
217-
run: |
218-
set -euo pipefail
237+
# Layer 1 (depends on layer 0)
219238
for c in pxsolver-validation pxsolver-cache pxsolver-detector \
220239
pxsolver-harvester pxsolver-pipeline pxsolver-native \
221240
pxsolver-auth; do
222-
echo "::group::cargo publish -p ${c}"
223-
cargo publish -p "${c}"
224-
echo "::endgroup::"
241+
publish_one "${c}"
225242
done
226243
227-
- name: Wait for crates.io index (layer 1)
228-
run: sleep 45
229-
230-
- name: Publish layer 2 (deps on layer 0 + 1)
231-
env:
232-
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
233-
shell: bash
234-
run: |
235-
set -euo pipefail
244+
# Layer 2 (depends on layer 0 + 1)
236245
for c in pxsolver-perimeterx pxsolver-cloudflare pxsolver-turnstile \
237246
pxsolver-captcha pxsolver-datadome pxsolver-camoufox; do
238-
echo "::group::cargo publish -p ${c}"
239-
cargo publish -p "${c}"
240-
echo "::endgroup::"
247+
publish_one "${c}"
241248
done
242249
243250
- name: Summary
244251
run: |
245-
echo "Published 16 pxsolver-* crates at v${{ needs.guard.outputs.version }} (${{ needs.guard.outputs.bump_kind }} bump)"
252+
echo "Publish loop complete for v${{ needs.guard.outputs.version }} (${{ needs.guard.outputs.bump_kind }} bump)."
253+
echo "Any crates already at this version were skipped idempotently."
246254
247255
skipped-notice:
248256
name: crates.io publish skipped

0 commit comments

Comments
 (0)