-
Notifications
You must be signed in to change notification settings - Fork 352
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'use-packer-to-create-vm-image-for-release-build-system-…
…ios-355'
- Loading branch information
Showing
13 changed files
with
450 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# Creating new macOS VMs to build MullvadVPN iOS in a CI environment | ||
This guide assumes you are running on macOS. | ||
## Prerequisites | ||
In order to create VMs on the fly, we decided to use [tart](https://tart.run/) and [packer](https://developer.hashicorp.com/packer). | ||
|
||
The various scripts that run in the VM are written in bash with the help of [shellcheck](shellcheck.net). | ||
|
||
## VM requirements | ||
- You will need at least 60GB of available space on your VM host | ||
- You will need at least 8GB of available RAM on your VM host | ||
- You will need at least 4 CPU cores available on your VM host | ||
|
||
## How to install Tart | ||
- brew install `cirruslabs/cli/tart` | ||
|
||
## How to install Packer | ||
- brew tap `hashicorp/tap` | ||
- brew install `hashicorp/tap/packer` | ||
|
||
## How to install shellcheck | ||
- brew install `shellcheck` | ||
|
||
> [!IMPORTANT] | ||
> # Prerequisite setup before running packer | ||
> - Get a copy of the Xcode version you want to install on the VM in a xip format | ||
> - Copy that file into the folder named `vm_shared_folder` | ||
> - Open the file named `variables.pkrvars.hcl` | ||
> - Edit the variables named `xcode_version` and `xcode_xip_name` | ||
Here is an example of what to expect | ||
```bash | ||
% ls vm_shared_folder | ||
Xcode_15.0.1.xip | ||
% head -2 variables.pkrvars.hcl | ||
xcode_version = "15.1" | ||
xcode_xip_name = "Xcode_15.1.xip" | ||
``` | ||
|
||
### Sanity checks before running packer | ||
It is a good idea to keep logs, the `logs` folder is provided to that effect. | ||
Enable packer logs by setting the following environment variables (assuming your are running with `zsh`) | ||
- export `PACKER_LOG=1` | ||
- export `PACKER_LOG_PATH="logs/packer_logs.txt"` | ||
|
||
> [!NOTE] | ||
> The logs will be overwritten with each packer command you issue. | ||
You can then check that the templates are valid before running `packer` | ||
- packer inspect `-var-file="variables.pkrvars.hcl" install-build-dependencies.pkr.hcl` | ||
- packer validate `-var-file="variables.pkrvars.hcl" install-build-dependencies.pkr.hcl` | ||
|
||
You can make sure you are not missing any dependencies with the `init` command | ||
- packer init `install-vanilla-ventura.pkr.hcl` | ||
|
||
### Create the VM image via Packer | ||
Once your setup is ready, you just need one command to create a VM. And one more to install Xcode on it. | ||
- packer build `-var-file="variables.pkrvars.hcl" install-vanilla-ventura.pkr.hcl` | ||
|
||
### Install Xcode on the VM image via Packer | ||
- packer build `-var-file="variables.pkrvars.hcl" install-build-dependencies.pkr.hcl` | ||
|
||
> [!IMPORTANT] | ||
> At the time of writing this, `tart` does not support VM snapshotting. This means that any action taken by packer will be **permanent** on the VM. | ||
Make sure to properly clean up the VM before running packer commands again if something went wrong. | ||
You can look at the `cleanup.sh` script in the `scripts` folder to see what type of cleanup is ran in case things go wrong. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
packer { | ||
required_plugins { | ||
tart = { | ||
version = ">= 1.2.0" | ||
source = "github.com/cirruslabs/tart" | ||
} | ||
} | ||
} | ||
|
||
variable "shared_folder_path" { type = string } | ||
|
||
variable "xcode_version" { | ||
type = string | ||
|
||
validation { | ||
condition = can(regex("(\\d)+(\\.)?((\\d)+)?(\\.)?((\\d)+)?", var.xcode_version)) | ||
error_message = "Invalid Xcode version number. Example of a valid number: '15.0.1'." | ||
} | ||
} | ||
|
||
variable "vm_name" { type = string } | ||
|
||
variable "user_name" { type = string } | ||
|
||
variable "xcode_xip_name" { | ||
type = string | ||
|
||
validation { | ||
condition = can(regex("Xcode_(\\d)+(\\.)?((\\d)+)?(\\.)?((\\d)+)?\\.xip", var.xcode_xip_name)) | ||
error_message = "Invalid Xcode file name. Example of a valid file name: 'Xcode_15.0.1.xip'." | ||
} | ||
} | ||
|
||
source "tart-cli" "tart" { | ||
vm_name = "${var.vm_name}" | ||
ssh_password = "admin" | ||
ssh_username = "admin" | ||
ssh_timeout = "120s" | ||
disk_size_gb = 80 | ||
} | ||
|
||
build { | ||
sources = ["source.tart-cli.tart"] | ||
|
||
|
||
// Create a symlink for bash compatibility | ||
provisioner "shell" { | ||
script = "scripts/link-zprofile.sh" | ||
} | ||
|
||
// Install brew | ||
provisioner "shell" { | ||
environment_vars = [ | ||
"USER=${var.user_name}" | ||
] | ||
script = "scripts/install-brew.sh" | ||
} | ||
|
||
|
||
// Install required brew dependencies | ||
provisioner "shell" { | ||
script = "scripts/install-brew-dependencies.sh" | ||
} | ||
|
||
// Install rustup | ||
provisioner "shell" { | ||
script = "scripts/install-rustup.sh" | ||
} | ||
|
||
// Install go | ||
provisioner "shell" { | ||
script = "scripts/install-go.sh" | ||
} | ||
|
||
// Copy the local Xcode xip file to the VM | ||
provisioner "file" { | ||
source = "${var.shared_folder_path}/${var.xcode_xip_name}" | ||
destination = "/tmp/${var.xcode_xip_name}" | ||
} | ||
|
||
// Install Xcode via xcodes.app | ||
provisioner "shell" { | ||
|
||
environment_vars = [ | ||
"XCODE_VERSION=${var.xcode_version}", | ||
"XCODE_XIP_NAME=${var.xcode_xip_name}", | ||
"XCODE_SHARED_PATH=/tmp", | ||
] | ||
script = "scripts/install-xcode.sh" | ||
} | ||
|
||
// Delete the Xcode xip file to save some space | ||
provisioner "shell" { | ||
inline = [ | ||
"rm -f /tmp/${var.xcode_xip_name}" | ||
] | ||
} | ||
|
||
// Run the xcodebuild first launch prompt to automatically accept terms and conditions, and download the iOS runtime simulator | ||
provisioner "shell" { | ||
script = "scripts/run-xcode-first-launch.sh" | ||
} | ||
|
||
// Add Apple root certs | ||
provisioner "shell" { | ||
script = "scripts/add-apple-certs.sh" | ||
} | ||
|
||
// Remove everything in case of error | ||
error-cleanup-provisioner "shell" { | ||
script = "scripts/cleanup.sh" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
packer { | ||
required_plugins { | ||
tart = { | ||
version = ">= 1.2.0" | ||
source = "github.com/cirruslabs/tart" | ||
} | ||
} | ||
} | ||
|
||
variable "vm_name" { type = string } | ||
|
||
source "tart-cli" "tart" { | ||
# You can find macOS IPSW URLs on various websites like https://ipsw.me/ | ||
# and https://www.theiphonewiki.com/wiki/Beta_Firmware/Mac/13.x | ||
from_ipsw = "https://updates.cdn-apple.com/2023SummerFCS/fullrestores/042-43686/945D434B-DA5D-48DB-A558-F6D18D11AD69/UniversalMac_13.5.2_22G91_Restore.ipsw" | ||
vm_name = "${var.vm_name}" | ||
cpu_count = 4 | ||
memory_gb = 8 | ||
disk_size_gb = 60 | ||
ssh_password = "admin" | ||
ssh_username = "admin" | ||
ssh_timeout = "120s" | ||
boot_command = [ | ||
# hello, hola, bonjour, etc. | ||
"<wait60s><spacebar>", | ||
# Language: most of the times we have a list of "English"[1], "English (UK)", etc. with | ||
# "English" language already selected. If we type "english", it'll cause us to switch | ||
# to the "English (UK)", which is not what we want. To solve this, we switch to some other | ||
# language first, e.g. "Italiano" and then switch back to "English". We'll then jump to the | ||
# first entry in a list of "english"-prefixed items, which will be "English". | ||
# | ||
# [1]: should be named "English (US)", but oh well 🤷 | ||
"<wait30s>italiano<esc>english<enter>", | ||
# Select Your Country and Region | ||
"<wait30s>united states<leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Written and Spoken Languages | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Accessibility | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Data & Privacy | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Migration Assistant | ||
"<wait10s><tab><tab><tab><spacebar>", | ||
# Sign In with Your Apple ID | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Are you sure you want to skip signing in with an Apple ID? | ||
"<wait10s><tab><spacebar>", | ||
# Terms and Conditions | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# I have read and agree to the macOS Software License Agreement | ||
"<wait10s><tab><spacebar>", | ||
# Create a Computer Account | ||
"<wait10s>admin<tab><tab>admin<tab>admin<tab><tab><tab><spacebar>", | ||
# Enable Location Services | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Are you sure you don't want to use Location Services? | ||
"<wait10s><tab><spacebar>", | ||
# Select Your Time Zone | ||
"<wait10s><tab>UTC<enter><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Analytics | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Screen Time | ||
"<wait10s><tab><spacebar>", | ||
# Siri | ||
"<wait10s><tab><spacebar><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Choose Your Look | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Enable Voice Over | ||
"<wait10s><leftAltOn><f5><leftAltOff><wait5s><enter>", | ||
# Now that the installation is done, open "System Settings" | ||
"<wait10s><leftAltOn><spacebar><leftAltOff>System Settings<enter>", | ||
# Navigate to "Sharing" | ||
"<wait10s><leftAltOn>f<leftAltOff>screen sharing<enter>", | ||
# Navigate to "Screen Sharing" and enable it | ||
"<wait10s><tab><down><spacebar>", | ||
# Navigate to "Remote Login" and enable it | ||
"<wait10s><tab><tab><tab><tab><tab><tab><spacebar>", | ||
# Open "Remote Login" details | ||
"<wait10s><tab><spacebar>", | ||
# Enable "Full Disk Access" | ||
"<wait10s><tab><spacebar>", | ||
# Click "Done" | ||
"<wait10s><leftShiftOn><tab><leftShiftOff><leftShiftOn><tab><leftShiftOff><spacebar>", | ||
# Disable Voice Over | ||
"<leftAltOn><f5><leftAltOff>", | ||
] | ||
|
||
// A (hopefully) temporary workaround for Virtualization.Framework's | ||
// installation process not fully finishing in a timely manner | ||
create_grace_time = "30s" | ||
} | ||
|
||
build { | ||
sources = ["source.tart-cli.tart"] | ||
|
||
provisioner "shell" { | ||
inline = [ | ||
// Enable passwordless sudo | ||
"echo admin | sudo -S sh -c \"mkdir -p /etc/sudoers.d/; echo 'admin ALL=(ALL) NOPASSWD: ALL' | EDITOR=tee visudo /etc/sudoers.d/admin-nopasswd\"", | ||
// Enable auto-login | ||
// | ||
// See https://github.com/xfreebird/kcpassword for details. | ||
"echo '00000000: 1ced 3f4a bcbc ba2c caca 4e82' | sudo xxd -r - /etc/kcpassword", | ||
"sudo defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser admin", | ||
// Disable screensaver at login screen | ||
"sudo defaults write /Library/Preferences/com.apple.screensaver loginWindowIdleTime 0", | ||
// Disable screensaver for admin user | ||
"defaults -currentHost write com.apple.screensaver idleTime 0", | ||
// Prevent the VM from sleeping | ||
"sudo systemsetup -setdisplaysleep Off", | ||
"sudo systemsetup -setsleep Off", | ||
"sudo systemsetup -setcomputersleep Off", | ||
// Launch Safari to populate the defaults | ||
"/Applications/Safari.app/Contents/MacOS/Safari &", | ||
"sleep 30", | ||
"kill -9 %1", | ||
// Enable Safari's remote automation and "Develop" menu | ||
"sudo safaridriver --enable", | ||
"defaults write com.apple.Safari.SandboxBroker ShowDevelopMenu -bool true", | ||
"defaults write com.apple.Safari IncludeDevelopMenu -bool true", | ||
// Disable screen lock | ||
// | ||
// Note that this only works if the user is logged-in, | ||
// i.e. not on login screen. | ||
"sysadminctl -screenLock off -password admin", | ||
"defaults -currentHost write com.apple.screensaver idleTime 0" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/bin/bash | ||
# inspired by https://github.com/actions/runner-images/blob/fb3b6fd69957772c1596848e2daaec69eabca1bb/images/macos/provision/configuration/configure-machine.sh#L33-L61 | ||
|
||
sudo security delete-certificate -Z FF6797793A3CD798DC5B2ABEF56F73EDC9F83A64 /Library/Keychains/System.keychain | ||
|
||
curl -o AppleWWDRCAG3.cer https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer | ||
curl -o DeveloperIDG2CA.cer https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer | ||
sudo security add-certificates AppleWWDRCAG3.cer | ||
sudo security add-certificates DeveloperIDG2CA.cer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#!/bin/bash | ||
|
||
set -euo pipefail | ||
|
||
# shellcheck source=/dev/null | ||
source ~/.bash_profile | ||
|
||
|
||
# Uninstall rust | ||
# shellcheck source=/dev/null | ||
if [[ -f "${HOME}/.cargo/env" ]] | ||
then | ||
source "${HOME}/.cargo/env" | ||
yes | rustup self uninstall | ||
fi | ||
|
||
# Uninstall brew (This should also delete all dependencies) | ||
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/uninstall.sh)" | ||
# Clean up folders that were not automatically removed | ||
sudo rm -rf /opt/homebrew | ||
|
||
# Remove the custom profiles | ||
rm -f ~/.zprofile ~/.profile ~/.bash_profile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/bin/bash | ||
|
||
set -euo pipefail | ||
|
||
# shellcheck source=/dev/null | ||
source ~/.bash_profile | ||
|
||
if command -v brew &>/dev/null | ||
then | ||
echo "Installing xcodes" | ||
brew install xcodesorg/made/xcodes | ||
echo "Installing xcodes" | ||
brew install bash | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#!/bin/bash | ||
|
||
set -euo pipefail | ||
|
||
if command -v brew &>/dev/null | ||
then | ||
echo >&1 "brew is already installed, nothing to do here" | ||
exit 0 | ||
fi | ||
|
||
echo >&1 "installing brew" | ||
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | ||
# This is intentionally in single quotes for echo to append properly | ||
# shellcheck disable=SC2016 | ||
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.bash_profile | ||
eval "$(/opt/homebrew/bin/brew shellenv)" | ||
|
||
# shellcheck source=/dev/null | ||
source ~/.bash_profile | ||
brew update |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#!/bin/bash | ||
|
||
set -euo pipefail | ||
|
||
# shellcheck source=/dev/null | ||
source ~/.bash_profile | ||
|
||
if command -v brew &>/dev/null | ||
then | ||
echo >&1 "Installing [email protected]" | ||
brew install [email protected] | ||
echo "export PATH='/opt/homebrew/opt/[email protected]/bin:$PATH'" >> ~/.bash_profile | ||
fi |
Oops, something went wrong.