authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-08-24 12:42:33-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-24 12:42:33-07:00
log5f1147cad72197d7315a2fd073c30d7ffe1767f2
treeede54202e19b75a050d9c01016df2b853bcf7023
parentf035a0f4cdf4abaa6704673af7b5a4a321149ba2
signature Signed by PGP key B5690EEEBB952194

fix(web): detect outdated nightly servers (#8124)


2 files changed, 31 insertions(+), 8 deletions(-)

apps/web/src/versionSkew.test.ts+19
...@@ -50,6 +50,25 @@ describe("versionSkew", () => {...@@ -50,6 +50,25 @@ describe("versionSkew", () => {
50 expect(resolveVersionMismatch("0.0.34")).toBeNull();50 expect(resolveVersionMismatch("0.0.34")).toBeNull();
51 });51 });
5252
53 it.each(["0.0.34-nightly.20260823.1124", "0.0.34-nightly.20260824.1124"])(
54 "warns when nightly server %s is behind a nightly client on the same release",
55 (serverVersion) => {
56 branding.APP_VERSION = "0.0.34-nightly.20260824.1125";
57
58 expect(resolveVersionMismatch(serverVersion)).toEqual({
59 clientVersion: "0.0.34-nightly.20260824.1125",
60 serverVersion,
61 hint: MISMATCH_HINT,
62 });
63 },
64 );
65
66 it("does not warn when a nightly server is ahead on the same release", () => {
67 branding.APP_VERSION = "0.0.34-nightly.20260824.1125";
68
69 expect(resolveVersionMismatch("0.0.34-nightly.20260824.1126")).toBeNull();
70 });
71
53 it("treats a nightly server built past the client as ahead, not skew", () => {72 it("treats a nightly server built past the client as ahead, not skew", () => {
54 expect(resolveVersionMismatch("0.0.35-nightly.20260818.1124")).toBeNull();73 expect(resolveVersionMismatch("0.0.35-nightly.20260818.1124")).toBeNull();
55 });74 });
apps/web/src/versionSkew.ts+12-8
...@@ -33,13 +33,11 @@ function versionCore(version: string): string {...@@ -33,13 +33,11 @@ function versionCore(version: string): string {
33 * The skew a user can act on: the connected server runs an older T3 Code than33 * The skew a user can act on: the connected server runs an older T3 Code than
34 * this client, so the server is the side that needs updating.34 * this client, so the server is the side that needs updating.
35 *35 *
36 * Versions compare as semver on their core `major.minor.patch` only. Nightlies36 * Two nightly builds compare their full versions, including the date and run.
37 * are `<core>-nightly.<date>.<run>` builds of the release they precede, so a37 * Other combinations compare their core `major.minor.patch` only, so a stable
38 * stable client on a nightly server (or the reverse, at the same core) shares a38 * build and a nightly build with the same core do not cause an update warning.
39 * wire contract and is not skew. A server *ahead* of the client is not skew39 * A server ahead of the client does not need an update. Versions that do not
40 * either: the client is the stale side, and every consumer of this result tells40 * parse as semver fall back to plain string inequality.
41 * the user to update their server. Versions that do not parse as semver fall
42 * back to plain string inequality.
43 */41 */
44export function resolveVersionMismatch(42export function resolveVersionMismatch(
45 serverVersion: string | null | undefined,43 serverVersion: string | null | undefined,
...@@ -52,9 +50,15 @@ export function resolveVersionMismatch(...@@ -52,9 +50,15 @@ export function resolveVersionMismatch(
5250
53 const clientCore = versionCore(normalizedClientVersion);51 const clientCore = versionCore(normalizedClientVersion);
54 const serverCore = versionCore(normalizedServerVersion);52 const serverCore = versionCore(normalizedServerVersion);
53 const compareNightlyBuilds =
54 parseSemver(normalizedClientVersion)?.prerelease[0] === "nightly" &&
55 parseSemver(normalizedServerVersion)?.prerelease[0] === "nightly";
55 const serverIsBehind =56 const serverIsBehind =
56 parseSemver(clientCore) && parseSemver(serverCore)57 parseSemver(clientCore) && parseSemver(serverCore)
57 ? compareSemverVersions(serverCore, clientCore) < 058 ? compareSemverVersions(
59 compareNightlyBuilds ? normalizedServerVersion : serverCore,
60 compareNightlyBuilds ? normalizedClientVersion : clientCore,
61 ) < 0
58 : normalizedServerVersion !== normalizedClientVersion;62 : normalizedServerVersion !== normalizedClientVersion;
59 if (!serverIsBehind) {63 if (!serverIsBehind) {
60 return null;64 return null;