Add Tahoe runner Xcode 26.6 and beta 2 (#356)

* Add Tahoe runner Xcode 26.6 and beta 2

* Add PR template validation workflow

* Install Ansible for template validation

* Build template images in PR workflow

* Authenticate Packer plugin downloads

* Prepare Xcode archives for PR builds

* Support authenticated Xcode archive downloads

* Use Xcode 27 beta 2 version token

* Increase Tahoe runner disk size

* Select Xcode apps by path during runner builds

* Update Homebrew before Tuist install

* Avoid Tuist cask validation during image builds

* Trust Tuist formula during image builds

* Add Tahoe iOS 27 beta runtime expectation
This commit is contained in:
Fedor Korotkov 2026-07-04 22:21:50 -04:00 committed by GitHub
parent 2be4479694
commit 71b405bd0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 408 additions and 28 deletions

View File

@ -104,11 +104,11 @@ jobs:
xcode_components: '"MetalToolchain"' xcode_components: '"MetalToolchain"'
disk_size: 380 disk_size: 380
- macos_version: tahoe - macos_version: tahoe
xcode_versions: '"26.5","27-beta","26.4.1","26.3","26.2","26.1.1","26.0.1"' xcode_versions: '"26.6","27-beta-2","26.5","27-beta","26.4.1","26.3","26.2","26.1.1","26.0.1"'
additional_ios_builds: "18.6" additional_ios_builds: "18.6"
additional_tvos_builds: "" additional_tvos_builds: ""
xcode_components: '"MetalToolchain"' xcode_components: '"MetalToolchain"'
disk_size: 330 disk_size: 520
env: env:
ADDITIONAL_IOS_BUILDS: ${{ matrix.additional_ios_builds }} ADDITIONAL_IOS_BUILDS: ${{ matrix.additional_ios_builds }}
ADDITIONAL_TVOS_BUILDS: ${{ matrix.additional_tvos_builds }} ADDITIONAL_TVOS_BUILDS: ${{ matrix.additional_tvos_builds }}

View File

@ -0,0 +1,380 @@
name: Template Builds
on:
pull_request:
paths:
- ".github/workflows/monthly.yml"
- ".github/workflows/release.yml"
- ".github/workflows/template-validation.yml"
- "data/**"
- "scripts/**"
- "templates/**"
permissions:
contents: read
packages: read
concurrency:
group: template-builds-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
FASTLANE_SESSION: ${{ secrets.FASTLANE_SESSION }}
FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TART_REGISTRY_HOSTNAME: ghcr.io
TART_REGISTRY_USERNAME: ${{ github.actor }}
TART_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
jobs:
packer-validate:
name: Packer Validate
runs-on: macos-15
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Packer
uses: hashicorp/setup-packer@v3
- name: Install validation dependencies
run: |
brew install ansible
- name: Prepare validation inputs
run: |
mkdir -p "$HOME/XcodesCache"
touch "$HOME/XcodesCache/Xcode_26.6.xip"
- name: Validate templates
run: |
set -euo pipefail
validate() {
local template="$1"
shift
packer init "$template"
packer validate "$@" "$template"
}
for template in templates/vanilla-*.pkr.hcl; do
validate "$template"
done
validate templates/base.pkr.hcl \
-var vm_name=template-validation-base
validate templates/disable-sip.pkr.hcl \
-var vm_name=template-validation-disable-sip
validate templates/disable-sip-with-username.pkr.hcl \
-var vm_name=template-validation-disable-sip-user
validate templates/exex-script.pkr.hcl \
-var vm_name=template-validation-exec \
-var script_path=scripts/finalize-tahoe.sh
validate templates/resolve-macos-number.pkr.hcl \
-var vm_base_name=template-validation-base \
-var vm_name=template-validation-resolve \
-var resolve_file=macos-version.txt
validate templates/xcode.pkr.hcl \
-var macos_version=tahoe \
-var 'xcode_version=["26.6"]' \
-var expected_runtimes_file=data/expected.tahoe.runtimes.txt
build-vanilla:
name: Build Vanilla Image (${{ matrix.macos_version }})
needs: packer-validate
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, macOS, ARM64]
timeout-minutes: 180
strategy:
fail-fast: false
max-parallel: 1
matrix:
macos_version:
- tahoe
- sequoia
- sonoma
- monterey
env:
MACOS_VERSION: ${{ matrix.macos_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Select image
id: select
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
git diff --name-only "$BASE_SHA" "$HEAD_SHA" > changed-files.txt
build=false
if grep -Fxq "templates/vanilla-$MACOS_VERSION.pkr.hcl" changed-files.txt; then
build=true
fi
echo "build=$build" >> "$GITHUB_OUTPUT"
- name: Tool versions
if: steps.select.outputs.build == 'true'
run: |
tart --version
packer --version
- name: Build vanilla image
if: steps.select.outputs.build == 'true'
run: |
packer init "templates/vanilla-$MACOS_VERSION.pkr.hcl"
packer build "templates/vanilla-$MACOS_VERSION.pkr.hcl"
- name: Cleanup
if: always() && steps.select.outputs.build == 'true'
run: |
tart delete "$MACOS_VERSION-vanilla" || true
build-base:
name: Build Base Image (${{ matrix.macos_version }})
needs: packer-validate
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, macOS, ARM64]
timeout-minutes: 180
strategy:
fail-fast: false
max-parallel: 1
matrix:
include:
- macos_version: sonoma
disable_sip_template: disable-sip.pkr.hcl
- macos_version: sequoia
disable_sip_template: disable-sip-with-username.pkr.hcl
- macos_version: tahoe
disable_sip_template: disable-sip-with-username.pkr.hcl
env:
DISABLE_SIP_TEMPLATE: ${{ matrix.disable_sip_template }}
MACOS_VERSION: ${{ matrix.macos_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Select image
id: select
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
git diff --name-only "$BASE_SHA" "$HEAD_SHA" > changed-files.txt
build=false
if grep -Eq '^(templates/base\.pkr\.hcl|templates/disable-sip.*\.pkr\.hcl|data/(github_known_hosts|limit\.maxfiles\.plist|setup-info-template\.json|tart-guest-.*\.plist)|scripts/install-actions-runner\.sh|ansible/)' changed-files.txt; then
build=true
fi
echo "build=$build" >> "$GITHUB_OUTPUT"
- name: Tool versions
if: steps.select.outputs.build == 'true'
run: |
tart --version
packer --version
- name: Pull vanilla image
if: steps.select.outputs.build == 'true'
run: |
tart pull "ghcr.io/cirruslabs/macos-$MACOS_VERSION-vanilla:latest"
tart clone "ghcr.io/cirruslabs/macos-$MACOS_VERSION-vanilla:latest" "$MACOS_VERSION-base"
- name: Disable SIP
if: steps.select.outputs.build == 'true'
run: |
packer init "templates/$DISABLE_SIP_TEMPLATE"
packer build -var "vm_name=$MACOS_VERSION-base" "templates/$DISABLE_SIP_TEMPLATE"
- name: Build base image
if: steps.select.outputs.build == 'true'
run: |
packer init templates/base.pkr.hcl
packer build -var "vm_name=$MACOS_VERSION-base" templates/base.pkr.hcl
- name: Cleanup
if: always() && steps.select.outputs.build == 'true'
run: |
tart delete "$MACOS_VERSION-base" || true
build-runner:
name: Build Runner Image (${{ matrix.macos_version }})
needs: packer-validate
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: [self-hosted, macOS, ARM64]
timeout-minutes: 180
strategy:
fail-fast: false
max-parallel: 1
matrix:
include:
- macos_version: tahoe
xcode_versions: '"26.6","27-beta-2","26.5","27-beta","26.4.1","26.3","26.2","26.1.1","26.0.1"'
additional_ios_builds: "18.6"
additional_tvos_builds: ""
xcode_components: '"MetalToolchain"'
disk_size: 520
- macos_version: sequoia
xcode_versions: '"26.0.1",16.4,16.3,16.2,16.1,16'
additional_ios_builds: "18.5,18.4,18.2,17.5"
additional_tvos_builds: "17.5"
xcode_components: '"MetalToolchain"'
disk_size: 380
env:
ADDITIONAL_IOS_BUILDS: ${{ matrix.additional_ios_builds }}
ADDITIONAL_TVOS_BUILDS: ${{ matrix.additional_tvos_builds }}
DISK_SIZE: ${{ matrix.disk_size }}
MACOS_VERSION: ${{ matrix.macos_version }}
XCODE_COMPONENTS: ${{ matrix.xcode_components }}
XCODE_VERSIONS: ${{ matrix.xcode_versions }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Select image
id: select
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
git diff --name-only "$BASE_SHA" "$HEAD_SHA" > changed-files.txt
build=false
if grep -Fxq ".github/workflows/release.yml" changed-files.txt; then
build=true
elif grep -Fxq "data/expected.$MACOS_VERSION.runtimes.txt" changed-files.txt; then
build=true
elif grep -Fxq "scripts/finalize-$MACOS_VERSION.sh" changed-files.txt; then
build=true
elif grep -Eq '^(templates/xcode\.pkr\.hcl|data/setup-info-template\.json|scripts/install-actions-runner\.sh)$' changed-files.txt; then
build=true
fi
echo "build=$build" >> "$GITHUB_OUTPUT"
- name: Tool versions
if: steps.select.outputs.build == 'true'
run: |
tart --version
packer --version
- name: Pull base image
if: steps.select.outputs.build == 'true'
run: |
tart pull "ghcr.io/cirruslabs/macos-$MACOS_VERSION-base:latest"
- name: Prepare Xcode archives
if: steps.select.outputs.build == 'true'
run: |
set -euo pipefail
source ~/.zprofile || true
if ! command -v xcodes >/dev/null; then
brew install xcodes
fi
mkdir -p "$HOME/XcodesCache"
IFS=',' read -ra versions <<< "$XCODE_VERSIONS"
for raw_version in "${versions[@]}"; do
version="${raw_version//\"/}"
target="$HOME/XcodesCache/Xcode_${version}.xip"
if [[ -f "$target" ]]; then
echo "Using cached Xcode $version at $target"
continue
fi
echo "Downloading Xcode $version"
if [[ -z "${FASTLANE_SESSION:-}" ]]; then
echo "::error::Missing $target and FASTLANE_SESSION is not configured. Pre-cache the Xcode archive on the runner or add Apple Developer auth secrets."
exit 1
fi
download_args=(download "$version" --directory "$HOME/XcodesCache" --use-fastlane-auth)
if [[ -n "${FASTLANE_USER:-}" ]]; then
download_args+=(--fastlane-user "$FASTLANE_USER")
fi
xcodes "${download_args[@]}"
candidate=""
case "$version" in
27-beta-2)
candidate="$HOME/XcodesCache/Xcode_27_beta_2.xip"
;;
27-beta)
candidate="$HOME/XcodesCache/Xcode_27_beta.xip"
;;
*)
candidate="$(find "$HOME/XcodesCache" -maxdepth 1 -type f -name "Xcode_${version}*.xip" -print -quit)"
;;
esac
if [[ -n "$candidate" && -f "$candidate" && "$candidate" != "$target" ]]; then
mv "$candidate" "$target"
fi
test -f "$target"
done
- name: Build runner image
if: steps.select.outputs.build == 'true'
run: |
packer init templates/xcode.pkr.hcl
packer build \
-var tag=runner \
-var "disk_size=$DISK_SIZE" \
-var disk_free_mb=100000 \
-var "macos_version=$MACOS_VERSION" \
-var "xcode_version=[$XCODE_VERSIONS]" \
-var "additional_ios_builds=[$ADDITIONAL_IOS_BUILDS]" \
-var "additional_tvos_builds=[$ADDITIONAL_TVOS_BUILDS]" \
-var "xcode_components=[$XCODE_COMPONENTS]" \
-var "expected_runtimes_file=data/expected.$MACOS_VERSION.runtimes.txt" \
templates/xcode.pkr.hcl
- name: Finalize runner image
if: steps.select.outputs.build == 'true'
run: |
if [[ -f "scripts/finalize-$MACOS_VERSION.sh" ]]; then
packer build \
-var "vm_name=$MACOS_VERSION-xcode:runner" \
-var "script_path=scripts/finalize-$MACOS_VERSION.sh" \
templates/exex-script.pkr.hcl
else
echo "Skipping prepare script for $MACOS_VERSION"
fi
- name: Cleanup
if: always() && steps.select.outputs.build == 'true'
run: |
tart delete "$MACOS_VERSION-xcode:runner" || true

View File

@ -5,12 +5,11 @@ iOS 26.1 (26.1 - 23B86) - com.apple.CoreSimulator.SimRuntime.iOS-26-1
iOS 26.3 (26.3.1 - 23D8133) - com.apple.CoreSimulator.SimRuntime.iOS-26-3 iOS 26.3 (26.3.1 - 23D8133) - com.apple.CoreSimulator.SimRuntime.iOS-26-3
iOS 26.4 (26.4.1 - 23E254a) - com.apple.CoreSimulator.SimRuntime.iOS-26-4 iOS 26.4 (26.4.1 - 23E254a) - com.apple.CoreSimulator.SimRuntime.iOS-26-4
iOS 26.5 (26.5 - 23F77) - com.apple.CoreSimulator.SimRuntime.iOS-26-5 iOS 26.5 (26.5 - 23F77) - com.apple.CoreSimulator.SimRuntime.iOS-26-5
tvOS 26.2 (26.2 - 23K51) - com.apple.CoreSimulator.SimRuntime.tvOS-26-2 iOS 27.0 (27.0 - 24A5355p) - com.apple.CoreSimulator.SimRuntime.iOS-27-0
tvOS 26.4 (26.4 - 23L243a) - com.apple.CoreSimulator.SimRuntime.tvOS-26-4 iOS 27.0 (27.0 - 24A5370g) - com.apple.CoreSimulator.SimRuntime.iOS-27-0
tvOS 26.5 (26.5 - 23L470) - com.apple.CoreSimulator.SimRuntime.tvOS-26-5 tvOS 26.5 (26.5 - 23L470) - com.apple.CoreSimulator.SimRuntime.tvOS-26-5
watchOS 26.2 (26.2 - 23S303) - com.apple.CoreSimulator.SimRuntime.watchOS-26-2 tvOS 27.0 (27.0 - 24J5305f) - com.apple.CoreSimulator.SimRuntime.tvOS-27-0
watchOS 26.4 (26.4 - 23T240b) - com.apple.CoreSimulator.SimRuntime.watchOS-26-4
watchOS 26.5 (26.5 - 23T570) - com.apple.CoreSimulator.SimRuntime.watchOS-26-5 watchOS 26.5 (26.5 - 23T570) - com.apple.CoreSimulator.SimRuntime.watchOS-26-5
visionOS 26.2 (26.2 - 23N301) - com.apple.CoreSimulator.SimRuntime.xrOS-26-2 watchOS 27.0 (27.0 - 24R5305f) - com.apple.CoreSimulator.SimRuntime.watchOS-27-0
visionOS 26.4 (26.4.1 - 23O249a) - com.apple.CoreSimulator.SimRuntime.xrOS-26-4
visionOS 26.5 (26.5 - 23O470) - com.apple.CoreSimulator.SimRuntime.xrOS-26-5 visionOS 26.5 (26.5 - 23O470) - com.apple.CoreSimulator.SimRuntime.xrOS-26-5
visionOS 27.0 (27.0 - 24M5306g) - com.apple.CoreSimulator.SimRuntime.xrOS-27-0

View File

@ -146,7 +146,7 @@ build {
} }
provisioner "file" { provisioner "file" {
sources = [ for version in var.xcode_version : pathexpand("~/XcodesCache/Xcode_${version}.xip")] sources = [for version in var.xcode_version : pathexpand("~/XcodesCache/Xcode_${version}.xip")]
destination = "/Users/admin/Downloads/" destination = "/Users/admin/Downloads/"
} }
@ -173,7 +173,7 @@ build {
content { content {
inline = [ inline = [
"source ~/.zprofile", "source ~/.zprofile",
"sudo xcodes select '${var.xcode_version[2]}'", "sudo xcode-select -s /Applications/Xcode_${var.xcode_version[2]}.app/Contents/Developer",
"xcodebuild -downloadAllPlatforms", "xcodebuild -downloadAllPlatforms",
] ]
} }
@ -185,7 +185,7 @@ build {
content { content {
inline = [ inline = [
"source ~/.zprofile", "source ~/.zprofile",
"sudo xcodes select '${var.xcode_version[1]}'", "sudo xcode-select -s /Applications/Xcode_${var.xcode_version[1]}.app/Contents/Developer",
"xcodebuild -downloadAllPlatforms", "xcodebuild -downloadAllPlatforms",
] ]
} }
@ -194,7 +194,7 @@ build {
provisioner "shell" { provisioner "shell" {
inline = [ inline = [
"source ~/.zprofile", "source ~/.zprofile",
"sudo xcodes select '${var.xcode_version[0]}'", "sudo xcode-select -s /Applications/Xcode_${var.xcode_version[0]}.app/Contents/Developer",
"xcodebuild -downloadAllPlatforms", "xcodebuild -downloadAllPlatforms",
] ]
} }
@ -232,8 +232,9 @@ build {
"brew install libimobiledevice ideviceinstaller ios-deploy carthage", "brew install libimobiledevice ideviceinstaller ios-deploy carthage",
"brew install xcbeautify swiftformat swiftlint swiftgen licenseplist", "brew install xcbeautify swiftformat swiftlint swiftgen licenseplist",
"brew install mint", "brew install mint",
"brew tap tuist/tuist", "git clone --depth 1 https://github.com/tuist/homebrew-tuist.git \"$(brew --repository)/Library/Taps/tuist/homebrew-tuist\"",
"brew install --formula tuist", "rm -rf \"$(brew --repository)/Library/Taps/tuist/homebrew-tuist/Casks\"",
"tuist_version=$(ruby -ne 'if $_ =~ %r{/download/([^/]+)/}; puts $1; exit; end' \"$(brew --repository)/Library/Taps/tuist/homebrew-tuist/Aliases/tuist\") && brew trust --formula \"tuist/tuist/tuist@$tuist_version\" && brew install --formula \"tuist/tuist/tuist@$tuist_version\"",
"rbenv install 3.3.10", "rbenv install 3.3.10",
"rbenv global 3.3.10", # fastlane conflicts with 3.4.0+ https://github.com/fastlane/fastlane/issues/29527 "rbenv global 3.3.10", # fastlane conflicts with 3.4.0+ https://github.com/fastlane/fastlane/issues/29527
"gem update", "gem update",