initial commit

This commit is contained in:
i2p
2026-08-27 21:09:14 +00:00
commit a5b6d59437
12681 changed files with 3253832 additions and 0 deletions
+224
View File
@@ -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}"]
}
}
+80
View File
@@ -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)"
}
+150
View File
@@ -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.'"
]
}
}
+155
View File
@@ -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.'"
]
}
}