[build images]

This commit is contained in:
Dylan Conway
2026-02-11 22:01:39 -08:00
parent 58e5724a9b
commit ecd6d8ea68
3 changed files with 69 additions and 54 deletions

View File

@@ -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;
};

View File

@@ -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) {