Skip to content

Retry the release attach, and stop signatures outliving their files - #18

Merged
livingstaccato merged 1 commit into
mainfrom
fix/release-attach-retry
Sep 4, 2026
Merged

Retry the release attach, and stop signatures outliving their files#18
livingstaccato merged 1 commit into
mainfrom
fix/release-attach-retry

Conversation

@livingstaccato

Copy link
Copy Markdown
Member

v0.5.0 shipped to PyPI and then lost half its release page to a single transient error:

HTTP 502: Server Error (https://api.github.com/repos/.../releases/assets/545028939)

gh release upload sends each file in turn and gives up on the first rejection, with part of the set already attached. The release kept the sdist and every .sigstore.json, while losing the wheel and the SBOM — so it carried signatures for two files it did not have.

That is worse than an empty release. A .sigstore.json beside no artifact reads as something published and verified that is merely hard to find.

What changes

The attach is retried with backoff. Uploading is idempotent under --clobber, so the whole set is resent rather than reconstructed piecemeal. Uploading each file in its own command would still leave a partial set when the retries ran out; this way the release either gets everything, or the job fails with nothing half-done that a reader would mistake for complete.

The repair path gains the same retry, and drops an orphaned SBOM signature. Repair deliberately does not rebuild an SBOM — today's dependency closure is not the release's, and an SBOM confidently wrong about what shipped is the failure this ecosystem has already had once. That reasoning is unchanged and left in place. But it means a partially-failed run leaves a signature with nothing to sign, and absent is the honest state. A release holding both is left alone.

sign-and-upload now checks out, for the attach script only, with persist-credentials: false. It was deliberately checkout-free so nothing fetched from a registry runs beside the signing identity; a script from this repository under a credential-less checkout does not reintroduce that, and the comment says so.

Both scripts live in scripts/, not inline in the YAML.

Not fixed here

  • v0.5.0's release page still carries the orphaned sbom-python.cdx.json.sigstore.json. The repair path restored the wheel — verified byte-identical to PyPI by sha256 — but cannot restore the SBOM. Re-running repair once this merges will drop the stray signature.
  • This release.yml is duplicated across the provide-io Python repositories. The same single-shot attach is in every one of them, so every one can lose part of a release to one 502. This PR fixes the repository where it actually happened; the others want the same change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Dhi1SLuFiEseRhH9iLMv8Q

`gh release upload` sends each file in turn and gives up on the first
rejection with part of the set already attached. A single HTTP 502 from
api.github.com did exactly that to v0.5.0:

    HTTP 502: Server Error (.../releases/assets/545028939)

PyPI had the package, and the release page kept the sdist and every
`.sigstore.json` while losing the wheel and the SBOM -- so it carried
signatures for two files it did not have. A signature beside no artifact
is worse than an empty release: it reads as something published and
verified that is merely hard to find.

The attach is retried with backoff now. Uploading is idempotent under
`--clobber`, so the whole set is simply sent again rather than
reconstructed, and the job fails with nothing half-done that a reader
would mistake for complete.

The repair path gains the same retry, and drops an SBOM signature whose
SBOM is gone. Repair deliberately does not rebuild one -- today's
dependency closure is not the release's -- so the signature has nothing
left to sign, and absent is the honest state. A release holding both is
left alone.

`sign-and-upload` now checks out, for the attach script only, with no
credentials persisted. It was deliberately checkout-free so that nothing
fetched from a registry runs beside the signing identity; a script from
the repository under a credential-less checkout does not reintroduce
that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dhi1SLuFiEseRhH9iLMv8Q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant