authorgravatar for 75276669+Gigioxx@users.noreply.github.comGuillermo Casanova <75276669+Gigioxx@users.noreply.github.com> 2026-09-15 19:21:25-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-15 15:21:25-07:00
logb84f63bb13f13b1d0ee8537c4d244d6f1efdb987
tree93f8ff9cb146d39b9973bfee36b23b76747ac40c
parentf0a0ead946ca3f3f310460a91b7dabb0e3fe4834
signature Signed by PGP key B5690EEEBB952194

fix(server): block updates under legacy service launchers (#11940)


2 files changed, 13 insertions(+), 5 deletions(-)

apps/server/src/cloud/servicePreflight.test.ts+10-3
......@@ -3,15 +3,22 @@ import { expect, it } from "@effect/vitest";
33import { runServicePreflight } from "./servicePreflight.ts";
44import { SERVICE_LAUNCHER_PROTOCOL } from "./serviceProtocol.ts";
55
6it("requires the database-snapshot launcher protocol", () => {
6it.each([1, 2])("blocks legacy launcher protocol %i", (launcherProtocol) => {
77 expect(
88 runServicePreflight({
99 databasePath: "/missing/state.sqlite",
10 launcherProtocol: SERVICE_LAUNCHER_PROTOCOL - 1,
10 launcherProtocol,
1111 version: "1.2.3",
1212 }),
13 ).toMatchObject({ status: "blocked", version: "1.2.3" });
13 ).toEqual({
14 status: "blocked",
15 version: "1.2.3",
16 reason:
17 "This release requires a newer T3 Code service launcher. Update it on the server machine.",
18 });
19});
1420
21it("accepts the current launcher protocol", () => {
1522 expect(
1623 runServicePreflight({
1724 databasePath: "/missing/state.sqlite",
apps/server/src/cloud/serviceProtocol.ts+3-2
......@@ -1,7 +1,8 @@
11import type { ServerSelfUpdateOutcome } from "@t3tools/contracts";
22
3/** Protocol 2 snapshots SQLite before trials so migrations can be rolled back safely. */
4export const SERVICE_LAUNCHER_PROTOCOL = 2 as const;
3// Protocol 3 requires the standalone executable layout. Bump when runtimePaths
4// or the installed runtime tree changes incompatibly; launchers survive self-updates.
5export const SERVICE_LAUNCHER_PROTOCOL = 3 as const;
56export const SERVICE_LAUNCHER_CONTEXT_ENV = "T3_SERVICE_LAUNCHER_CONTEXT";
67export const SERVICE_STATE_FILE = "service-state.json";
78/** Written by the launcher just before an explicit stop kills its child, so