fix(scripts): retry release asset upload on transient 404 #37

Open
ses wants to merge 1 commit from feat/upload-assets-retry into main
Owner

Summary

scripts/publish/upload-assets.sh now retries each asset upload up to three times with exponential backoff (5s, 10s, 15s). Tolerates the Forgejo tag→release index lag that breaks fresh releases on this host.

Why

  • Symptom: v0.5.2 release run 2401 failed at publish:upload-assets with HTTP 404 on /releases/tags/v0.5.2, 73 seconds after create_release. The same failure mode hit v0.5.1's run 72.
  • Root cause: on this host (Forgejo v16.0.3 + gitea-1.22.0) the tag→release lookup endpoint returns 404 for several minutes after a release is created, even though the release and the tag both exist. The asset upload script internally calls getRelease({tag}) to resolve the release ID; when that 404s, the upload fails.
  • Empirical timing (v0.5.2): release created at 22:43:52Z, index-lag 404 at 22:45:05Z (+73s), manual upload first-try success at 22:49:20Z (+324s). Lag settles within ~5 minutes.
  • Policy: the script owns retry behaviour for transient host-side errors. Recovery should not require a maintainer to manually upload assets.
  • Alternative rejected: wait a fixed delay before the upload step in publish.yaml. Less flexible, adds latency to every release even when the index is warm.
  • Out of scope: clear-assets.sh still silently swallows 404s (post-mortem follow-up #2); prepare-assets.sh still doesn't clean dist/ (follow-up #4). Both deferred to separate PRs.

Verification

Scenario Expected Result
bun run lint on the branch clean biome check, 122 files, 0 fixes
bun run typecheck on the branch clean tsc --noEmit exit 0
bun test on the branch 621 pass / 0 fail 621 pass, 1306 expect() calls
Shell syntax of upload-assets.sh parses with set -euo pipefail readable, function-scoped vars, exit codes preserved
upload-assets.sh against v0.5.2 release id 560 both assets present already verified tonight via the manual upload path; script body unchanged on the happy path

Out of scope

  • clear-assets.sh fail-loudly on 404 — post-mortem follow-up #2.
  • prepare-assets.sh clean dist/ before pack — post-mortem follow-up #4.
  • Retightening the Authorized Integration's event_name/ref claim rules — admin-UI change; the loosened rules were needed for workflow_dispatch, which the trigger no longer accepts after PR #35.
  • Permissions-matrix docs in AGENTS.md — separate PR if requested.

Follow-ups after merge

  1. v0.5.3 release to validate end-to-end. Expect: workflow runs release: published → derive-tag uses FORGEJO_REF_NAME fallback → check-pkg idempotent → publish:npm succeeds → upload-assets retries through the index lag and succeeds on attempt 2 or 3, no manual recovery needed.
## Summary `scripts/publish/upload-assets.sh` now retries each asset upload up to three times with exponential backoff (5s, 10s, 15s). Tolerates the Forgejo tag→release index lag that breaks fresh releases on this host. ## Why - **Symptom**: v0.5.2 release run 2401 failed at `publish:upload-assets` with HTTP 404 on `/releases/tags/v0.5.2`, 73 seconds after `create_release`. The same failure mode hit v0.5.1's run 72. - **Root cause**: on this host (Forgejo v16.0.3 + gitea-1.22.0) the tag→release lookup endpoint returns 404 for several minutes after a release is created, even though the release and the tag both exist. The asset upload script internally calls `getRelease({tag})` to resolve the release ID; when that 404s, the upload fails. - **Empirical timing** (v0.5.2): release created at 22:43:52Z, index-lag 404 at 22:45:05Z (+73s), manual upload first-try success at 22:49:20Z (+324s). Lag settles within ~5 minutes. - **Policy**: the script owns retry behaviour for transient host-side errors. Recovery should not require a maintainer to manually upload assets. - **Alternative rejected**: wait a fixed delay before the upload step in `publish.yaml`. Less flexible, adds latency to every release even when the index is warm. - **Out of scope**: `clear-assets.sh` still silently swallows 404s (post-mortem follow-up #2); `prepare-assets.sh` still doesn't clean `dist/` (follow-up #4). Both deferred to separate PRs. ## Verification | Scenario | Expected | Result | |---|---|---| | `bun run lint` on the branch | clean | ✅ biome check, 122 files, 0 fixes | | `bun run typecheck` on the branch | clean | ✅ `tsc --noEmit` exit 0 | | `bun test` on the branch | 621 pass / 0 fail | ✅ 621 pass, 1306 expect() calls | | Shell syntax of `upload-assets.sh` | parses with `set -euo pipefail` | ✅ readable, function-scoped vars, exit codes preserved | | `upload-assets.sh` against v0.5.2 release id 560 | both assets present | ✅ already verified tonight via the manual upload path; script body unchanged on the happy path | ## Out of scope - `clear-assets.sh` fail-loudly on 404 — post-mortem follow-up #2. - `prepare-assets.sh` clean `dist/` before pack — post-mortem follow-up #4. - Retightening the Authorized Integration's `event_name`/`ref` claim rules — admin-UI change; the loosened rules were needed for `workflow_dispatch`, which the trigger no longer accepts after PR #35. - Permissions-matrix docs in AGENTS.md — separate PR if requested. ## Follow-ups after merge 1. v0.5.3 release to validate end-to-end. Expect: workflow runs `release: published` → derive-tag uses `FORGEJO_REF_NAME` fallback → check-pkg idempotent → publish:npm succeeds → **upload-assets retries through the index lag and succeeds on attempt 2 or 3**, no manual recovery needed.
fix(scripts): retry release asset upload on transient 404
All checks were successful
ci / test (pull_request) Successful in 1m20s
ba57d4efeb
All checks were successful
ci / test (pull_request) Successful in 1m20s
Required
Details
This pull request is blocked because it's outdated.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/upload-assets-retry:feat/upload-assets-retry
git switch feat/upload-assets-retry
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
sebatec-eu/forgejoctl!37
No description provided.