ci: prepare source-bound native macOS build and tests under TCG

This commit is contained in:
dh
2026-10-04 10:56:46 +02:00
parent c01ae13185
commit 17fb74dd74
9 changed files with 1495 additions and 27 deletions
+100
View File
@@ -0,0 +1,100 @@
#!/bin/bash
# Apple LaunchDaemon bootstrap before the guest .NET SDK exists.
# Runs only inside the isolated owned VM; installs CLT and expands the pinned SDK.
set -u
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
export PATH
PROOF_TOKEN="${1:-}"
STATE_DIR="/Volumes/installstate"
WORK="/private/var/tmp/meeting-assistant-native-$PROOF_TOKEN"
[[ "$PROOF_TOKEN" =~ ^[0-9a-f]{32}$ ]] || exit 1
[ "$(cat "$STATE_DIR/run.owner" 2>/dev/null)" = "$PROOF_TOKEN" ] || exit 1
exec >> "$STATE_DIR/firstboot.log" 2>&1
phase() {
printf '{"token":"%s","phase":"%s","updatedUtc":"%s"}\n' "$PROOF_TOKEN" "$1" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$STATE_DIR/guest-phase.json.tmp"
mv -f "$STATE_DIR/guest-phase.json.tmp" "$STATE_DIR/guest-phase.json"
}
fail() { printf '[firstboot] ERROR: %s\n' "$1"; phase bootstrap-failed; exit 1; }
require_installed_macos_version() {
# Numeric sw_vers product release with a major version of at least 14.
[[ "${1:-}" =~ ^(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,})\.[0-9]+(\.[0-9]+)?$ ]]
}
phase toolchain-installing
echo '[firstboot] verifying installed OS, APFS and owned physical store'
uname -a; id
installed_version=$(/usr/bin/sw_vers -productVersion) || fail installed_os_version_failed
require_installed_macos_version "$installed_version" || fail installed_macos_14_or_newer_required
printf '[firstboot] installed macOS version=%s\n' "$installed_version"
[ "$(id -u)" = 0 ] && [ "$(uname -m)" = x86_64 ] || fail wrong_guest_platform
/usr/sbin/diskutil info -plist / > "$STATE_DIR/installed-root.plist" || fail root_diskutil_failed
[ "$(/usr/libexec/PlistBuddy -c 'Print :FilesystemType' "$STATE_DIR/installed-root.plist")" = apfs ] || fail root_is_not_installed_apfs
container=$(/usr/libexec/PlistBuddy -c 'Print :APFSContainerReference' "$STATE_DIR/installed-root.plist") || fail root_container_missing
/usr/sbin/diskutil apfs list -plist > "$STATE_DIR/apfs-containers.plist" || fail apfs_list_failed
index=0
physical=""
while reference=$(/usr/libexec/PlistBuddy -c "Print :Containers:$index:ContainerReference" "$STATE_DIR/apfs-containers.plist" 2>/dev/null); do
if [ "$reference" = "$container" ]; then
[ -z "$physical" ] || fail ambiguous_root_containers
physical=$(/usr/libexec/PlistBuddy -c "Print :Containers:$index:PhysicalStores:0:DeviceIdentifier" "$STATE_DIR/apfs-containers.plist") || fail physical_store_missing
/usr/libexec/PlistBuddy -c "Print :Containers:$index:PhysicalStores:1" "$STATE_DIR/apfs-containers.plist" >/dev/null 2>&1 && fail multiple_physical_stores
fi
index=$((index + 1))
done
[[ "$physical" =~ ^disk[0-9]+s[0-9]+$ ]] || fail invalid_physical_store
/usr/sbin/diskutil info -plist "/dev/$physical" > "$STATE_DIR/physical-store.plist" || fail physical_store_info_failed
whole=$(/usr/libexec/PlistBuddy -c 'Print :ParentWholeDisk' "$STATE_DIR/physical-store.plist") || fail physical_parent_missing
. "$STATE_DIR/macos-native-disk-guard.sh"
verify_owned_disk "/dev/$whole" "$STATE_DIR" "$PROOF_TOKEN" || fail installed_root_is_not_the_owned_64g_disk
# The phase has an independent 30-minute watchdog; host outer deadline is 180 minutes.
parent=$$
(
trap 'kill "$sleeper" 2>/dev/null || :; exit 0' TERM INT
# Toolchain watchdog: 30 minutes, also bounded by the host's 172-minute total.
sleep 1800 & sleeper=$!; wait "$sleeper"; kill -TERM "$parent" 2>/dev/null || :
) & watchdog=$!
clt_marker="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress"
trap 'rm -f "$clt_marker"; kill -TERM "$watchdog" 2>/dev/null || :; wait "$watchdog" 2>/dev/null || :' EXIT
trap 'fail toolchain_deadline_or_cancellation' TERM INT
[ ! -e "$WORK" ] || fail guest_work_directory_already_exists
mkdir -p "$WORK" || fail guest_work_directory_failed
printf '%s\n' "$PROOF_TOKEN" > "$WORK/run.owner" || fail guest_work_owner_failed
free_kib=$(df -Pk "$WORK" | awk 'NR==2 {print $4}')
[[ "$free_kib" =~ ^[0-9]+$ ]] && (( free_kib >= 8 * 1024 * 1024 )) || fail insufficient_existing_guest_free_space
echo '[firstboot] installing a compatible Apple CLT catalog entry without a GUI'
touch "$clt_marker" || fail clt_marker_failed
/usr/sbin/softwareupdate -l > "$STATE_DIR/clt-catalog.log" 2>&1 || fail clt_catalog_failed
label=$(grep -B 1 -E 'Command Line Tools' "$STATE_DIR/clt-catalog.log" | awk -F'*' '/^ *\*/ {print $2}' | sed -e 's/^ *Label: //' -e 's/^ *//' | sort -V | tail -n 1)
[ -n "$label" ] || fail no_compatible_headless_clt_label
printf '[firstboot] selected CLT: %s\n' "$label"
/usr/sbin/softwareupdate -i "$label" > "$STATE_DIR/clt-install.log" 2>&1 || fail clt_install_failed
/usr/bin/xcode-select --switch /Library/Developer/CommandLineTools || fail clt_switch_failed
/usr/sbin/pkgutil --pkg-info=com.apple.pkg.CLTools_Executables || fail clt_receipt_failed
/usr/bin/xcrun --find swiftc || fail swiftc_missing
/usr/bin/xcrun swiftc --version || fail swiftc_version_failed
{
/usr/bin/xcrun --sdk macosx --show-sdk-version &&
/usr/bin/xcrun --sdk macosx --show-sdk-path
} > "$STATE_DIR/clt-sdk.log" 2>&1 || fail clt_sdk_identity_failed
printf 'import AppKit\nimport AVFoundation\nimport ScreenCaptureKit\nimport EventKit\nimport WebKit\nprint("native SDK ready")\n' > "$WORK/toolchain-smoke.swift"
/usr/bin/xcrun swiftc -target x86_64-apple-macos13.0 "$WORK/toolchain-smoke.swift" -o "$WORK/toolchain-smoke" || fail native_framework_compile_failed
"$WORK/toolchain-smoke" || fail native_framework_execution_failed
rm -f "$clt_marker"
echo '[firstboot] validating and expanding the pinned .NET SDK on owned APFS'
expected_sdk=33401b4a2da8554e3306db6072ea8569d9fcc608509c271e0aa4b39e7cc432da3631f14e7e1e2445d67d72550d18ce44a8bbd2382a756867ad2edab6b1c963c0
actual_sdk=$(shasum -a 512 "$STATE_DIR/sdk.tar.gz" | awk '{print $1}')
[ "$actual_sdk" = "$expected_sdk" ] || fail sdk_hash_mismatch
mkdir "$WORK/dotnet" || fail sdk_directory_failed
tar -xzf "$STATE_DIR/sdk.tar.gz" -C "$WORK/dotnet" || fail sdk_extract_failed
[ "$("$WORK/dotnet/dotnet" --version)" = 10.0.401 ] || fail sdk_version_mismatch
# Guest C# owns build/test deadlines from this point; stop the CLT-only watchdog.
kill -TERM "$watchdog" 2>/dev/null || :
wait "$watchdog" 2>/dev/null || :
trap - TERM INT
"$WORK/dotnet/dotnet" run --file "$STATE_DIR/MacOsNativeGuest.cs" -- --run --state "$STATE_DIR" --work "$WORK"
result=$?
(( result == 0 )) || fail native_tests_failed
exit 0