From ecd6d8ea68e4e218d9cbd9bc74da3e1bbfad4db9 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Wed, 11 Feb 2026 22:01:39 -0800 Subject: [PATCH] [build images] --- .buildkite/ci.mjs | 26 ++++++++++++++++------- scripts/azure.mjs | 49 ++++++++++++++++++++++++------------------- scripts/bootstrap.ps1 | 48 ++++++++++++++++++++---------------------- 3 files changed, 69 insertions(+), 54 deletions(-) diff --git a/.buildkite/ci.mjs b/.buildkite/ci.mjs index c218e99438..6a5446d1c1 100755 --- a/.buildkite/ci.mjs +++ b/.buildkite/ci.mjs @@ -99,6 +99,19 @@ function getTargetLabel(target) { * @property {string[]} [features] */ +/** + * @type {Platform[]} + */ +// Azure VM sizes — single source of truth for both ci.mjs and azure.mjs +const azureVmSize = { + "windows-aarch64": "Standard_D4ps_v6", // 4 vCPU, 16 GiB, Cobalt 100 + "windows-x64": "Standard_D8ds_v6", // 8 vCPU, 32 GiB +}; + +function getAzureVmSize(os, arch) { + return azureVmSize[`${os}-${arch}`]; +} + /** * @type {Platform[]} */ @@ -303,8 +316,7 @@ function getCppAgent(platform, options) { } return getEc2Agent(platform, options, { - instanceType: - os === "windows" && arch === "aarch64" ? "Standard_D4ps_v6" : arch === "aarch64" ? "c8g.4xlarge" : "c7i.4xlarge", + instanceType: os === "windows" ? getAzureVmSize(os, arch) : arch === "aarch64" ? "c8g.4xlarge" : "c7i.4xlarge", }); } @@ -326,7 +338,7 @@ function getLinkBunAgent(platform, options) { if (os === "windows") { return getEc2Agent(platform, options, { - instanceType: arch === "aarch64" ? "Standard_D4ps_v6" : "r7i.large", + instanceType: getAzureVmSize(os, arch), }); } @@ -356,10 +368,10 @@ function getZigPlatform() { function getZigAgent(platform, options) { const { os, arch } = platform; - // Windows ARM64 Zig builds on Azure runners - if (os === "windows" && arch === "aarch64") { + // Windows Zig builds on Azure runners + if (os === "windows") { return getEc2Agent(platform, options, { - instanceType: "Standard_D4ps_v6", + instanceType: getAzureVmSize(os, arch), }); } @@ -387,7 +399,7 @@ function getTestAgent(platform, options) { // TODO: delete this block when we upgrade to mimalloc v3 if (os === "windows") { return getEc2Agent(platform, options, { - instanceType: arch === "aarch64" ? "Standard_D4ps_v6" : "c7i.2xlarge", + instanceType: getAzureVmSize(os, arch), cpuCount: 2, threadsPerCore: 1, }); diff --git a/scripts/azure.mjs b/scripts/azure.mjs index 147b49c147..650447280e 100644 --- a/scripts/azure.mjs +++ b/scripts/azure.mjs @@ -143,7 +143,7 @@ async function azureFetch(method, path, body, apiVersion = "2024-07-01") { throw new Error(`[azure] ${method} ${path} failed after 3 retries`); } -async function waitForOperation(operationUrl, maxWaitMs = 600_000) { +async function waitForOperation(operationUrl, maxWaitMs = 3_600_000) { const start = Date.now(); let fetchErrors = 0; @@ -337,6 +337,9 @@ async function createVm(opts) { adminUsername: opts.adminUsername, adminPassword: opts.adminPassword, }, + securityProfile: { + securityType: "TrustedLaunch", + }, networkProfile: { networkInterfaces: [{ id: opts.nicId, properties: { deleteOption: "Delete" } }], }, @@ -477,8 +480,12 @@ async function ensureImageDefinition(name, os, arch) { identifier: { publisher: "bun", offer: `${os}-${arch}-ci`, - sku: "buildkite-agent", + sku: name, }, + features: [ + { name: "DiskControllerTypes", value: "SCSI, NVMe" }, + { name: "SecurityType", value: "TrustedLaunch" }, + ], }, }, GALLERY_API_VERSION, @@ -630,9 +637,10 @@ export const azure = { // This avoids the sshd startup issues on Azure Windows VMs. const spawnFn = async (command, opts) => { - // Convert command array to a single PowerShell command string const script = command.join(" "); console.log(`[azure] Run: ${script}`); + // Note: Azure Run Command output is limited to the last 4096 bytes. + // Full output is not available — only the tail is returned. const result = await runCommand(vmName, [script]); const stdout = typeof result === "string" ? result : (result?.value?.[0]?.message ?? ""); if (opts?.stdio === "inherit") { @@ -687,40 +695,37 @@ export const azure = { await generalizeVm(vmName); - // Ensure gallery and image definition exist + // Ensure gallery and image definition exist. + // Use the label as the image definition name — this matches what ci.mjs + // emits as the image-name agent tag, so robobun can look it up directly. await ensureGallery(); - // Use underscore format to match robobun's getAzureGalleryImageName() - const release = options.release || (arch === "aarch64" ? "11" : "2019"); - const safeRelease = release.replace(/\./g, "_"); - const imageDefName = `${os}_${arch}_${safeRelease}`; + const imageDefName = label; await ensureImageDefinition(imageDefName, os, arch); - // Azure gallery image versions must be semver (Major.Minor.Patch). - // Use timestamp-based version, and store the label as a tag so - // robobun/CI can look up images by their logical name. - const now = new Date(); - const version = `${now.getFullYear() % 100}.${now.getMonth() * 100 + now.getDate()}.${now.getHours() * 10000 + now.getMinutes() * 100 + now.getSeconds()}`; - await createImageVersionWithLabel(imageDefName, version, vmId, label); + // Create a single version "1.0.0" under this definition. + await createImageVersion(imageDefName, "1.0.0", vmId); - // Wait for image version to finish provisioning before deleting the source VM + // Wait for image replication to complete before returning. + // Single-region replication typically takes 5-15 minutes. const { galleryName } = config(); - const versionPath = `${rgPath()}/providers/Microsoft.Compute/galleries/${galleryName}/images/${imageDefName}/versions/${version}`; - console.log(`[azure] Waiting for image to replicate...`); + const versionPath = `${rgPath()}/providers/Microsoft.Compute/galleries/${galleryName}/images/${imageDefName}/versions/1.0.0`; + console.log(`[azure] Waiting for image replication...`); for (let i = 0; i < 120; i++) { const ver = await azureFetch("GET", versionPath, undefined, GALLERY_API_VERSION); const state = ver?.properties?.provisioningState; if (state === "Succeeded") { - console.log(`[azure] Image replicated successfully`); + console.log(`[azure] Image ready: ${imageDefName}/1.0.0`); break; } if (state === "Failed") { - throw new Error(`[azure] Image replication failed: ${JSON.stringify(ver?.properties?.provisioningProfile)}`); + throw new Error(`[azure] Image replication failed: ${JSON.stringify(ver?.properties)}`); } - console.log(`[azure] Image state: ${state}, waiting...`); - await new Promise(r => setTimeout(r, 15_000)); + if (i % 6 === 0) { + console.log(`[azure] Image replicating... (${i}m elapsed)`); + } + await new Promise(r => setTimeout(r, 10_000)); } - console.log(`[azure] Image created: ${imageDefName}/${version} (label: ${label})`); return label; }; diff --git a/scripts/bootstrap.ps1 b/scripts/bootstrap.ps1 index 01a31d205e..4af48739d6 100755 --- a/scripts/bootstrap.ps1 +++ b/scripts/bootstrap.ps1 @@ -26,16 +26,6 @@ $script:IsARM64 = [System.Runtime.InteropServices.RuntimeInformation]::OSArchite # Utility functions # ============================================================================ -function Execute-Command { - $command = $args -join ' ' - Write-Output "$ $command" - - & $args[0] $args[1..$args.Length] - - if ((-not $?) -or ($LASTEXITCODE -ne 0 -and $null -ne $LASTEXITCODE)) { - throw "Command failed: $command" - } -} function Which { param ([switch]$Required = $false) @@ -180,7 +170,7 @@ function Install-Scoop-Package { } Write-Output "Installing $Name (via Scoop)..." - Execute-Command scoop install $Name + scoop install $Name Refresh-Path } @@ -192,10 +182,10 @@ function Install-Git { Install-Scoop-Package git if ($CI) { - Execute-Command git config --system --add safe.directory "*" - Execute-Command git config --system core.autocrlf false - Execute-Command git config --system core.eol lf - Execute-Command git config --system core.longpaths true + git config --system --add safe.directory "*" + git config --system core.autocrlf false + git config --system core.eol lf + git config --system core.longpaths true } } @@ -208,11 +198,18 @@ function Install-CMake { } function Install-Llvm { - if ($script:IsARM64) { - Install-Scoop-Package llvm-arm64 -Command clang-cl - } else { - Install-Scoop-Package llvm -Command clang-cl + $LLVM_VERSION = "21.1.8" + if (Which clang-cl) { + return } + if ($script:IsARM64) { + Write-Output "Installing LLVM $LLVM_VERSION (ARM64 via Scoop)..." + scoop install "llvm-arm64@$LLVM_VERSION" + } else { + Write-Output "Installing LLVM $LLVM_VERSION (x64 via Scoop)..." + scoop install "llvm@$LLVM_VERSION" + } + Refresh-Path } function Install-Ninja { @@ -257,8 +254,9 @@ function Install-Pwsh { return } - Write-Output "Installing PowerShell Core (ARM64)..." - $msi = Download-File "https://github.com/PowerShell/PowerShell/releases/download/v7.5.2/PowerShell-7.5.2-win-arm64.msi" -Name "pwsh-arm64.msi" + $pwshArch = if ($script:IsARM64) { "arm64" } else { "x64" } + Write-Output "Installing PowerShell Core ($pwshArch)..." + $msi = Download-File "https://github.com/PowerShell/PowerShell/releases/download/v7.5.2/PowerShell-7.5.2-win-$pwshArch.msi" -Name "pwsh-$pwshArch.msi" $process = Start-Process msiexec -ArgumentList "/i `"$msi`" /quiet /norestart ADD_PATH=1" -Wait -PassThru -NoNewWindow if ($process.ExitCode -ne 0) { throw "Failed to install PowerShell: code $($process.ExitCode)" @@ -304,7 +302,7 @@ function Install-Bun { Write-Output "Installing Bun..." $installScript = Download-File "https://bun.sh/install.ps1" -Name "bun-install.ps1" $pwsh = Which pwsh powershell -Required - Execute-Command $pwsh $installScript + & $pwsh $installScript } function Install-Rust { @@ -316,7 +314,7 @@ function Install-Rust { $rustupInit = Download-File "https://win.rustup.rs/" -Name "rustup-init.exe" Write-Output "Installing Rust..." - Execute-Command $rustupInit -y + & $rustupInit -y Write-Output "Moving Rust to $env:ProgramFiles..." $rustPath = Join-Path $env:ProgramFiles "Rust" @@ -358,7 +356,7 @@ function Install-Visual-Studio { } function Install-PdbAddr2line { - Execute-Command cargo install --examples "pdb-addr2line@0.11.2" + cargo install --examples "pdb-addr2line@0.11.2" } function Install-Nssm { @@ -403,7 +401,7 @@ function Install-Buildkite { $env:buildkiteAgentToken = "xxx" $installScript = Download-File "https://raw.githubusercontent.com/buildkite/agent/main/install.ps1" $pwsh = Which pwsh powershell -Required - Execute-Command $pwsh $installScript + & $pwsh $installScript Refresh-Path if ($CI) {