From 0ffbbe0d48cd65638423fb9e37cee37653fad4db Mon Sep 17 00:00:00 2001 From: gambtho Date: Tue, 17 Mar 2026 09:50:57 -0400 Subject: [PATCH] dx: Streamline setup with single command, parallel installs, and cross-platform support --- README.md | 99 +++++++++++++---------------------- build/setup-external-tools.ts | 52 ++++++++++++++---- package.json | 9 ++-- scripts/setup-submodule.mjs | 55 +++++++++++++++++++ 4 files changed, 139 insertions(+), 76 deletions(-) create mode 100644 scripts/setup-submodule.mjs diff --git a/README.md b/README.md index 55d938a58..6a2534063 100644 --- a/README.md +++ b/README.md @@ -15,82 +15,55 @@ Please download the latest release for your platform from the [Releases](https:/ ## Development -To run AKS desktop locally, follow these steps: +### Prerequisites -1. Clone the repository: +- [Node.js](https://nodejs.org/) 20+ +- [Go](https://go.dev/) 1.22+ (for the Headlamp backend) +- [Git](https://git-scm.com/) +- GNU Make — included on macOS/Linux; on Windows install via `winget install GnuWin32.Make` or [Chocolatey](https://community.chocolatey.org/packages/make) (`choco install make`) - ```bash - git clone --recurse-submodules https://github.com/Azure/aks-desktop.git - ``` +### Quick start -2. Navigate to the project directory: +```bash +git clone --recurse-submodules https://github.com/Azure/aks-desktop.git +cd aks-desktop +npm run setup # resets submodule, installs deps, builds backend +npm run dev # starts the app in development mode +``` - ```bash - cd aks-desktop - ``` +### Optional: use system Azure CLI -3. Install the dependencies: +If you already have [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) installed and want to skip the bundled download: - ```bash - ./scripts/headlamp-submodule.sh --reset - npm install - npm run install:all - ``` +```bash +# Linux / macOS +AKS_DESKTOP_SYSTEM_AZ=1 npm run setup -4. Check for the resource folder: +# Windows (cmd) +set AKS_DESKTOP_SYSTEM_AZ=1 && npm run setup - Ensure that the `resources` folder exists in the `headlamp/app` directory. - If `headlamp/app/resources` does not exist, run the following command from the root directory. +# Windows (PowerShell) +$env:AKS_DESKTOP_SYSTEM_AZ="1"; npm run setup +``` - ```bash - npm run plugin:setup - ``` +### Manual steps (if you prefer) -5. Build the Headlamp backend server: - - Navigate to the `headlamp` directory and build the backend server - - ```bash - cd headlamp - make backend - ``` - -6. Start the application at the root directory: - - Navigate back to the root directory and start the application in development mode: - - ```bash - npm run dev - ``` +```bash +node scripts/setup-submodule.mjs # or: ./scripts/headlamp-submodule.sh --reset +npm ci +npm run install:all # installs headlamp, plugin, and ai-assistant in parallel +cd headlamp && make backend && cd .. +npm run dev +``` ## How to Build -To get started with AKS desktop, follow these steps: - -1. Clone the repository: - - ```bash - git clone --recurse-submodules https://github.com/Azure/aks-desktop.git - ``` - -2. Navigate to the project directory: - - ```bash - cd aks-desktop - ``` - -3. Install the dependencies: - - ```bash - ./scripts/headlamp-submodule.sh --reset - npm install - npm run install:all - ``` - -4. Build the project: - ```bash - npm run build - ``` +```bash +git clone --recurse-submodules https://github.com/Azure/aks-desktop.git +cd aks-desktop +npm run setup +npm run build +``` ## Documentation diff --git a/build/setup-external-tools.ts b/build/setup-external-tools.ts index f08099101..5339c596a 100644 --- a/build/setup-external-tools.ts +++ b/build/setup-external-tools.ts @@ -35,19 +35,53 @@ const EXTERNAL_TOOLS_DIR = path.join(ROOT_DIR, 'headlamp', 'app', 'resources', ' const EXTERNAL_TOOLS_BIN = path.join(EXTERNAL_TOOLS_DIR, 'bin'); const AZ_CLI_DIR = path.join(EXTERNAL_TOOLS_DIR, 'az-cli', PLATFORM); -// Download and install Azure CLI +// Download and install Azure CLI (or use system az if AKS_DESKTOP_SYSTEM_AZ is set) console.log('=========================================='); console.log('Installing Azure CLI...'); console.log('=========================================='); -try { - execSync(`npx --yes tsx "${path.join(SCRIPT_DIR, 'download-az-cli.ts')}"`, { - stdio: 'inherit', - cwd: ROOT_DIR - }); -} catch (error) { - console.error('❌ ERROR: Failed to install Azure CLI'); - process.exit(1); +if (process.env.AKS_DESKTOP_SYSTEM_AZ) { + // Check if system az is available + try { + const azVersion = execSync('az version --output tsv', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }); + console.log('Using system Azure CLI installation:'); + console.log(azVersion.trim()); + console.log(''); + console.log('Skipping bundled Azure CLI download (AKS_DESKTOP_SYSTEM_AZ is set).'); + + // Create the az-cli directory structure so the rest of the setup doesn't fail + const azCliBinDir = path.join(AZ_CLI_DIR, 'bin'); + fs.mkdirSync(azCliBinDir, { recursive: true }); + + // Create a wrapper that delegates to the system az using its absolute path. + // This avoids infinite recursion since Electron prepends az-cli/bin to PATH at runtime. + if (PLATFORM === 'win32') { + const systemAzPath = execSync('where az', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim().split(/\r?\n/)[0]; + fs.writeFileSync(path.join(azCliBinDir, 'az.cmd'), `@echo off\r\ncall "${systemAzPath}" %*\r\n`); + } else { + const systemAzPath = execSync('command -v az', { encoding: 'utf-8', shell: '/bin/sh', stdio: ['pipe', 'pipe', 'pipe'] }).trim(); + fs.writeFileSync(path.join(azCliBinDir, 'az-wrapper'), `#!/bin/sh\nexec "${systemAzPath}" "$@"\n`, { mode: 0o755 }); + const azSymlink = path.join(azCliBinDir, 'az'); + if (fs.existsSync(azSymlink)) fs.unlinkSync(azSymlink); + fs.symlinkSync('az-wrapper', azSymlink); + } + + console.log('✅ System Azure CLI wrapper created'); + } catch { + console.error('❌ ERROR: AKS_DESKTOP_SYSTEM_AZ is set but "az" was not found on PATH.'); + console.error(' Install Azure CLI or unset AKS_DESKTOP_SYSTEM_AZ to download it automatically.'); + process.exit(1); + } +} else { + try { + execSync(`npx --yes tsx "${path.join(SCRIPT_DIR, 'download-az-cli.ts')}"`, { + stdio: 'inherit', + cwd: ROOT_DIR + }); + } catch (error) { + console.error('❌ ERROR: Failed to install Azure CLI'); + process.exit(1); + } } console.log(''); diff --git a/package.json b/package.json index 4de651e2c..eb50f8c3b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "build:unpacked:win": "npm run plugin:setup && cd headlamp && make app-build", "test:post-build": "npx --yes tsx ./build/verify-bundled-tools.ts", "plugin:setup": "npx --yes tsx ./build/setup-plugins.ts", - "plugin:install": "cd plugins/aks-desktop && npm install", + "plugin:install": "cd plugins/aks-desktop && npm ci", "plugin:build": "cd plugins/aks-desktop && npm run build", "plugin:start": "cd plugins/aks-desktop && npm start", "plugin:test": "cd plugins/aks-desktop && npm test", @@ -23,15 +23,16 @@ "plugin:test:all": "cd plugins/aks-desktop && npm run test:all", "plugin:format": "cd plugins/aks-desktop && npm run format", "plugin:package": "cd plugins/aks-desktop && npm run package", - "ai-assistant:install": "cd plugins/ai-assistant && npm install", + "ai-assistant:install": "cd plugins/ai-assistant && npm ci", "ai-assistant:build": "cd plugins/ai-assistant && npm run build", "ai-assistant:start": "cd plugins/ai-assistant && npm start", "ai-assistant:test": "cd plugins/ai-assistant && npm test", "ai-assistant:lint": "cd plugins/ai-assistant && npm run lint", "ai-assistant:format": "cd plugins/ai-assistant && npm run format", - "headlamp:install": "cd headlamp/frontend && npm install && cd ../app && npm install", + "headlamp:install": "cd headlamp/frontend && npm ci && cd ../app && npm ci", "headlamp:build": "cd headlamp && make backend && make frontend", - "install:all": "npm run headlamp:install && npm run plugin:install && npm run ai-assistant:install", + "install:all": "concurrently \"npm run headlamp:install\" \"npm run plugin:install\" \"npm run ai-assistant:install\"", + "setup": "node scripts/setup-submodule.mjs && npm ci && npm run install:all && cd headlamp && make backend", "build:all": "npm run headlamp:build && npm run plugin:build && npm run ai-assistant:build", "dev": "concurrently --kill-others --names \"PLUGIN,AI-ASSISTANT,BACKEND,FRONTEND,APP\" -c \"cyan,blue,green,yellow,magenta\" \"npm run plugin:start\" \"npm run ai-assistant:start\" \"cd headlamp && make run-backend\" \"cd headlamp && make run-frontend\" \"cd headlamp && make run-only-app\"", "prepare": "husky", diff --git a/scripts/setup-submodule.mjs b/scripts/setup-submodule.mjs new file mode 100644 index 000000000..5e168be97 --- /dev/null +++ b/scripts/setup-submodule.mjs @@ -0,0 +1,55 @@ +#!/usr/bin/env node + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache 2.0. + +/** + * Cross-platform Node.js replacement for headlamp-submodule.sh --reset + * Resets the headlamp submodule to the commit recorded in the superproject. + * + * Usage: + * node scripts/setup-submodule.mjs # reset submodule (default) + * node scripts/setup-submodule.mjs --reset # same as above + */ + +import { execSync } from 'child_process'; +import { existsSync } from 'fs'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const ROOT_DIR = dirname(dirname(__filename)); +const HEADLAMP_DIR = join(ROOT_DIR, 'headlamp'); + +// Check for dirty submodule +if (existsSync(HEADLAMP_DIR)) { + try { + const status = execSync('git status --porcelain', { + cwd: HEADLAMP_DIR, + encoding: 'utf-8', + }).trim(); + if (status) { + console.warn( + '[warn] You have local changes inside the headlamp submodule that will be overwritten by reset.' + ); + } + } catch { + // Not initialized yet — that's fine + } +} + +console.log('[info] Resetting headlamp submodule to superproject recorded commit'); + +execSync('git submodule update --init --checkout headlamp', { + cwd: ROOT_DIR, + stdio: 'inherit', +}); + +// Show current commit +const desc = execSync('git log --oneline -1', { + cwd: HEADLAMP_DIR, + encoding: 'utf-8', +}).trim(); + +console.log(`[info] Headlamp now at: ${desc}`); +console.log('[done] Submodule reset to recorded commit.');