authorgravatar for saiansh2525@gmail.comYash Singh <saiansh2525@gmail.com> 2026-09-15 23:42:00-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-15 23:42:00-05:00
log53612cc040ad828566f958a275e3d3bad01c5033
tree8bbb2643794aebbbe1908940efd004721eb54724
parent3060cc461458e66842dfb656d979c4acca4dfb20
signature Signed by PGP key B5690EEEBB952194

fix(server): detect unsupported legacy Android command-line tools (#12017)


2 files changed, 64 insertions(+), 7 deletions(-)

apps/server/src/device/LocalDeviceHost.test.ts+53-4
...@@ -14,17 +14,21 @@ import * as NetService from "@t3tools/shared/Net";...@@ -14,17 +14,21 @@ import * as NetService from "@t3tools/shared/Net";
14import * as ServerConfig from "../config.ts";14import * as ServerConfig from "../config.ts";
15import * as ProcessRunner from "../processRunner.ts";15import * as ProcessRunner from "../processRunner.ts";
1616
17const diagnose = (files: ReadonlyArray<string>, environment: NodeJS.ProcessEnv) =>17const diagnose = (
18 files: ReadonlyArray<string>,
19 environment: NodeJS.ProcessEnv,
20 platform: NodeJS.Platform = "darwin",
21) =>
18 LocalDeviceHost.__testing.platformReason("android").pipe(22 LocalDeviceHost.__testing.platformReason("android").pipe(
19 Effect.provideService(HostProcessEnvironment, environment),23 Effect.provideService(HostProcessEnvironment, environment),
20 Effect.provideService(HostProcessPlatform, "darwin"),24 Effect.provideService(HostProcessPlatform, platform),
21 Effect.provideService(25 Effect.provideService(
22 FileSystem.FileSystem,26 FileSystem.FileSystem,
23 FileSystem.makeNoop({27 FileSystem.makeNoop({
24 exists: (file) => Effect.succeed(files.includes(file)),28 exists: (file) => Effect.succeed(files.includes(file)),
25 }),29 }),
26 ),30 ),
27 Effect.provide(NodePath.layer),31 Effect.provide(platform === "win32" ? NodePath.layerWin32 : NodePath.layerPosix),
28 );32 );
2933
30describe("Android SDK availability", () => {34describe("Android SDK availability", () => {
...@@ -40,7 +44,52 @@ describe("Android SDK availability", () => {...@@ -40,7 +44,52 @@ describe("Android SDK availability", () => {
40 const reason = yield* diagnose(["/sdk/platform-tools/adb", "/sdk/emulator/emulator"], {44 const reason = yield* diagnose(["/sdk/platform-tools/adb", "/sdk/emulator/emulator"], {
41 ANDROID_HOME: "/sdk",45 ANDROID_HOME: "/sdk",
42 });46 });
43 expect(reason).toContain("Command-line Tools (latest)");47 expect(reason).toContain("Command-line Tools (latest) are missing");
48 }),
49 );
50
51 it.effect("explains how to upgrade legacy command-line tools in the standard macOS SDK", () =>
52 Effect.gen(function* () {
53 const root = "/test/home/Library/Android/sdk";
54 const reason = yield* diagnose(
55 [`${root}/platform-tools/adb`, `${root}/emulator/emulator`, `${root}/tools/bin/avdmanager`],
56 { HOME: "/test/home" },
57 );
58 expect(reason).toContain("older, unsupported version");
59 expect(reason).toContain(root);
60 expect(reason).toContain(
61 "Install Android SDK Command-line Tools (latest) in Android Studio's SDK Manager under SDK Tools.",
62 );
63 }),
64 );
65
66 it.effect("recognizes legacy command-line tools on Windows", () =>
67 Effect.gen(function* () {
68 const reason = yield* diagnose(
69 [
70 "C:\\Android\\Sdk\\platform-tools\\adb.exe",
71 "C:\\Android\\Sdk\\emulator\\emulator.exe",
72 "C:\\Android\\Sdk\\tools\\bin\\avdmanager.bat",
73 ],
74 { ANDROID_HOME: "C:\\Android\\Sdk" },
75 "win32",
76 );
77 expect(reason).toContain("older, unsupported version");
78 }),
79 );
80
81 it.effect("accepts the latest command-line tools when legacy tools are also installed", () =>
82 Effect.gen(function* () {
83 const reason = yield* diagnose(
84 [
85 "/sdk/platform-tools/adb",
86 "/sdk/emulator/emulator",
87 "/sdk/tools/bin/avdmanager",
88 "/sdk/cmdline-tools/latest/bin/avdmanager",
89 ],
90 { ANDROID_HOME: "/sdk" },
91 );
92 expect(reason).toBeNull();
44 }),93 }),
45 );94 );
4695
apps/server/src/device/LocalDeviceHost.ts+11-3
...@@ -104,8 +104,11 @@ const platformReason = Effect.fn("LocalDeviceHost.platformReason")(function* (...@@ -104,8 +104,11 @@ const platformReason = Effect.fn("LocalDeviceHost.platformReason")(function* (
104 return `Android SDK Platform-Tools are missing from ${sdk.root}. Install them in Android Studio's SDK Manager.`;104 return `Android SDK Platform-Tools are missing from ${sdk.root}. Install them in Android Studio's SDK Manager.`;
105 if (!sdk.emulator)105 if (!sdk.emulator)
106 return `Android Emulator is missing from ${sdk.root}. Install it in Android Studio's SDK Manager.`;106 return `Android Emulator is missing from ${sdk.root}. Install it in Android Studio's SDK Manager.`;
107 if (!sdk.avdmanager)107 if (!sdk.avdmanager) {
108 if (sdk.legacyAvdmanager)
109 return `The Android SDK command-line tools in ${sdk.root} appear to be an older, unsupported version. Install Android SDK Command-line Tools (latest) in Android Studio's SDK Manager under SDK Tools.`;
108 return `Android SDK Command-line Tools (latest) are missing from ${sdk.root}. Install them in Android Studio's SDK Manager.`;110 return `Android SDK Command-line Tools (latest) are missing from ${sdk.root}. Install them in Android Studio's SDK Manager.`;
111 }
109 return null;112 return null;
110});113});
111114
...@@ -155,10 +158,15 @@ const androidSdk = Effect.gen(function* () {...@@ -155,10 +158,15 @@ const androidSdk = Effect.gen(function* () {
155 platform === "win32" ? "avdmanager.bat" : "avdmanager",158 platform === "win32" ? "avdmanager.bat" : "avdmanager",
156 ),159 ),
157 );160 );
158 return { root, adb, emulator, avdmanager };161 const legacyAvdmanager =
162 !avdmanager &&
163 (yield* exists(
164 path.join(root, "tools", "bin", platform === "win32" ? "avdmanager.bat" : "avdmanager"),
165 ));
166 return { root, adb, emulator, avdmanager, legacyAvdmanager };
159 }167 }
160 }168 }
161 return { root: null, adb: false, emulator: false, avdmanager: false };169 return { root: null, adb: false, emulator: false, avdmanager: false, legacyAvdmanager: false };
162});170});
163171
164const deviceHostEnvironment = (172const deviceHostEnvironment = (