History

Respect installed version update channel

- Derive desktop updater channel from the running app version - Keep stable builds on latest and prereleases on their own track - Add tests and release docs for channel matching

Julius Marminge committed 7 months ago

Commit b3291cf1 parent c9b07d64 files · +91 −5Browse files

Unified · Split

4 files
  1. apps/desktop/src/main.ts +22 −5
  2. apps/desktop/src/updateChannel.test.ts +40 −0
  3. apps/desktop/src/updateChannel.ts +26 −0
  4. docs/release.md +3 −0
apps/desktop/src/main.ts+22−5
@@ -54,6 +54,11 @@ import { isBackendReadinessAborted, waitForHttpReady } from "./backendReadiness" · 53 unmodified lines
5454 import { showDesktopConfirmDialog } from "./confirmDialog";
5555 import { resolveDesktopServerExposure } from "./serverExposure";
5656 import { syncShellEnvironment } from "./syncShellEnvironment";
57+import {
58+ resolveDesktopUpdateChannel,
59+ shouldAcceptDesktopUpdateForCurrentVersion,
60+ shouldAllowDesktopPrereleaseUpdates,
61+} from "./updateChannel";
5762 import { getAutoUpdateDisabledReason, shouldBroadcastDownloadProgress } from "./updateState";
5863 import { ServerListeningDetector } from "./serverListeningDetector";
5964 import {
@@ -116,8 +121,6 @@ const APP_RUN_ID = Crypto.randomBytes(6).toString("hex"); · 56 unmodified lines
116121 const SERVER_SETTINGS_PATH = Path.join(STATE_DIR, "settings.json");
117122 const AUTO_UPDATE_STARTUP_DELAY_MS = 15_000;
118123 const AUTO_UPDATE_POLL_INTERVAL_MS = 4 * 60 * 60 * 1000;
119−const DESKTOP_UPDATE_CHANNEL = "latest";
120−const DESKTOP_UPDATE_ALLOW_PRERELEASE = false;
121124 const DESKTOP_LOOPBACK_HOST = "127.0.0.1";
122125 const DESKTOP_REQUIRED_PORT_PROBE_HOSTS = ["0.0.0.0", "::"] as const;
123126 const TITLEBAR_HEIGHT = 40;
@@ -1168,6 +1171,9 @@ async function installDownloadedUpdate(): Promise<{ accepted: boolean; completed · 1044 unmodified lines
11681171 }
11691172
11701173 function configureAutoUpdater(): void {
1174+ const currentVersion = app.getVersion();
1175+ const updateChannel = resolveDesktopUpdateChannel(currentVersion);
1176+ const allowPrerelease = shouldAllowDesktopPrereleaseUpdates(currentVersion);
11711177 const githubToken =
11721178 process.env.T3CODE_DESKTOP_UPDATE_GITHUB_TOKEN?.trim() || process.env.GH_TOKEN?.trim() || "";
11731179 if (githubToken) {
@@ -1205,12 +1211,14 @@ function configureAutoUpdater(): void { · 31 unmodified lines
12051211
12061212 autoUpdater.autoDownload = false;
12071213 autoUpdater.autoInstallOnAppQuit = false;
1208− // Keep alpha branding, but force all installs onto the stable update track.
1209− autoUpdater.channel = DESKTOP_UPDATE_CHANNEL;
1210− autoUpdater.allowPrerelease = DESKTOP_UPDATE_ALLOW_PRERELEASE;
1214+ autoUpdater.channel = updateChannel;
1215+ autoUpdater.allowPrerelease = allowPrerelease;
12111216 autoUpdater.allowDowngrade = false;
12121217 autoUpdater.disableDifferentialDownload = isArm64HostRunningIntelBuild(desktopRuntimeInfo);
12131218 let lastLoggedDownloadMilestone = -1;
1219+ console.info(
1220+ `[desktop-updater] Configured update channel=${updateChannel} currentVersion=${currentVersion} allowPrerelease=${allowPrerelease}.`,
1221+ );
12141222
12151223 if (isArm64HostRunningIntelBuild(desktopRuntimeInfo)) {
12161224 console.info(
@@ -1222,6 +1230,15 @@ function configureAutoUpdater(): void { · 5 unmodified lines
12221230 console.info("[desktop-updater] Looking for updates...");
12231231 });
12241232 autoUpdater.on("update-available", (info) => {
1233+ if (!shouldAcceptDesktopUpdateForCurrentVersion(currentVersion, info.version)) {
1234+ setUpdateState(reduceDesktopUpdateStateOnNoUpdate(updateState, new Date().toISOString()));
1235+ lastLoggedDownloadMilestone = -1;
1236+ console.info(
1237+ `[desktop-updater] Ignoring update ${info.version} on channel=${resolveDesktopUpdateChannel(info.version)} while currentVersion=${currentVersion} is on channel=${updateChannel}.`,
1238+ );
1239+ return;
1240+ }
1241+
12251242 setUpdateState(
12261243 reduceDesktopUpdateStateOnUpdateAvailable(
12271244 updateState,
apps/desktop/src/updateChannel.test.ts+40−0
@@ -0,0 +1,40 @@
1+import { describe, expect, it } from "vitest";
2+
3+import {
4+ resolveDesktopUpdateChannel,
5+ shouldAcceptDesktopUpdateForCurrentVersion,
6+ shouldAllowDesktopPrereleaseUpdates,
7+} from "./updateChannel";
8+
9+describe("updateChannel", () => {
10+ it("uses the stable latest channel for plain versions", () => {
11+ expect(resolveDesktopUpdateChannel("0.0.17")).toBe("latest");
12+ expect(shouldAllowDesktopPrereleaseUpdates("0.0.17")).toBe(false);
13+ });
14+
15+ it("derives the prerelease channel from the installed version", () => {
16+ expect(resolveDesktopUpdateChannel("0.0.18-nightly.20260415")).toBe("nightly");
17+ expect(shouldAllowDesktopPrereleaseUpdates("0.0.18-nightly.20260415")).toBe(true);
18+ });
19+
20+ it("accepts updates that stay on the same prerelease track", () => {
21+ expect(
22+ shouldAcceptDesktopUpdateForCurrentVersion(
23+ "0.0.18-nightly.20260415",
24+ "0.0.18-nightly.20260416",
25+ ),
26+ ).toBe(true);
27+ });
28+
29+ it("rejects stable releases while running a nightly build", () => {
30+ expect(shouldAcceptDesktopUpdateForCurrentVersion("0.0.18-nightly.20260415", "0.0.17")).toBe(
31+ false,
32+ );
33+ });
34+
35+ it("rejects updates from a different prerelease track", () => {
36+ expect(
37+ shouldAcceptDesktopUpdateForCurrentVersion("0.0.18-nightly.20260415", "0.0.18-beta.1"),
38+ ).toBe(false);
39+ });
40+});
apps/desktop/src/updateChannel.ts+26−0
@@ -0,0 +1,26 @@
1+const VERSION_PRERELEASE_CHANNEL_PATTERN =
2+ /^\d+\.\d+\.\d+(?:-([0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z.-]+)?$/;
3+
4+function resolvePrereleaseChannel(version: string): string | null {
5+ const normalizedVersion = version.trim();
6+ const match = VERSION_PRERELEASE_CHANNEL_PATTERN.exec(normalizedVersion);
7+ const channel = match?.[1]?.trim().toLowerCase();
8+ return channel && channel.length > 0 ? channel : null;
9+}
10+
11+export function resolveDesktopUpdateChannel(version: string): string {
12+ return resolvePrereleaseChannel(version) ?? "latest";
13+}
14+
15+export function shouldAllowDesktopPrereleaseUpdates(version: string): boolean {
16+ return resolvePrereleaseChannel(version) !== null;
17+}
18+
19+export function shouldAcceptDesktopUpdateForCurrentVersion(
20+ currentVersion: string,
21+ candidateVersion: string,
22+): boolean {
23+ return (
24+ resolveDesktopUpdateChannel(currentVersion) === resolveDesktopUpdateChannel(candidateVersion)
25+ );
26+}
docs/release.md+3−0
@@ -25,6 +25,9 @@ This document covers how to run desktop releases from one tag, first without sig · 24 unmodified lines
2525 - Background checks run on startup delay + interval.
2626 - No automatic download or install.
2727 - The desktop UI shows a rocket update button when an update is available; click once to download, click again after download to restart/install.
28+- Update channel selection:
29+ - Stable builds (`X.Y.Z`) only accept stable updates.
30+ - Prerelease builds (for example `X.Y.Z-nightly.N`) stay on that prerelease track and ignore stable or differently-tagged prerelease releases.
2831 - Provider: GitHub Releases (`provider: github`) configured at build time.
2932 - Repository slug source:
3033 - `T3CODE_DESKTOP_UPDATE_REPOSITORY` (format `owner/repo`), if set.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t