Skip to content

Only offer an update once the release has been promoted - #429

Merged
thcp merged 1 commit into
mainfrom
fix/only-offer-promoted-releases
Aug 24, 2026
Merged

Only offer an update once the release has been promoted#429
thcp merged 1 commit into
mainfrom
fix/only-offer-promoted-releases

Conversation

@thcp

@thcp thcp commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Two halves of the same bug, found while publishing v0.14.1.

The release flag was being clobbered

None of the three release workflows passed prerelease to softprops/action-gh-release. The action defaults it to false and writes it back, so each platform's upload step silently promoted the release it was attaching assets to.

v0.14.1  published 11:14  →  promoted 11:54
v0.14.0  published 20:30  →  promoted 21:15

Both were published as pre-releases and both ended up marked as the latest release within the hour. That also fires the released event, which docker-publish.yml handles by pushing :latest to GHCR, so a pre-release became the :latest image. Every release workflow now carries prerelease: ${{ github.event.release.prerelease }}.

The updater offered pre-releases

checkForUpdate took the newest non-draft release, pre-releases included. That was deliberate when written: /releases/latest excludes pre-releases, and since every StemDeck release was one, tracking stable would have meant nobody was ever notified.

With the flag now preserved, a pre-release is genuinely a pre-release, and offering it would push unverified builds to every user. The check now takes the newest release that is neither a draft nor a pre-release.

The release flow this establishes: publish as a pre-release, which triggers the builds, verify it, then promote it ("Set as the latest release"). Only at that point are users offered the update.

Testing

  • The e2e stub now returns an unpromoted pre-release ahead of the stable one, and the spec asserts the card names v9.9.9 rather than v9.9.10, so the filter is pinned rather than incidental.
  • Full Playwright suite: 25 passed locally.
  • All three workflow files re-parsed to confirm the expression lands in the right place. Linux's upload step is already guarded by if: github.event_name == 'release', so the release context is always present where the expression is evaluated.

Note

Only catalog.js consumes the releases API. The Rust side receives asset URLs from the frontend (AppUpdateQuery), so this filter is the single decision point and the two halves cannot disagree.

Two halves of the same bug.

The upload steps passed no `prerelease` to softprops/action-gh-release,
which defaults it to false and writes it back. Attaching assets therefore
promoted the release it was attaching them to. v0.13.0 and v0.14.0 were
both published as pre-releases and both ended up marked as the latest
release within the hour, which also fired `released` and pushed :latest
to GHCR. Each workflow now carries the release's own flag.

The updater took the newest non-draft release, pre-releases included.
That was deliberate when it was written, because /releases/latest hides
pre-releases and every release was one, so tracking stable meant nobody
would ever be notified. With the flag preserved, a pre-release is now
genuinely a pre-release, and offering it would push unverified builds to
everyone. It now takes the newest release that is neither draft nor
pre-release, so a release reaches users only once it has been promoted.

The e2e stub grew an unpromoted pre-release ahead of the stable one, and
the spec pins which of the two the card names.
@thcp
thcp merged commit b943711 into main Aug 24, 2026
10 checks passed
@thcp
thcp deleted the fix/only-offer-promoted-releases branch August 24, 2026 12:12
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