Skip to content

ci: draft release until assets upload, then publish atomically#282

Merged
quiet-node merged 1 commit into
mainfrom
ci/draft-release-atomic-publish
Jun 30, 2026
Merged

ci: draft release until assets upload, then publish atomically#282
quiet-node merged 1 commit into
mainfrom
ci/draft-release-atomic-publish

Conversation

@quiet-node

Copy link
Copy Markdown
Owner

Overview

Closes the window where curl -fsSL https://thuki.app/install.sh | sh returns 404 right after a release is cut.

The install script downloads from https://github.com/quiet-node/thuki/releases/latest/download/Thuki.dmg, which follows GitHub's "latest release" pointer. release-please publishes the GitHub Release the moment the release PR merges (a fast ubuntu job), but the DMG, its signature, and latest.json are built and uploaded minutes later by the macOS job. During that gap, releases/latest already points at the new tag while its assets do not exist yet, so the installer (and the in-app auto-updater, which reads latest.json the same way) 404s.

What changed

  • release-please-config.json: create the GitHub Release as a draft (draft: true) and create its git tag immediately (force-tag-creation: true).
  • .github/workflows/release-please.yml: add a final Publish release step that runs gh release edit "$TAG" --draft=false after the asset upload.

How it works

GitHub excludes draft (and prerelease) releases from releases/latest, the public API, and the latest/download asset redirect. So while the macOS job builds, the new release stays hidden and releases/latest keeps serving the previous good version. Only once every asset is uploaded does the publish step flip the draft to public, making go-live atomic: the release becomes "latest" and its DMG is already in place.

force-tag-creation exists because GitHub does not create a git tag for a draft release until it is published. Without it, an abandoned draft (a failed build that never publishes) would leave release-please unable to find the previous tag on the next run, producing a changelog spanning the entire history. Both options are supported by the release-please version bundled in the pinned action (v5.0.0 ships release-please ^17.6.0; the options landed in 17.2.0).

The publish step intentionally has no if: always(). A failed build or upload leaves an unpublished draft and releases/latest stays on the last good version; a re-run targets the existing draft.

The nightly-release.yml workflow is unaffected: it already publishes with --prerelease, which is likewise excluded from releases/latest.

Testing

  • release-please-config.json validated as JSON; release-please.yml validated as YAML.
  • draft and force-tag-creation confirmed present in the v17.6.0 config schema (the version bundled by the pinned release-please-action@v5.0.0).
  • End-to-end behavior is observable only on the next real release: the release should appear as a draft during the macOS build and flip to published once assets finish uploading, with releases/latest never pointing at an assetless release.

Note

This PR is typed ci: so it does not trigger a release-please version bump; there is no associated release to merge.

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
@quiet-node quiet-node merged commit 4c80cfa into main Jun 30, 2026
5 checks passed
@quiet-node quiet-node deleted the ci/draft-release-atomic-publish branch June 30, 2026 20:33
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