initial commit
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
// Linux (debian-13) build image for local dev (docker) and CI (AWS AMI).
|
||||
// Both builders run the same scripts/bootstrap.sh provisioner so the two
|
||||
// environments stay in lock-step.
|
||||
//
|
||||
// Shared variables (repo_ref, bootstrap_script, agent_script, image_name,
|
||||
// build_number) live in variables.pkr.hcl — `packer init`/`build` is invoked
|
||||
// on the whole directory (see scripts/machine.mjs), so redeclaring them here
|
||||
// would be a duplicate-variable error.
|
||||
|
||||
packer {
|
||||
required_plugins {
|
||||
docker = {
|
||||
source = "github.com/hashicorp/docker"
|
||||
version = ">= 1.0.0"
|
||||
}
|
||||
amazon = {
|
||||
source = "github.com/hashicorp/amazon"
|
||||
version = ">= 1.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "arch" {
|
||||
type = string
|
||||
default = "x64"
|
||||
description = "Target architecture: x64 or aarch64."
|
||||
validation {
|
||||
condition = contains(["x64", "aarch64"], var.arch)
|
||||
error_message = "The arch variable must be one of: x64, aarch64."
|
||||
}
|
||||
}
|
||||
|
||||
variable "ci" {
|
||||
type = bool
|
||||
default = true
|
||||
description = "Pass --ci to bootstrap.sh (installs buildkite-agent, sysroots, prefetch cache)."
|
||||
}
|
||||
|
||||
variable "version" {
|
||||
type = string
|
||||
default = "0"
|
||||
description = "Bootstrap version (the `# Version:` comment in scripts/bootstrap.sh). Used in the AMI name / docker tag."
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
default = env("AWS_REGION") != "" ? env("AWS_REGION") : "us-east-1"
|
||||
description = "AWS region for the amazon-ebs builder."
|
||||
}
|
||||
|
||||
variable "instance_type" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "EC2 instance type for the bake VM. Empty => derived from arch."
|
||||
}
|
||||
|
||||
variable "root_volume_size" {
|
||||
type = number
|
||||
default = 64
|
||||
description = "Root EBS volume size in GiB (xfs, gp3)."
|
||||
}
|
||||
|
||||
locals {
|
||||
// Debian Cloud Team AMIs (owner 136693071363) use amd64/arm64 in the Name;
|
||||
// EC2's architecture filter wants x86_64/arm64.
|
||||
debian_name_arch = var.arch == "aarch64" ? "arm64" : "amd64"
|
||||
ec2_arch = var.arch == "aarch64" ? "arm64" : "x86_64"
|
||||
// c7i for x64, c7g for Graviton — build-only VM, not the CI runner size.
|
||||
instance_type = var.instance_type != "" ? var.instance_type : (var.arch == "aarch64" ? "c7g.2xlarge" : "c7i.2xlarge")
|
||||
// Matches getImageKey()+getImageName() in .buildkite/ci.mjs when called
|
||||
// with {os:"linux", arch, distro:"debian", release:"13"} under publish:
|
||||
// "linux-<arch>-13-debian-v<bootstrapVersion>"
|
||||
// image_name (from variables.pkr.hcl) overrides for [build images] runs.
|
||||
ami_name = var.image_name != "" ? var.image_name : "linux-${var.arch}-13-debian-v${var.version}"
|
||||
ci_flag = var.ci ? "--ci" : ""
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Docker: local-dev image. Same bootstrap as the AMI so `docker run` matches
|
||||
// what CI sees. `commit=true` => the provisioned container is committed to an
|
||||
// image; the docker-tag post-processor names it.
|
||||
// ---------------------------------------------------------------------------
|
||||
source "docker" "debian" {
|
||||
image = "debian:13-slim"
|
||||
platform = "linux/${local.debian_name_arch}"
|
||||
commit = true
|
||||
changes = [
|
||||
"LABEL org.opencontainers.image.source=https://github.com/oven-sh/bun",
|
||||
"LABEL sh.bun.bootstrap.version=${var.version}",
|
||||
"ENV CI=true",
|
||||
"ENV DEBIAN_FRONTEND=noninteractive",
|
||||
]
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AWS: CI AMI. Filters the latest official debian-13 cloud image for the
|
||||
// requested arch, bakes on a gp3/xfs root volume.
|
||||
// ---------------------------------------------------------------------------
|
||||
source "amazon-ebs" "debian" {
|
||||
region = var.region
|
||||
instance_type = local.instance_type
|
||||
ssh_username = "admin"
|
||||
|
||||
ami_name = local.ami_name
|
||||
ami_description = "Bun CI build image (debian-13, ${var.arch}, bootstrap v${var.version})"
|
||||
force_deregister = true
|
||||
force_delete_snapshot = true
|
||||
|
||||
source_ami_filter {
|
||||
filters = {
|
||||
name = "debian-13-${local.debian_name_arch}-*"
|
||||
architecture = local.ec2_arch
|
||||
root-device-type = "ebs"
|
||||
virtualization-type = "hvm"
|
||||
}
|
||||
owners = ["136693071363"] // Debian Cloud Team
|
||||
most_recent = true
|
||||
}
|
||||
|
||||
// Root volume on gp3. Debian cloud AMIs expose root as /dev/xvda.
|
||||
// NOTE: the Debian base AMI ships an ext4 root; an xfs ROOT needs a
|
||||
// rebased AMI or a separate data volume. For now we attach a second gp3
|
||||
// volume that bootstrap can mkfs.xfs and mount for /var/lib/buildkite —
|
||||
// revisit once a debian-13-xfs base exists.
|
||||
launch_block_device_mappings {
|
||||
device_name = "/dev/xvda"
|
||||
volume_size = var.root_volume_size
|
||||
volume_type = "gp3"
|
||||
delete_on_termination = true
|
||||
}
|
||||
launch_block_device_mappings {
|
||||
device_name = "/dev/xvdb"
|
||||
volume_size = var.root_volume_size
|
||||
volume_type = "gp3"
|
||||
delete_on_termination = true
|
||||
}
|
||||
user_data = <<-EOF
|
||||
#cloud-config
|
||||
fs_setup:
|
||||
- device: /dev/xvdb
|
||||
filesystem: xfs
|
||||
label: build
|
||||
mounts:
|
||||
- ["/dev/xvdb", "/var/lib/buildkite-agent", "xfs", "defaults,nofail", "0", "2"]
|
||||
EOF
|
||||
|
||||
tags = {
|
||||
Name = local.ami_name
|
||||
os = "linux"
|
||||
arch = var.arch
|
||||
distro = "debian"
|
||||
build = var.build_number
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
name = "linux-debian-13"
|
||||
sources = [
|
||||
"source.docker.debian",
|
||||
"source.amazon-ebs.debian",
|
||||
]
|
||||
|
||||
// debian:13-slim has no curl/sudo/ca-certs; bootstrap.sh's fetch() needs
|
||||
// curl-or-wget before install_common_software runs. The Debian AMI already
|
||||
// has these, but a second `apt-get install` is a no-op there.
|
||||
provisioner "shell" {
|
||||
environment_vars = ["DEBIAN_FRONTEND=noninteractive"]
|
||||
inline = [
|
||||
"set -eu",
|
||||
"if command -v sudo >/dev/null 2>&1; then SUDO=sudo; else SUDO=; fi",
|
||||
"$SUDO apt-get update -y",
|
||||
"$SUDO apt-get install -y --no-install-recommends ca-certificates curl sudo",
|
||||
]
|
||||
}
|
||||
|
||||
// Upload bootstrap.sh (path comes from -var bootstrap_script=..., see
|
||||
// machine.mjs; default in variables.pkr.hcl is the .ps1 — caller must
|
||||
// override for this template).
|
||||
provisioner "file" {
|
||||
source = var.bootstrap_script
|
||||
destination = "/tmp/bootstrap.sh"
|
||||
}
|
||||
|
||||
// Run bootstrap. docker runs as root (no sudo); amazon-ebs runs as `admin`
|
||||
// with passwordless sudo. `-E` preserves BUN_BOOTSTRAP_REPO_REF across the
|
||||
// sudo boundary so prefetch_build_deps() clones the right ref.
|
||||
provisioner "shell" {
|
||||
environment_vars = [
|
||||
"BUN_BOOTSTRAP_REPO_REF=${var.repo_ref}",
|
||||
"DEBIAN_FRONTEND=noninteractive",
|
||||
]
|
||||
execute_command = "chmod +x {{ .Path }}; if command -v sudo >/dev/null 2>&1 && [ \"$(id -u)\" -ne 0 ]; then sudo -E sh -c '{{ .Vars }} {{ .Path }}'; else {{ .Vars }} sh '{{ .Path }}'; fi"
|
||||
inline = [
|
||||
"set -eu",
|
||||
"sh /tmp/bootstrap.sh ${local.ci_flag}",
|
||||
]
|
||||
}
|
||||
|
||||
// Optional: install agent.mjs as a service. Skipped when agent_script is
|
||||
// empty (local docker dev image). Mirrors the Windows templates' step 2/3.
|
||||
provisioner "file" {
|
||||
only = ["amazon-ebs.debian"]
|
||||
source = var.agent_script
|
||||
destination = "/tmp/agent.mjs"
|
||||
}
|
||||
provisioner "shell" {
|
||||
only = ["amazon-ebs.debian"]
|
||||
inline = [
|
||||
"set -eu",
|
||||
"if [ -s /tmp/agent.mjs ]; then",
|
||||
" sudo mkdir -p /var/lib/buildkite-agent",
|
||||
" sudo cp /tmp/agent.mjs /var/lib/buildkite-agent/agent.mjs",
|
||||
" sudo $(command -v node || command -v bun) /var/lib/buildkite-agent/agent.mjs install",
|
||||
"fi",
|
||||
]
|
||||
}
|
||||
|
||||
// Tag the committed docker image so `docker run oven/bun-build:<name>` works.
|
||||
post-processor "docker-tag" {
|
||||
only = ["docker.debian"]
|
||||
repository = "oven/bun-build"
|
||||
tags = [local.ami_name, "debian-13-${var.arch}"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
azure = {
|
||||
source = "github.com/hashicorp/azure"
|
||||
version = "= 2.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shared variables for all Windows image builds
|
||||
|
||||
variable "client_id" {
|
||||
type = string
|
||||
default = env("AZURE_CLIENT_ID")
|
||||
}
|
||||
|
||||
variable "client_secret" {
|
||||
type = string
|
||||
sensitive = true
|
||||
default = env("AZURE_CLIENT_SECRET")
|
||||
}
|
||||
|
||||
variable "subscription_id" {
|
||||
type = string
|
||||
default = env("AZURE_SUBSCRIPTION_ID")
|
||||
}
|
||||
|
||||
variable "tenant_id" {
|
||||
type = string
|
||||
default = env("AZURE_TENANT_ID")
|
||||
}
|
||||
|
||||
variable "resource_group" {
|
||||
type = string
|
||||
default = env("AZURE_RESOURCE_GROUP")
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
type = string
|
||||
default = "eastus2"
|
||||
}
|
||||
|
||||
variable "gallery_name" {
|
||||
type = string
|
||||
default = "bunCIGallery2"
|
||||
}
|
||||
|
||||
variable "build_number" {
|
||||
type = string
|
||||
default = "0"
|
||||
}
|
||||
|
||||
variable "image_name" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "Gallery image definition name. If empty, derived from build_number."
|
||||
}
|
||||
|
||||
variable "bootstrap_script" {
|
||||
type = string
|
||||
default = "scripts/bootstrap.ps1"
|
||||
}
|
||||
|
||||
variable "agent_script" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "Path to bundled agent.mjs. If empty, agent install is skipped."
|
||||
}
|
||||
|
||||
variable "repo_ref" {
|
||||
type = string
|
||||
default = "main"
|
||||
description = "Branch/tag of oven-sh/bun for bootstrap's Prefetch-Build-Deps to shallow-clone (dep version pins live in scripts/build/deps/)."
|
||||
}
|
||||
|
||||
variable "gallery_resource_group" {
|
||||
type = string
|
||||
default = "BUN-CI"
|
||||
description = "Resource group containing the Compute Gallery (may differ from build RG)"
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
source "azure-arm" "windows-arm64" {
|
||||
// Authentication
|
||||
client_id = var.client_id
|
||||
client_secret = var.client_secret
|
||||
subscription_id = var.subscription_id
|
||||
tenant_id = var.tenant_id
|
||||
|
||||
// Source image — Windows 11 ARM64 (no Windows Server ARM64 exists)
|
||||
os_type = "Windows"
|
||||
image_publisher = "MicrosoftWindowsDesktop"
|
||||
image_offer = "windows11preview-arm64"
|
||||
image_sku = "win11-24h2-pro"
|
||||
image_version = "latest"
|
||||
|
||||
// Build VM — only used during image creation, not for CI runners.
|
||||
// CI runner VM sizes are set in ci.mjs (azureVmSizes).
|
||||
vm_size = "Standard_D4pds_v6"
|
||||
|
||||
// Use existing resource group instead of creating a temp one
|
||||
build_resource_group_name = var.resource_group
|
||||
os_disk_size_gb = 150
|
||||
|
||||
// Security
|
||||
security_type = "TrustedLaunch"
|
||||
secure_boot_enabled = true
|
||||
vtpm_enabled = true
|
||||
|
||||
// Networking — Packer creates a temp VNet + public IP + NSG automatically.
|
||||
|
||||
// WinRM communicator
|
||||
communicator = "winrm"
|
||||
winrm_use_ssl = true
|
||||
winrm_insecure = true
|
||||
winrm_timeout = "15m"
|
||||
winrm_username = "packer"
|
||||
|
||||
// CRITICAL: No managed_image_name — ARM64 doesn't support Managed Images.
|
||||
// Packer publishes directly from the VM to the gallery (PR #242 feature).
|
||||
|
||||
// SIG replication to 27 regions takes longer than the 60m default; the
|
||||
// CreateOrUpdate poll was hitting "context deadline exceeded" at exactly 1h.
|
||||
shared_image_gallery_timeout = "3h"
|
||||
|
||||
shared_image_gallery_destination {
|
||||
subscription = var.subscription_id
|
||||
resource_group = var.gallery_resource_group
|
||||
gallery_name = var.gallery_name
|
||||
image_name = var.image_name != "" ? var.image_name : "windows-aarch64-11-build-${var.build_number}"
|
||||
image_version = "1.0.0"
|
||||
// Premium_LRS: SSD-backed gallery storage — faster provisioning when
|
||||
// robobun launches runners from this image, and faster cross-region
|
||||
// replication during the publish step above.
|
||||
storage_account_type = "Premium_LRS"
|
||||
target_region { name = var.location }
|
||||
target_region { name = "australiaeast" }
|
||||
target_region { name = "brazilsouth" }
|
||||
target_region { name = "canadacentral" }
|
||||
target_region { name = "canadaeast" }
|
||||
target_region { name = "centralindia" }
|
||||
target_region { name = "centralus" }
|
||||
target_region { name = "francecentral" }
|
||||
target_region { name = "germanywestcentral" }
|
||||
target_region { name = "italynorth" }
|
||||
target_region { name = "japaneast" }
|
||||
target_region { name = "japanwest" }
|
||||
target_region { name = "koreacentral" }
|
||||
target_region { name = "mexicocentral" }
|
||||
target_region { name = "northcentralus" }
|
||||
target_region { name = "northeurope" }
|
||||
target_region { name = "southcentralus" }
|
||||
target_region { name = "southeastasia" }
|
||||
target_region { name = "spaincentral" }
|
||||
target_region { name = "swedencentral" }
|
||||
target_region { name = "switzerlandnorth" }
|
||||
target_region { name = "uaenorth" }
|
||||
target_region { name = "ukwest" }
|
||||
target_region { name = "westeurope" }
|
||||
target_region { name = "westus" }
|
||||
target_region { name = "westus2" }
|
||||
target_region { name = "westus3" }
|
||||
}
|
||||
|
||||
azure_tags = {
|
||||
os = "windows"
|
||||
arch = "aarch64"
|
||||
build = var.build_number
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.azure-arm.windows-arm64"]
|
||||
|
||||
// Step 1: Run bootstrap — installs all build dependencies
|
||||
provisioner "powershell" {
|
||||
script = var.bootstrap_script
|
||||
valid_exit_codes = [0, 3010]
|
||||
environment_vars = ["CI=true", "BUN_BOOTSTRAP_REPO_REF=${var.repo_ref}"]
|
||||
}
|
||||
|
||||
// Step 2: Upload agent.mjs
|
||||
provisioner "file" {
|
||||
source = var.agent_script
|
||||
destination = "C:\\buildkite-agent\\agent.mjs"
|
||||
}
|
||||
|
||||
// Step 3: Install agent service via nssm
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"C:\\Scoop\\apps\\nodejs\\current\\node.exe C:\\buildkite-agent\\agent.mjs install"
|
||||
]
|
||||
valid_exit_codes = [0]
|
||||
}
|
||||
|
||||
// Step 4: Reboot to clear pending updates (VS Build Tools, Windows Updates)
|
||||
provisioner "windows-restart" {
|
||||
restart_timeout = "10m"
|
||||
}
|
||||
|
||||
// Step 5: Sysprep — MUST be last provisioner
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"Remove-Item -Recurse -Force C:\\Windows\\Panther -ErrorAction SilentlyContinue",
|
||||
"Write-Output '>>> Clearing pending reboot flags...'",
|
||||
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending' -Recurse -Force -ErrorAction SilentlyContinue",
|
||||
"Remove-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update' -Name 'RebootRequired' -Force -ErrorAction SilentlyContinue",
|
||||
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired' -Recurse -Force -ErrorAction SilentlyContinue",
|
||||
"Remove-ItemProperty 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager' -Name 'PendingFileRenameOperations' -Force -ErrorAction SilentlyContinue",
|
||||
"Write-Output '>>> Waiting for Azure Guest Agent...'",
|
||||
"while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
|
||||
"while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
|
||||
"Write-Output '>>> Running Sysprep...'",
|
||||
"$global:LASTEXITCODE = 0",
|
||||
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
|
||||
"$timeout = 300; $elapsed = 0",
|
||||
"while ($true) {",
|
||||
" $imageState = (Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State).ImageState",
|
||||
" Write-Output \"ImageState: $imageState ($${elapsed}s)\"",
|
||||
" if ($imageState -eq 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { break }",
|
||||
" if ($elapsed -ge $timeout) {",
|
||||
" Write-Error \"Timed out after $${timeout}s -- stuck at $imageState\"",
|
||||
" Get-Content \"$env:SystemRoot\\System32\\Sysprep\\Panther\\setupact.log\" -Tail 100 -ErrorAction SilentlyContinue",
|
||||
" exit 1",
|
||||
" }",
|
||||
" Start-Sleep -s 10",
|
||||
" $elapsed += 10",
|
||||
"}",
|
||||
"Write-Output '>>> Sysprep complete.'"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
source "azure-arm" "windows-x64" {
|
||||
// Authentication (from env vars or -var flags)
|
||||
client_id = var.client_id
|
||||
client_secret = var.client_secret
|
||||
subscription_id = var.subscription_id
|
||||
tenant_id = var.tenant_id
|
||||
|
||||
// Source image — Windows Server 2019 Gen2
|
||||
os_type = "Windows"
|
||||
image_publisher = "MicrosoftWindowsServer"
|
||||
image_offer = "WindowsServer"
|
||||
image_sku = "2019-datacenter-gensecond"
|
||||
image_version = "latest"
|
||||
|
||||
// Build VM — only used during image creation, not for CI runners.
|
||||
// CI runner VM sizes are set in ci.mjs (azureVmSizes).
|
||||
// D4as_v7 (AMD): D4ds_v6 hit repeated AllocationFailed (no capacity for
|
||||
// that size in the region); Azure's allocation-guidance suggested this
|
||||
// size as an in-region alternative. Build-only VM, so the CPU vendor
|
||||
// doesn't affect the produced image.
|
||||
vm_size = "Standard_D4as_v7"
|
||||
|
||||
// Use existing resource group instead of creating a temp one
|
||||
build_resource_group_name = var.resource_group
|
||||
os_disk_size_gb = 150
|
||||
|
||||
// Security
|
||||
security_type = "TrustedLaunch"
|
||||
secure_boot_enabled = true
|
||||
vtpm_enabled = true
|
||||
|
||||
// Networking — Packer creates a temp VNet + public IP + NSG automatically.
|
||||
// WinRM needs the public IP to connect from CI runners.
|
||||
|
||||
// WinRM communicator — Packer auto-configures via temp Key Vault
|
||||
communicator = "winrm"
|
||||
winrm_use_ssl = true
|
||||
winrm_insecure = true
|
||||
winrm_timeout = "15m"
|
||||
winrm_username = "packer"
|
||||
|
||||
// Output — Managed Image (x64 supports this)
|
||||
|
||||
// SIG replication to 27 regions takes longer than the 60m default; the
|
||||
// CreateOrUpdate poll was hitting "context deadline exceeded" at exactly 1h.
|
||||
shared_image_gallery_timeout = "3h"
|
||||
|
||||
// Also publish to Compute Gallery
|
||||
shared_image_gallery_destination {
|
||||
subscription = var.subscription_id
|
||||
resource_group = var.gallery_resource_group
|
||||
gallery_name = var.gallery_name
|
||||
image_name = var.image_name != "" ? var.image_name : "windows-x64-2019-build-${var.build_number}"
|
||||
image_version = "1.0.0"
|
||||
// Premium_LRS: SSD-backed gallery storage — faster provisioning when
|
||||
// robobun launches runners from this image, and faster cross-region
|
||||
// replication during the publish step above.
|
||||
storage_account_type = "Premium_LRS"
|
||||
target_region { name = var.location }
|
||||
target_region { name = "australiaeast" }
|
||||
target_region { name = "brazilsouth" }
|
||||
target_region { name = "canadacentral" }
|
||||
target_region { name = "canadaeast" }
|
||||
target_region { name = "centralindia" }
|
||||
target_region { name = "centralus" }
|
||||
target_region { name = "francecentral" }
|
||||
target_region { name = "germanywestcentral" }
|
||||
target_region { name = "italynorth" }
|
||||
target_region { name = "japaneast" }
|
||||
target_region { name = "japanwest" }
|
||||
target_region { name = "koreacentral" }
|
||||
target_region { name = "mexicocentral" }
|
||||
target_region { name = "northcentralus" }
|
||||
target_region { name = "northeurope" }
|
||||
target_region { name = "southcentralus" }
|
||||
target_region { name = "southeastasia" }
|
||||
target_region { name = "spaincentral" }
|
||||
target_region { name = "swedencentral" }
|
||||
target_region { name = "switzerlandnorth" }
|
||||
target_region { name = "uaenorth" }
|
||||
target_region { name = "ukwest" }
|
||||
target_region { name = "westeurope" }
|
||||
target_region { name = "westus" }
|
||||
target_region { name = "westus2" }
|
||||
target_region { name = "westus3" }
|
||||
}
|
||||
|
||||
azure_tags = {
|
||||
os = "windows"
|
||||
arch = "x64"
|
||||
build = var.build_number
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.azure-arm.windows-x64"]
|
||||
|
||||
// Step 1: Run bootstrap — installs all build dependencies
|
||||
provisioner "powershell" {
|
||||
script = var.bootstrap_script
|
||||
valid_exit_codes = [0, 3010]
|
||||
environment_vars = ["CI=true", "BUN_BOOTSTRAP_REPO_REF=${var.repo_ref}"]
|
||||
}
|
||||
|
||||
// Step 2: Upload agent.mjs
|
||||
provisioner "file" {
|
||||
source = var.agent_script
|
||||
destination = "C:\\buildkite-agent\\agent.mjs"
|
||||
}
|
||||
|
||||
// Step 3: Install agent service via nssm
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"C:\\Scoop\\apps\\nodejs\\current\\node.exe C:\\buildkite-agent\\agent.mjs install"
|
||||
]
|
||||
valid_exit_codes = [0]
|
||||
}
|
||||
|
||||
// Step 4: Reboot to clear pending updates (VS Build Tools, Windows Updates)
|
||||
provisioner "windows-restart" {
|
||||
restart_timeout = "10m"
|
||||
}
|
||||
|
||||
// Step 5: Sysprep — MUST be last provisioner
|
||||
provisioner "powershell" {
|
||||
inline = [
|
||||
"Remove-Item -Recurse -Force C:\\Windows\\Panther -ErrorAction SilentlyContinue",
|
||||
"Write-Output '>>> Clearing pending reboot flags...'",
|
||||
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending' -Recurse -Force -ErrorAction SilentlyContinue",
|
||||
"Remove-ItemProperty 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update' -Name 'RebootRequired' -Force -ErrorAction SilentlyContinue",
|
||||
"Remove-Item 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update\\RebootRequired' -Recurse -Force -ErrorAction SilentlyContinue",
|
||||
"Remove-ItemProperty 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager' -Name 'PendingFileRenameOperations' -Force -ErrorAction SilentlyContinue",
|
||||
"Write-Output '>>> Waiting for Azure Guest Agent...'",
|
||||
"while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
|
||||
"while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
|
||||
"Write-Output '>>> Running Sysprep...'",
|
||||
"$global:LASTEXITCODE = 0",
|
||||
"& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
|
||||
"$timeout = 300; $elapsed = 0",
|
||||
"while ($true) {",
|
||||
" $imageState = (Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State).ImageState",
|
||||
" Write-Output \"ImageState: $imageState ($${elapsed}s)\"",
|
||||
" if ($imageState -eq 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { break }",
|
||||
" if ($elapsed -ge $timeout) {",
|
||||
" Write-Error \"Timed out after $${timeout}s -- stuck at $imageState\"",
|
||||
" Get-Content \"$env:SystemRoot\\System32\\Sysprep\\Panther\\setupact.log\" -Tail 100 -ErrorAction SilentlyContinue",
|
||||
" exit 1",
|
||||
" }",
|
||||
" Start-Sleep -s 10",
|
||||
" $elapsed += 10",
|
||||
"}",
|
||||
"Write-Output '>>> Sysprep complete.'"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user