Skip to content

ci(publish): two releases never reached a registry, the retries added in #719 do not cover the steps that failed #1091

Description

@BryanFRD

v7.21.4 and v7.21.5 were tagged on 2026-09-17 and 2026-09-20 and neither reached a registry. npm and crates.io sat at 7.21.3 for three days. I re-ran both Publish runs by hand today and they succeeded unchanged, so nothing was wrong with the code being released.

This is the same failure shape as #718, and #719's fixes do not cover it.

What failed

Tag Run Step Error
v7.21.4 35270645888 Upload assets to draft release HTTP 500: Error saving asset (https://uploads.github.com/.../assets?...name=ferrflow-darwin-x64.tar.gz.sigstore.json)
v7.21.5 35503666674 Install cosign curl: (35) Recv failure: Connection reset by peer fetching cosign v3.1.3

Both are transient network noise, seconds long, on someone else's infrastructure.

Why #719 did not catch it

#719 added sign_with_retry, 3 attempts with backoff, to both signing steps. That is the step that failed in #718 and it held. These two failures landed on steps either side of it, and neither has a retry:

  • Install cosign is sigstore/cosign-installer, which downloads the binary itself. It runs before the retried signing step, so a reset connection there kills the job with the retry logic still untouched further down.
  • Upload assets to draft release is a bare loop, gh release upload "$TAG" "$file" --clobber, with no retry. It is already idempotent thanks to --clobber, so it is the cheapest possible thing to retry. v7.21.4 died on the 8th of 20 assets.

Install cosign for Docker signing in the docker job uses the same unretried action, so it has the same exposure.

Why the orphan sweep did not save it either

#719 also added "Publish any orphaned older draft releases", which is exactly the right idea. It sits inside the upload job, after the steps above. When that job dies, the sweep never runs, so it can only rescue an orphan on a later release whose own upload job survives every fragile step first.

Two consecutive releases failed in that one job, so the sweep never fired for either. It recovers from one bad run followed by a good one; it cannot recover from a run of bad luck, which is what happened.

Why nobody noticed for three days

Every publish job is needs: upload, so when that job fails, crates.io, npm, @ferrflow/wasm, @ferrflow/doc and Docker are all reported as skipped rather than failed. The run goes red on a tag, which blocks no pull request and appears on no dashboard anyone was watching. The releases looked cut: tags existed, GitHub releases existed as drafts, CHANGELOG.md was updated.

It surfaced only because ferrflow.com was still serving 7.21.3 docs, which is a long way downstream from the cause.

What would fix it

  1. Retry the cosign install, in both upload and docker. The installer action takes no retry input, so either wrap the step or install the binary with the same 3-attempt loop sign_with_retry already uses.
  2. Retry gh release upload per file. --clobber makes it safe to repeat, and a partial upload is exactly the state a retry should recover from.
  3. Move the orphan sweep somewhere that runs when upload fails. A separate job with if: always(), or a scheduled sweep, so recovery does not depend on the job that just died. This is the one that turns a bad minute into a self-healing blip instead of a three-day outage.
  4. Say something when Publish fails on a tag. A red run on a tag is currently indistinguishable from silence. Even a job that fails loudly with the list of skipped registries would have shortened this from three days to one.

Items 1 and 2 stop these two specific failures. Item 3 is what stops the next one nobody predicted, and item 4 is what stops it being measured in days.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions