Skip to content

fix(release): tolerate a release repeated across an offset page boundary - #71

Merged
jan-kubica merged 1 commit into
mainfrom
fix/release-index-paging-artifact
Aug 8, 2026
Merged

fix(release): tolerate a release repeated across an offset page boundary#71
jan-kubica merged 1 commit into
mainfrom
fix/release-index-paging-artifact

Conversation

@jan-kubica

Copy link
Copy Markdown
Contributor

Problem

A publish run failed with:

##[error]GitHub returned duplicate releases for '@stll/folio-core@0.3.1'.

The tag named in the error belongs to a release from a year-old version that the run was not touching, and no duplicate release exists in the repository.

prepare creates the draft releases, then waitForStagedState re-reads repos/{repo}/releases?per_page=100 through gh api --paginate and hands the pages to indexReleases. That repository had just passed 100 releases, so the list spans two offset pages, and the tag in the error was the entry sitting on the page boundary.

A draft becoming visible between the page 1 and page 2 requests shifts every later entry down one position, and the boundary entry is returned on both pages. indexReleases saw one tag_name twice and failed the release.

The comment immediately above that call already anticipates the underlying condition:

GitHub's release list can lag a successful draft creation. Re-read with a bounded backoff so publishing still requires every exact draft asset.

The duplicate is a symptom of exactly that lag. waitForStagedState exists to retry through it, but fail() inside indexReleases runs first, so the retry never gets the chance.

The failure is a race, so it is intermittent, and it is only reachable once a repository has more than one page of releases.

Change

Two releases sharing a tag remains fatal: GitHub allows any number of drafts on one tag, and publishing an arbitrary one of them is not a choice this action may make.

Seeing the same release twice is a different fact, and the release id separates the two cases. A repeat of an id already indexed is now skipped; a second id under a tag already indexed still fails.

The opposite shift, an entry skipped rather than repeated, needs no handling: a missing draft leaves its entry pending, and the existing retry re-reads the list.

The existing test encoded the bug

rejects duplicate drafts for the same package tag built one draft object and passed it as two pages:

assert.throws(() => indexReleases([[draft], [draft]]), /duplicate releases/);

That is the paging artifact, not two drafts. Two distinct drafts have distinct ids. The test has been given the fixture its name describes, and a second test covers the boundary repeat.

Verification

find .github -type f -name '*.test.mjs' -print0 | sort -z | xargs -0 node --test — 48 pass, 0 fail.

Recovery on the affected repository confirmed the diagnosis: a plain re-run published all four packages and cleared the drafts, because the drafts already existed, no release was created, and the list therefore did not shift mid-read.

Follow-up not included

Offset pagination over a list this action concurrently appends to is unstable by construction; the id check makes the instability harmless rather than removing it. Worth revisiting separately, since consumers keep adding releases and the window only widens.

Distinguish the same release seen twice from two distinct releases sharing a tag, so a paginated re-read during draft creation no longer fails the publish.
@jan-kubica
jan-kubica merged commit 5628b97 into main Aug 8, 2026
3 checks passed
@jan-kubica
jan-kubica deleted the fix/release-index-paging-artifact branch August 8, 2026 06:28
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant