diff --git a/build-windows-base.sh b/build-windows-base.sh index 4288708..a567444 100755 --- a/build-windows-base.sh +++ b/build-windows-base.sh @@ -31,6 +31,8 @@ set -euo pipefail images_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" downloader="${images_dir}/download-images.sh" +# Path lookups below must not offer to run this script again. +export VM_IMAGE_NO_PROMPT=1 win_dir="${images_dir}/windows11" usage() { @@ -116,6 +118,17 @@ if ((!from_img)); then || die "install socat or nc (it presses a key at the boot prompt of the ISO)." fi +# Without the ISO, offer to download it (needs a terminal). +if [[ -z "${WIN11_VM_INSTALL_ISO:-}" ]] && ! "$downloader" --print-path windows11 --iso >/dev/null 2>&1; then + "$downloader" --print-path windows11 --iso >&2 || true + if { : /dev/tty; } 2>/dev/null; then + printf 'Download the Windows 11 ISO now? [y/N] ' >/dev/tty + read -r answer /dev/tty; } 2>/dev/null || return 1 + printf '%s [y/N] ' "$1" >/dev/tty + read -r answer /dev/null || true + if [[ -z "${VM_IMAGE_NO_PROMPT:-}" && -x "$builder" ]] && ask_yes_no "Build the Windows 11 base image now with build-windows-base.sh?"; then + # stdout carries the path, so the build's output goes to the terminal. + "$builder" >/dev/tty 2>&1 /dev/null +} + print_image_path() { local target="$1" local media="${2:-qcow2}" @@ -171,7 +201,7 @@ print_image_path() { if [[ "$media" == iso ]]; then windows_iso_path else - windows_qcow2_path + windows_base_path fi ;; *) @@ -416,7 +446,27 @@ download_windows_iso() { printf 'Error: node is required to get the Windows 11 ISO URL.\n' >&2 exit 1 fi - url="$(node "${images_dir}/nodejs/get-windows-iso-url.js" --arch "$windows_arch")" + if [[ ! -d "${images_dir}/nodejs/node_modules/playwright-core" ]]; then + printf 'Error: the Node.js dependencies are not installed (npm ci has not been run in %s/nodejs).\n' \ + "$images_dir" >&2 + command -v npm >/dev/null 2>&1 || { printf 'Error: npm is required; install Node.js with npm.\n' >&2; exit 1; } + ask_yes_no "Run npm ci in ${images_dir}/nodejs now?" || exit 1 + (cd "${images_dir}/nodejs" && npm ci) || exit 1 + fi + local status=0 + url="$(node "${images_dir}/nodejs/get-windows-iso-url.js" --arch "$windows_arch")" || status=$? + if ((status == 2)) && [[ -z "${CHROME_PATH:-}" ]]; then + # Exit status 2 means Chromium was not found (the script has said so). + ask_yes_no "Install Chromium with playwright-core now?" || exit 1 + # Default to the npmmirror mirror (the official CDN is slow in China); set + # PLAYWRIGHT_DOWNLOAD_HOST to override it. + (cd "${images_dir}/nodejs" \ + && PLAYWRIGHT_DOWNLOAD_HOST="${PLAYWRIGHT_DOWNLOAD_HOST:-https://cdn.npmmirror.com/binaries/playwright}" \ + npx playwright-core install chromium) || exit 1 + url="$(node "${images_dir}/nodejs/get-windows-iso-url.js" --arch "$windows_arch")" || exit 1 + elif ((status != 0)); then + exit "$status" + fi [[ -n "$url" ]] || { printf 'Error: no Windows 11 ISO URL.\n' >&2; exit 1; } name="${url%%\?*}" name="${name##*/}" diff --git a/nodejs/get-windows-iso-url.js b/nodejs/get-windows-iso-url.js index d69e280..807eb16 100755 --- a/nodejs/get-windows-iso-url.js +++ b/nodejs/get-windows-iso-url.js @@ -5,12 +5,11 @@ // Usage: node get-windows-iso-url.js [--arch x64|arm64] [--lang NAME] // Environment: // PLAYWRIGHT_CORE playwright-core directory (default: node_modules here) -// CHROME_PATH Chromium executable (default: newest one in ~/.cache/ms-playwright) +// CHROME_PATH Chromium executable (default: the one Playwright installed) // The URL expires after about 24 hours and may be bound to the IP that // requested it. Only the URL goes to stdout; logs go to stderr. const fs = require('fs'); -const os = require('os'); const path = require('path'); const args = process.argv.slice(2); @@ -28,8 +27,7 @@ Options: Environment: PLAYWRIGHT_CORE playwright-core directory (default: node_modules here) - CHROME_PATH Chromium executable (default: newest one in - ~/.cache/ms-playwright) + CHROME_PATH Chromium executable (default: the one Playwright installed) Examples: node get-windows-iso-url.js --arch x64 --lang "Chinese Simplified" @@ -60,17 +58,20 @@ function findPlaywright() { process.exit(2); } -function findChrome() { - if (process.env.CHROME_PATH) return process.env.CHROME_PATH; - const root = path.join(os.homedir(), '.cache/ms-playwright'); - const dirs = fs.existsSync(root) ? fs.readdirSync(root).filter(d => /^chromium-\d+$/.test(d)).sort() : []; - if (!dirs.length) { console.error('Chromium not found: set CHROME_PATH or run npx playwright-core install chromium'); process.exit(2); } - return path.join(root, dirs.pop(), 'chrome-linux64/chrome'); +// CHROME_PATH wins; otherwise Playwright picks the Chromium it installed (in +// its own per-OS cache, PLAYWRIGHT_BROWSERS_PATH if set). +function launchOptions(chromium) { + if (process.env.CHROME_PATH) return { executablePath: process.env.CHROME_PATH, headless: true }; + if (!fs.existsSync(chromium.executablePath())) { + console.error('Chromium not found: set CHROME_PATH or run npx playwright-core install chromium'); + process.exit(2); + } + return { headless: true }; } (async () => { const { chromium } = require(findPlaywright()); - const browser = await chromium.launch({ executablePath: findChrome(), headless: true }); + const browser = await chromium.launch(launchOptions(chromium)); try { const ctx = await browser.newContext({ locale: 'en-US',