Skip to content

Commit

Permalink
ci: add scripts for building macOS images (#14743)
Browse files Browse the repository at this point in the history
  • Loading branch information
Electroid authored Oct 22, 2024
1 parent b9240f6 commit 4044ff7
Show file tree
Hide file tree
Showing 12 changed files with 1,397 additions and 0 deletions.
84 changes: 84 additions & 0 deletions ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# CI

This directory contains scripts for building CI images for Bun.

## Building

### `macOS`

On macOS, images are built using [`tart`](https://tart.run/), a tool that abstracts over the [`Virtualization.Framework`](https://developer.apple.com/documentation/virtualization) APIs, to run macOS VMs.

To install the dependencies required, run:

```sh
$ cd ci
$ bun run bootstrap
```

To build a vanilla macOS VM, run:

```sh
$ bun run build:darwin-aarch64-vanilla
```

This builds a vanilla macOS VM with the current macOS release on your machine. It runs scripts to disable things like spotlight and siri, but it does not install any software.

> Note: The image size is 50GB, so make sure you have enough disk space.
If you want to build a specific macOS release, you can run:

```sh
$ bun run build:darwin-aarch64-vanilla-15
```

> Note: You cannot build a newer release of macOS on an older macOS machine.
To build a macOS VM with software installed to build and test Bun, run:

```sh
$ bun run build:darwin-aarch64
```

## Running

### `macOS`

## How To

### Support a new macOS release

1. Visit [`ipsw.me`](https://ipsw.me/VirtualMac2,1) and find the IPSW of the macOS release you want to build.

2. Add an entry to [`ci/darwin/variables.pkr.hcl`](/ci/darwin/variables.pkr.hcl) with the following format:

```hcl
sonoma = {
distro = "sonoma"
release = "15"
ipsw = "https://updates.cdn-apple.com/..."
}
```

3. Add matching scripts to [`ci/package.json`](/ci/package.json) to build the image, then test it:

```sh
$ bun run build:darwin-aarch64-vanilla-15
```

> Note: If you need to troubleshoot the build, you can remove the `headless = true` property from [`ci/darwin/image-vanilla.pkr.hcl`](/ci/darwin/image-vanilla.pkr.hcl) and the VM's screen will be displayed.
4. Test and build the non-vanilla image:

```sh
$ bun run build:darwin-aarch64-15
```

This will use the vanilla image and run the [`scripts/bootstrap.sh`](/scripts/bootstrap.sh) script to install the required software to build and test Bun.

5. Publish the images:

```sh
$ bun run login
$ bun run publish:darwin-aarch64-vanilla-15
$ bun run publish:darwin-aarch64-15
```
46 changes: 46 additions & 0 deletions ci/darwin/image-vanilla.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generates a vanilla macOS VM with optimized settings for virtualized environments.
# See login.sh and optimize.sh for details.

data "external-raw" "boot-script" {
program = ["sh", "-c", templatefile("scripts/boot-image.sh", var)]
}

source "tart-cli" "bun-darwin-aarch64-vanilla" {
vm_name = "bun-darwin-aarch64-vanilla-${local.release.distro}-${local.release.release}"
from_ipsw = local.release.ipsw
cpu_count = local.cpu_count
memory_gb = local.memory_gb
disk_size_gb = local.disk_size_gb
ssh_username = local.username
ssh_password = local.password
ssh_timeout = "120s"
create_grace_time = "30s"
boot_command = split("\n", data.external-raw.boot-script.result)
headless = true # Disable if you need to debug why the boot_command is not working
}

build {
sources = ["source.tart-cli.bun-darwin-aarch64-vanilla"]

provisioner "file" {
content = file("scripts/setup-login.sh")
destination = "/tmp/setup-login.sh"
}

provisioner "shell" {
inline = ["echo \"${local.password}\" | sudo -S sh -c 'sh /tmp/setup-login.sh \"${local.username}\" \"${local.password}\"'"]
}

provisioner "file" {
content = file("scripts/optimize-machine.sh")
destination = "/tmp/optimize-machine.sh"
}

provisioner "shell" {
inline = ["sudo sh /tmp/optimize-machine.sh"]
}

provisioner "shell" {
inline = ["sudo rm -rf /tmp/*"]
}
}
44 changes: 44 additions & 0 deletions ci/darwin/image.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generates a macOS VM with software installed to build and test Bun.

source "tart-cli" "bun-darwin-aarch64" {
vm_name = "bun-darwin-aarch64-${local.release.distro}-${local.release.release}"
vm_base_name = "bun-darwin-aarch64-vanilla-${local.release.distro}-${local.release.release}"
cpu_count = local.cpu_count
memory_gb = local.memory_gb
disk_size_gb = local.disk_size_gb
ssh_username = local.username
ssh_password = local.password
ssh_timeout = "120s"
headless = true
}

build {
sources = ["source.tart-cli.bun-darwin-aarch64"]

provisioner "file" {
content = file("../../scripts/bootstrap.sh")
destination = "/tmp/bootstrap.sh"
}

provisioner "shell" {
inline = ["CI=true sh /tmp/bootstrap.sh"]
}

provisioner "file" {
source = "darwin/plists/"
destination = "/tmp/"
}

provisioner "shell" {
inline = [
"sudo ls /tmp/",
"sudo mv /tmp/*.plist /Library/LaunchDaemons/",
"sudo chown root:wheel /Library/LaunchDaemons/*.plist",
"sudo chmod 644 /Library/LaunchDaemons/*.plist",
]
}

provisioner "shell" {
inline = ["sudo rm -rf /tmp/*"]
}
}
44 changes: 44 additions & 0 deletions ci/darwin/plists/buildkite-agent.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.buildkite.buildkite-agent</string>

<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/buildkite-agent</string>
<string>start</string>
</array>

<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false />
</dict>

<key>RunAtLoad</key>
<true />

<key>StandardOutPath</key>
<string>/var/buildkite-agent/logs/buildkite-agent.log</string>

<key>StandardErrorPath</key>
<string>/var/buildkite-agent/logs/buildkite-agent.log</string>

<key>EnvironmentVariables</key>
<dict>
<key>BUILDKITE_AGENT_CONFIG</key>
<string>/etc/buildkite-agent/buildkite-agent.cfg</string>
</dict>

<key>LimitLoadToSessionType</key>
<array>
<string>Aqua</string>
<string>LoginWindow</string>
<string>Background</string>
<string>StandardIO</string>
<string>System</string>
</array>
</dict>
</plist>
20 changes: 20 additions & 0 deletions ci/darwin/plists/tailscale.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.tailscale.tailscaled</string>

<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/tailscale</string>
<string>up</string>
<string>--ssh</string>
<string>--authkey</string>
<string>${TAILSCALE_AUTHKEY}</string>
</array>

<key>RunAtLoad</key>
<true />
</dict>
</plist>
16 changes: 16 additions & 0 deletions ci/darwin/plists/tailscaled.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.tailscale.tailscaled</string>

<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/tailscaled</string>
</array>

<key>RunAtLoad</key>
<true />
</dict>
</plist>
124 changes: 124 additions & 0 deletions ci/darwin/scripts/boot-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/bin/sh

# This script generates the boot commands for the macOS installer GUI.
# It is run on your local machine, not inside the VM.

# Sources:
# - https://github.com/cirruslabs/macos-image-templates/blob/master/templates/vanilla-sequoia.pkr.hcl

if ! [ "${release}" ] || ! [ "${username}" ] || ! [ "${password}" ]; then
echo "Script must be run with variables: release, username, and password" >&2
exit 1
fi

# Hello, hola, bonjour, etc.
echo "<wait120s><spacebar>"

# Select Your Country and Region
echo "<wait30s>italiano<esc>english<enter>"
echo "<wait30s>united states<leftShiftOn><tab><leftShiftOff><spacebar>"

# Written and Spoken Languages
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"

# Accessibility
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"

# Data & Privacy
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"

# Migration Assistant
echo "<wait30s><tab><tab><tab><spacebar>"

# Sign In with Your Apple ID
echo "<wait30s><leftShiftOn><tab><leftShiftOff><leftShiftOn><tab><leftShiftOff><spacebar>"

# Are you sure you want to skip signing in with an Apple ID?
echo "<wait30s><tab><spacebar>"

# Terms and Conditions
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"

# I have read and agree to the macOS Software License Agreement
echo "<wait30s><tab><spacebar>"

# Create a Computer Account
echo "<wait30s>${username}<tab><tab>${password}<tab>${password}<tab><tab><tab><spacebar>"

# Enable Location Services
echo "<wait60s><leftShiftOn><tab><leftShiftOff><spacebar>"

# Are you sure you don't want to use Location Services?
echo "<wait30s><tab><spacebar>"

# Select Your Time Zone
echo "<wait30s><tab>UTC<enter><leftShiftOn><tab><leftShiftOff><spacebar>"

# Analytics
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"

# Screen Time
echo "<wait30s><tab><spacebar>"

# Siri
echo "<wait30s><tab><spacebar><leftShiftOn><tab><leftShiftOff><spacebar>"

# Choose Your Look
echo "<wait30s><leftShiftOn><tab><leftShiftOff><spacebar>"

if [ "${release}" = "13" ] || [ "${release}" = "14" ]; then
# Enable Voice Over
echo "<wait30s><leftAltOn><f5><leftAltOff><wait5s>v"
else
# Welcome to Mac
echo "<wait30s><spacebar>"

# Enable Keyboard navigation
echo "<wait30s><leftAltOn><spacebar><leftAltOff>Terminal<enter>"
echo "<wait30s>defaults write NSGlobalDomain AppleKeyboardUIMode -int 3<enter>"
echo "<wait30s><leftAltOn>q<leftAltOff>"
fi

# Now that the installation is done, open "System Settings"
echo "<wait30s><leftAltOn><spacebar><leftAltOff>System Settings<enter>"

# Navigate to "Sharing"
echo "<wait30s><leftAltOn>f<leftAltOff>sharing<enter>"

if [ "${release}" = "13" ]; then
# Navigate to "Screen Sharing" and enable it
echo "<wait30s><tab><down><spacebar>"

# Navigate to "Remote Login" and enable it
echo "<wait30s><tab><tab><tab><tab><tab><tab><spacebar>"

# Open "Remote Login" details
echo "<wait30s><tab><spacebar>"

# Enable "Full Disk Access"
echo "<wait30s><tab><spacebar>"

# Click "Done"
echo "<wait30s><leftShiftOn><tab><leftShiftOff><leftShiftOn><tab><leftShiftOff><spacebar>"

# Disable Voice Over
echo "<leftAltOn><f5><leftAltOff>"
elif [ "${release}" = "14" ]; then
# Navigate to "Screen Sharing" and enable it
echo "<wait30s><tab><tab><tab><tab><tab><spacebar>"

# Navigate to "Remote Login" and enable it
echo "<wait30s><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><spacebar>"

# Disable Voice Over
echo "<wait30s><leftAltOn><f5><leftAltOff>"
elif [ "${release}" = "15" ]; then
# Navigate to "Screen Sharing" and enable it
echo "<wait30s><tab><tab><tab><tab><tab><spacebar>"

# Navigate to "Remote Login" and enable it
echo "<wait30s><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><tab><spacebar>"
fi

# Quit System Settings
echo "<wait30s><leftAltOn>q<leftAltOff>"
Loading

0 comments on commit 4044ff7

Please sign in to comment.