From f5f26c5ac66f77ff24eca5ef32c47f0c754a03c5 Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Fri, 14 Nov 2025 11:14:14 -0800 Subject: [PATCH 1/4] fix: don't only rely on apt if on Linux --- npm_modules/cli/src/commands/doctor.ts | 42 ++++++++++++----- npm_modules/cli/src/setup/linuxSetup.ts | 61 +++++++++++++++++++++---- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/npm_modules/cli/src/commands/doctor.ts b/npm_modules/cli/src/commands/doctor.ts index ab37c1e7..2f15104e 100644 --- a/npm_modules/cli/src/commands/doctor.ts +++ b/npm_modules/cli/src/commands/doctor.ts @@ -45,6 +45,7 @@ import { BazelClient } from '../utils/BazelClient'; import { checkCommandExists, runCliCommand } from '../utils/cliUtils'; import { makeCommandHandler } from '../utils/errorUtils'; import { wrapInColor } from '../utils/logUtils'; +import { getLinuxPackageManager } from '../setup/linuxSetup'; /** Discord support link for troubleshooting */ const DISCORD_SUPPORT_URL = 'https://discord.gg/uJyNEeYX2U'; @@ -631,6 +632,20 @@ class ValdiDoctor { } } + private async getLinuxJavaInstallCommnad() { + const pm = await getLinuxPackageManager(); + + if (pm === 'apt') { + return 'sudo apt install openjdk-17-jdk'; + } else if (pm === 'yum') { + return 'sudo yum install java-17-openjdk-devel'; + } else if (pm === 'pacman') { + return 'sudo pacman -S jdk17-openjdk && sudo archlinux-java set jdk17-openjdk'; + } else { + return 'Please install Java JDK manually'; + } + } + /** * Validates Java JDK installation and configuration as set up by dev_setup. * @@ -683,7 +698,7 @@ class ValdiDoctor { message: `Java ${version} is outdated. Java 17+ is recommended`, details: 'dev_setup now installs Java 17 for better compatibility', fixable: true, - fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : 'sudo apt install openjdk-17-jdk', + fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : await this.getLinuxJavaInstallCommnad(), category: 'Java installation', }); } else { @@ -711,7 +726,7 @@ class ValdiDoctor { message: 'Java not found in PATH', details: 'dev_setup installs Java JDK for Android development', fixable: true, - fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : 'sudo apt install openjdk-17-jdk', + fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : await this.getLinuxJavaInstallCommnad(), category: 'Java installation', }); } @@ -1054,7 +1069,7 @@ class ValdiDoctor { }); } } else { - const fixCommand = this.getFixCommandForDependency(dep); + const fixCommand = await this.getFixCommandForDependency(dep); this.addResult({ name: `${dep} installation`, status: failureLevel, @@ -1163,19 +1178,22 @@ class ValdiDoctor { * @returns Platform-appropriate installation command or instruction * @private */ - private getFixCommandForDependency(dep: string): string { + private async getFixCommandForDependency(dep: string): Promise { + const pm = await getLinuxPackageManager(); + const pmInstall = (pm === 'pacman') ? 'sudo pacman -S' : `sudo ${pm} install`; + switch (dep) { case 'git': { - return os.platform() === 'darwin' ? 'brew install git' : 'sudo apt-get install git'; + return os.platform() === 'darwin' ? 'brew install git' : `${pmInstall} git`; } case 'npm': { return 'Install Node.js from https://nodejs.org (includes npm)'; } case 'watchman': { - return os.platform() === 'darwin' ? 'brew install watchman' : 'sudo apt-get install watchman'; + return os.platform() === 'darwin' ? 'brew install watchman' : `${pmInstall} watchman`; } case 'git-lfs': { - return os.platform() === 'darwin' ? 'brew install git-lfs' : 'sudo apt-get install git-lfs'; + return os.platform() === 'darwin' ? 'brew install git-lfs' : `${pmInstall} git-lfs`; } case 'bazelisk': { return os.platform() === 'darwin' ? 'brew install bazelisk' : 'valdi dev_setup'; @@ -1332,7 +1350,7 @@ class ValdiDoctor { * * @param argv - Resolved command line arguments * @returns Promise that resolves when the doctor command completes - * + * * @example * ```bash * valdi doctor --verbose --fix --json @@ -1360,13 +1378,13 @@ async function valdiDoctor(argv: ArgumentsResolver): Promise< /** * The command name as it appears in the CLI. - + */ export const command = 'doctor'; /** * Human-readable description of the command for help output. - + */ export const describe = 'Check your Valdi development environment for common issues'; @@ -1379,7 +1397,7 @@ export const describe = 'Check your Valdi development environment for common iss * - `--json` (-j): Output results in JSON format for automation * * @param yargs - The yargs instance to configure - + */ export const builder = (yargs: Argv): void => { yargs @@ -1417,6 +1435,6 @@ export const builder = (yargs: Argv): void => { /** * The command handler wrapped with error handling and logging. - + */ export const handler = makeCommandHandler(valdiDoctor); diff --git a/npm_modules/cli/src/setup/linuxSetup.ts b/npm_modules/cli/src/setup/linuxSetup.ts index 62b36e2d..f19ac056 100644 --- a/npm_modules/cli/src/setup/linuxSetup.ts +++ b/npm_modules/cli/src/setup/linuxSetup.ts @@ -6,20 +6,63 @@ import { ANDROID_LINUX_COMMANDLINE_TOOLS } from './versions'; const BAZELISK_URL = 'https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-amd64'; +export async function getLinuxPackageManager(): Promise { + let pm: string | null = null; + const distro = await fs.promises.readFile('/etc/os-release', 'utf8'); + const match = distro.match(/^ID="?([^"\n]*)"?$/m); + if (match && match[1]) { + switch (match[1]) { + case 'ubuntu': + pm = 'apt'; + break; + case 'debian': + pm = 'apt'; + break; + case 'fedora': + pm = 'dnf'; + break; + case 'centos': + pm = 'yum'; + break; + case 'arch': + pm = 'pacman'; + break; + } + } else { + if (checkCommandExists('pacman')) { + pm = 'pacman'; + } else if (checkCommandExists('dnf')) { + pm = 'dnf'; + } else if (checkCommandExists('yum')) { + pm = 'yum'; + } else if (checkCommandExists('apt')) { + pm = 'apt'; + } + } + + return pm; +} + export async function linuxSetup(): Promise { const devSetup = new DevSetupHelper(); + const pm = await getLinuxPackageManager(); - await devSetup.runShell('Installing dependencies from apt', [ - `sudo apt-get install zlib1g-dev git-lfs watchman libfontconfig-dev adb`, - ]); + if (pm === "apt") { + await devSetup.runShell('Installing dependencies from apt', [ + `sudo apt-get install zlib1g-dev git-lfs watchman libfontconfig-dev adb`, + ]); - await devSetup.runShell('Installing libtinfo5', [ - `wget http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb`, - `sudo apt install ./libtinfo5_6.3-2ubuntu0.1_amd64.deb`, - ]); + await devSetup.runShell('Installing libtinfo5', [ + `wget https://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb`, + `sudo apt install ./libtinfo5_6.3-2ubuntu0.1_amd64.deb`, + ]); - if (!checkCommandExists('java')) { - await devSetup.runShell('Installing Java Runtime Environment', ['sudo apt install default-jre']); + if (!checkCommandExists('java')) { + await devSetup.runShell('Installing Java Runtime Environment', ['sudo apt install default-jre']); + } + } else { + console.log('Unsupported package manager, please install the required dependencies manually:'); + console.log('zlib git-lfs watchman libfontconfig-dev adb libtinfo5 openjdk-17'); } const bazeliskPathSuffix = '.valdi/bin/bazelisk'; From 8710516f9a6534ae87c8051ea089b99705efba18 Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Fri, 14 Nov 2025 11:21:13 -0800 Subject: [PATCH 2/4] wrap error in red --- npm_modules/cli/src/setup/linuxSetup.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/npm_modules/cli/src/setup/linuxSetup.ts b/npm_modules/cli/src/setup/linuxSetup.ts index f19ac056..e2b67695 100644 --- a/npm_modules/cli/src/setup/linuxSetup.ts +++ b/npm_modules/cli/src/setup/linuxSetup.ts @@ -3,6 +3,8 @@ import path from 'path'; import { checkCommandExists } from '../utils/cliUtils'; import { DevSetupHelper, HOME_DIR } from './DevSetupHelper'; import { ANDROID_LINUX_COMMANDLINE_TOOLS } from './versions'; +import { wrapInColor } from '../utils/logUtils'; +import { ANSI_COLORS } from '../core/constants'; const BAZELISK_URL = 'https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-amd64'; @@ -61,8 +63,9 @@ export async function linuxSetup(): Promise { await devSetup.runShell('Installing Java Runtime Environment', ['sudo apt install default-jre']); } } else { - console.log('Unsupported package manager, please install the required dependencies manually:'); - console.log('zlib git-lfs watchman libfontconfig-dev adb libtinfo5 openjdk-17'); + console.log(wrapInColor('Unsupported package manager, please install the required dependencies manually:', ANSI_COLORS.RED_COLOR)); + console.log(wrapInColor('zlib git-lfs watchman libfontconfig-dev adb libtinfo5 openjdk-17', ANSI_COLORS.RED_COLOR)); + console.log() } const bazeliskPathSuffix = '.valdi/bin/bazelisk'; From 6810afb0a4d9d5e65f73a99ae36a4ff32952a42d Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Mon, 17 Nov 2025 09:34:42 -0800 Subject: [PATCH 3/4] review comments --- npm_modules/cli/src/commands/doctor.ts | 12 +++++----- npm_modules/cli/src/setup/linuxSetup.ts | 31 +++++++++++-------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/npm_modules/cli/src/commands/doctor.ts b/npm_modules/cli/src/commands/doctor.ts index 2f15104e..95cbd1e4 100644 --- a/npm_modules/cli/src/commands/doctor.ts +++ b/npm_modules/cli/src/commands/doctor.ts @@ -239,7 +239,7 @@ class ValdiDoctor { * - Output structured JSON for machine processing (--json flag) * - Display formatted, colored output for human consumption * - + * * @example * ```typescript @@ -632,7 +632,7 @@ class ValdiDoctor { } } - private async getLinuxJavaInstallCommnad() { + private async getLinuxJavaInstallCommand() { const pm = await getLinuxPackageManager(); if (pm === 'apt') { @@ -665,7 +665,7 @@ class ValdiDoctor { const { stdout, stderr } = await runCliCommand('java -version'); // java -version typically outputs to stderr, but check both const versionInfo = stderr || stdout || ''; - + // Try multiple version string formats: // 1. version "17.0.16" (older format) // 2. openjdk 17.0.16 (OpenJDK format) @@ -677,7 +677,7 @@ class ValdiDoctor { if (!versionMatch) { versionMatch = versionInfo.match(/java\s+(\d+\.\d+\.\d+)/i); } - + const version = versionMatch?.[1] ?? 'Unknown version'; // Check if Java version is 17 or higher @@ -698,7 +698,7 @@ class ValdiDoctor { message: `Java ${version} is outdated. Java 17+ is recommended`, details: 'dev_setup now installs Java 17 for better compatibility', fixable: true, - fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : await this.getLinuxJavaInstallCommnad(), + fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : await this.getLinuxJavaInstallCommand(), category: 'Java installation', }); } else { @@ -726,7 +726,7 @@ class ValdiDoctor { message: 'Java not found in PATH', details: 'dev_setup installs Java JDK for Android development', fixable: true, - fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : await this.getLinuxJavaInstallCommnad(), + fixCommand: os.platform() === 'darwin' ? 'brew install openjdk@17' : await this.getLinuxJavaInstallCommand(), category: 'Java installation', }); } diff --git a/npm_modules/cli/src/setup/linuxSetup.ts b/npm_modules/cli/src/setup/linuxSetup.ts index e2b67695..128fa1c9 100644 --- a/npm_modules/cli/src/setup/linuxSetup.ts +++ b/npm_modules/cli/src/setup/linuxSetup.ts @@ -12,24 +12,21 @@ export async function getLinuxPackageManager(): Promise { let pm: string | null = null; const distro = await fs.promises.readFile('/etc/os-release', 'utf8'); const match = distro.match(/^ID="?([^"\n]*)"?$/m); + + const distroToPackageManager: { [key: string]: string } = { + ubuntu: "apt", + fedora: "dnf", + debian: "apt", + arch: "pacman", + linuxmint: "apt", + opensuse: "zypper", + rhel: "dnf", + centos: "dnf", + manjaro: "pacman", + }; + if (match && match[1]) { - switch (match[1]) { - case 'ubuntu': - pm = 'apt'; - break; - case 'debian': - pm = 'apt'; - break; - case 'fedora': - pm = 'dnf'; - break; - case 'centos': - pm = 'yum'; - break; - case 'arch': - pm = 'pacman'; - break; - } + return distroToPackageManager[match[1]] || null; } else { if (checkCommandExists('pacman')) { pm = 'pacman'; From e12525fcb7fc80bce9fc9461cb6e0a3722c7fa9b Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Mon, 17 Nov 2025 09:41:05 -0800 Subject: [PATCH 4/4] pacman setup --- npm_modules/cli/src/setup/linuxSetup.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/npm_modules/cli/src/setup/linuxSetup.ts b/npm_modules/cli/src/setup/linuxSetup.ts index 128fa1c9..8cec4f71 100644 --- a/npm_modules/cli/src/setup/linuxSetup.ts +++ b/npm_modules/cli/src/setup/linuxSetup.ts @@ -5,6 +5,7 @@ import { DevSetupHelper, HOME_DIR } from './DevSetupHelper'; import { ANDROID_LINUX_COMMANDLINE_TOOLS } from './versions'; import { wrapInColor } from '../utils/logUtils'; import { ANSI_COLORS } from '../core/constants'; +import { execSync } from 'child_process'; const BAZELISK_URL = 'https://github.com/bazelbuild/bazelisk/releases/download/v1.26.0/bazelisk-linux-amd64'; @@ -59,6 +60,25 @@ export async function linuxSetup(): Promise { if (!checkCommandExists('java')) { await devSetup.runShell('Installing Java Runtime Environment', ['sudo apt install default-jre']); } + } else if (pm === "pacman") { + await devSetup.runShell('Installing dependencies from pacman', [ + `sudo pacman -S zlib git-lfs watchman libfontconfig-dev adb`, + ]); + + try { + execSync("pacman -Q ncurses5-compat-libs", { stdio: 'ignore' }); + } catch { + await devSetup.runShell('Installing libtinfo5', [ + `git clone https://aur.archlinux.org/ncurses5-compat-libs.git`, + `cd ncurses5-compat-libs`, + `makepkg -sic`, + `cd ..` + ]); + } + + if (!checkCommandExists('java')) { + await devSetup.runShell('Installing Java Runtime Environment', ['sudo pacman -S jdk17-openjdk']); + } } else { console.log(wrapInColor('Unsupported package manager, please install the required dependencies manually:', ANSI_COLORS.RED_COLOR)); console.log(wrapInColor('zlib git-lfs watchman libfontconfig-dev adb libtinfo5 openjdk-17', ANSI_COLORS.RED_COLOR));