authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-03-07 04:05:36-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-07 04:05:36-08:00
logb74c7a79abbfbb7f6e8c5c4affb20784cea2b11c
tree39ad7783d0b9f0b789d3032937992f0cbc065294
parentf772720d8c2a90448154271d7e4623f40cdf83fc
signature Signed by PGP key B5690EEEBB952194

Handle prerelease tags in GitHub release publishing (#344)

* Handle prerelease tags when publishing GitHub releases - Derive `is_prerelease` and `make_latest` from the version format in preflight - Mark suffixed versions (for example `1.2.3-alpha.1`) as prereleases - Keep only plain `X.Y.Z` releases marked as latest - Document prerelease/latest behavior in `docs/release.md` * Force desktop auto-updates to stable latest channel - set Electron updater channel to `latest` - disable prerelease and downgrade updates regardless of app version

3 files changed, 19 insertions(+), 2 deletions(-)

.github/workflows/release.yml+11-1
...@@ -22,6 +22,8 @@ jobs:...@@ -22,6 +22,8 @@ jobs:
22 outputs:22 outputs:
23 version: ${{ steps.release_meta.outputs.version }}23 version: ${{ steps.release_meta.outputs.version }}
24 tag: ${{ steps.release_meta.outputs.tag }}24 tag: ${{ steps.release_meta.outputs.tag }}
25 is_prerelease: ${{ steps.release_meta.outputs.is_prerelease }}
26 make_latest: ${{ steps.release_meta.outputs.make_latest }}
25 ref: ${{ github.sha }}27 ref: ${{ github.sha }}
26 steps:28 steps:
27 - name: Checkout29 - name: Checkout
...@@ -45,6 +47,13 @@ jobs:...@@ -45,6 +47,13 @@ jobs:
4547
46 echo "version=$version" >> "$GITHUB_OUTPUT"48 echo "version=$version" >> "$GITHUB_OUTPUT"
47 echo "tag=v$version" >> "$GITHUB_OUTPUT"49 echo "tag=v$version" >> "$GITHUB_OUTPUT"
50 if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
51 echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
52 echo "make_latest=true" >> "$GITHUB_OUTPUT"
53 else
54 echo "is_prerelease=true" >> "$GITHUB_OUTPUT"
55 echo "make_latest=false" >> "$GITHUB_OUTPUT"
56 fi
4857
49 - name: Setup Bun58 - name: Setup Bun
50 uses: oven-sh/setup-bun@v259 uses: oven-sh/setup-bun@v2
...@@ -260,7 +269,8 @@ jobs:...@@ -260,7 +269,8 @@ jobs:
260 target_commitish: ${{ needs.preflight.outputs.ref }}269 target_commitish: ${{ needs.preflight.outputs.ref }}
261 name: T3 Code v${{ needs.preflight.outputs.version }}270 name: T3 Code v${{ needs.preflight.outputs.version }}
262 generate_release_notes: true271 generate_release_notes: true
263 make_latest: true272 prerelease: ${{ needs.preflight.outputs.is_prerelease }}
273 make_latest: ${{ needs.preflight.outputs.make_latest }}
264 files: |274 files: |
265 release-assets/*.dmg275 release-assets/*.dmg
266 release-assets/*.zip276 release-assets/*.zip
apps/desktop/src/main.ts+6-1
...@@ -58,6 +58,8 @@ const LOG_FILE_MAX_FILES = 10;...@@ -58,6 +58,8 @@ const LOG_FILE_MAX_FILES = 10;
58const APP_RUN_ID = Crypto.randomBytes(6).toString("hex");58const APP_RUN_ID = Crypto.randomBytes(6).toString("hex");
59const AUTO_UPDATE_STARTUP_DELAY_MS = 15_000;59const AUTO_UPDATE_STARTUP_DELAY_MS = 15_000;
60const AUTO_UPDATE_POLL_INTERVAL_MS = 4 * 60 * 60 * 1000;60const AUTO_UPDATE_POLL_INTERVAL_MS = 4 * 60 * 60 * 1000;
61const DESKTOP_UPDATE_CHANNEL = "latest";
62const DESKTOP_UPDATE_ALLOW_PRERELEASE = false;
6163
62type DesktopUpdateErrorContext = DesktopUpdateState["errorContext"];64type DesktopUpdateErrorContext = DesktopUpdateState["errorContext"];
6365
...@@ -725,7 +727,10 @@ function configureAutoUpdater(): void {...@@ -725,7 +727,10 @@ function configureAutoUpdater(): void {
725727
726 autoUpdater.autoDownload = false;728 autoUpdater.autoDownload = false;
727 autoUpdater.autoInstallOnAppQuit = false;729 autoUpdater.autoInstallOnAppQuit = false;
728 autoUpdater.allowPrerelease = app.getVersion().includes("-");730 // Keep alpha branding, but force all installs onto the stable update track.
731 autoUpdater.channel = DESKTOP_UPDATE_CHANNEL;
732 autoUpdater.allowPrerelease = DESKTOP_UPDATE_ALLOW_PRERELEASE;
733 autoUpdater.allowDowngrade = false;
729 let lastLoggedDownloadMilestone = -1;734 let lastLoggedDownloadMilestone = -1;
730735
731 autoUpdater.on("checking-for-update", () => {736 autoUpdater.on("checking-for-update", () => {
docs/release.md+2
...@@ -12,6 +12,8 @@ This document covers how to run desktop releases from one tag, first without sig...@@ -12,6 +12,8 @@ This document covers how to run desktop releases from one tag, first without sig
12 - Linux `x64` AppImage12 - Linux `x64` AppImage
13 - Windows `x64` NSIS installer13 - Windows `x64` NSIS installer
14- Publishes one GitHub Release with all produced files.14- Publishes one GitHub Release with all produced files.
15 - Versions with a suffix after `X.Y.Z` (for example `1.2.3-alpha.1`) are published as GitHub prereleases.
16 - Only plain `X.Y.Z` releases are marked as the repository's latest release.
15- Includes Electron auto-update metadata (for example `latest*.yml` and `*.blockmap`) in release assets.17- Includes Electron auto-update metadata (for example `latest*.yml` and `*.blockmap`) in release assets.
16- Publishes the CLI package (`apps/server`, npm package `t3`) with OIDC trusted publishing.18- Publishes the CLI package (`apps/server`, npm package `t3`) with OIDC trusted publishing.
17- Signing is optional and auto-detected per platform from secrets.19- Signing is optional and auto-detected per platform from secrets.