From fa2742a75d017c426a435d1ba58eb310cd9c792c Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Fri, 6 Nov 2026 12:35:30 -0800 Subject: [PATCH] feat(e2e): scripts that build the golden image of the macOS guest and its programs build-golden.sh makes the golden Tart image from a vanilla one of Cirrus Labs: it provisions the guest over SSH (provision.sh: the agent starts at login from a Terminal window, no state of earlier runs is left, Terminal does not reopen old windows), stops for the one step that cannot be scripted (a person allows Screen Recording and Accessibility for Terminal, once, and the grants live in every clone), checks that the agent answers, and shuts the guest down from inside, so that the last writes are on the disk the clones copy. build-bins.sh builds the programs under test for the host and points the libiconv of the nix store at the system's, as the nix package does. --- e2e/platforms/tart_macos/build-bins.sh | 36 +++++++++++ e2e/platforms/tart_macos/build-golden.sh | 80 ++++++++++++++++++++++++ e2e/platforms/tart_macos/provision.sh | 40 ++++++++++++ 3 files changed, 156 insertions(+) create mode 100755 e2e/platforms/tart_macos/build-bins.sh create mode 100755 e2e/platforms/tart_macos/build-golden.sh create mode 100755 e2e/platforms/tart_macos/provision.sh diff --git a/e2e/platforms/tart_macos/build-bins.sh b/e2e/platforms/tart_macos/build-bins.sh new file mode 100755 index 00000000..158389dc --- /dev/null +++ b/e2e/platforms/tart_macos/build-bins.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# Builds the programs the macOS guest runs and puts them in BIN_DIR, ready to upload: +# +# build-bins.sh BIN_DIR [cargo args, e.g. --release] +# +# komorebi, komorebic, the test window and the agent, built for the host (the guest is the same +# architecture). A build in the dev shell of the flake links the libiconv of the nix store, which the +# guest does not have, so the library is pointed at the system's, as the nix package does, and the +# programs are signed again, as the system asks of a changed one. +set -eu + +bin_dir=${1:?usage: build-bins.sh BIN_DIR [cargo args]} +shift +root=$(cd "$(dirname "$0")/../../.." && pwd) +cd "$root" + +# the default dev shell builds for Windows: this is for the host +unset CARGO_BUILD_TARGET +profile=debug +case " $* " in *" --release "*) profile=release ;; esac + +COMMIT_HASH=${COMMIT_HASH:-$(git rev-parse HEAD 2>/dev/null || echo dev)} \ + cargo build --locked -p komorebi -p komorebic -p komorebi-testwin -p komorebi-e2e-agent "$@" + +mkdir -p "$bin_dir" +for bin in komorebi komorebic komorebi-testwin komorebi-e2e-agent; do + rm -f "$bin_dir/$bin" + cp "target/$profile/$bin" "$bin_dir/$bin" + chmod u+w "$bin_dir/$bin" + for lib in $(otool -L "$bin_dir/$bin" | grep -o '/nix/store/[^ ]*' || true); do + install_name_tool -change "$lib" "/usr/lib/$(basename "$lib" | sed 's/\.[0-9.]*dylib/.dylib/')" "$bin_dir/$bin" + done + codesign -f -s - "$bin_dir/$bin" 2>/dev/null +done +git rev-parse HEAD > "$bin_dir/commit" 2>/dev/null || true +echo "build-bins: $bin_dir" diff --git a/e2e/platforms/tart_macos/build-golden.sh b/e2e/platforms/tart_macos/build-golden.sh new file mode 100755 index 00000000..1b7214f7 --- /dev/null +++ b/e2e/platforms/tart_macos/build-golden.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# Builds the golden image of the macOS guest from a vanilla Tart image of Cirrus Labs. +# +# build-golden.sh BIN_DIR [GOLDEN [VANILLA]] +# +# BIN_DIR is where `build-bins.sh` put komorebi-e2e-agent. GOLDEN is the name of the image to make +# (komorebi-e2e-golden), VANILLA the image to start from (the latest macOS of Cirrus Labs). +# +# One step cannot be scripted, and is the reason for a golden image: macOS asks a person to allow +# Screen Recording and Accessibility, once, for the program the agent runs under (Terminal). The +# grants live in the image and survive every clone of it. The script stops for it. +# +# It needs `tart` (KOMOREBI_E2E_TART or the path) and `sshpass` (the vanilla images log in with +# admin/admin), and about 30 GB of disk. +set -eu + +bin_dir=${1:?usage: build-golden.sh BIN_DIR [GOLDEN [VANILLA]]} +golden=${2:-komorebi-e2e-golden} +vanilla=${3:-ghcr.io/cirruslabs/macos-tahoe-vanilla:latest} +tart=${KOMOREBI_E2E_TART:-tart} +here=$(cd "$(dirname "$0")" && pwd) + +if "$tart" list | grep -q " $golden "; then + echo "build-golden: $golden exists; delete it first (tart delete $golden)" >&2 + exit 1 +fi + +"$tart" clone "$vanilla" "$golden" +# the display of every guest is this size, which the scenarios and the work area rely on +"$tart" set "$golden" --display 1280x800 --cpu 4 --memory 8192 + +# a window, because a person has to use it +"$tart" run "$golden" > "${TMPDIR:-/tmp}/$golden-run.log" 2>&1 & +run=$! +ip=$("$tart" ip "$golden" --wait 120) +echo "build-golden: $golden is at $ip" + +# a throwaway guest: its key is new every time, and the password is the one of the vanilla image +guest() { + sshpass -p admin ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + -o LogLevel=ERROR "admin@$ip" "$@" +} +copy_to_guest() { + sshpass -p admin scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ + -o LogLevel=ERROR "$@" +} +i=0 +until guest true 2>/dev/null; do + i=$((i + 1)); [ "$i" -lt 60 ] || { echo "build-golden: no ssh" >&2; exit 1; } + sleep 2 +done + +guest 'mkdir -p ~/e2e/bin' +copy_to_guest "$here/provision.sh" "$bin_dir/komorebi-e2e-agent" "admin@$ip:/Users/admin/e2e/bin/" +guest 'sh ~/e2e/bin/provision.sh && rm ~/e2e/bin/provision.sh && chmod +x ~/e2e/bin/komorebi-e2e-agent' + +cat <<'MSG' + +build-golden: now, in the window of the guest: + 1. open Terminal and run: ~/e2e/agent.command + 2. allow Screen Recording and Accessibility for Terminal when asked + (System Settings, Privacy & Security; the password of the account is admin) + 3. when the agent runs without the prompts coming back, press Enter here. +MSG +read -r _ + +# the agent has to answer, or what was granted is not what a clone will run +python3 - "$ip" <<'PY' +import json, socket, sys +s = socket.create_connection((sys.argv[1], 7878), timeout=10) +f = s.makefile("rwb") +f.write(b'{"id": 1, "verb": "displays"}\n'); f.flush() +print("build-golden: the agent answers:", json.loads(f.readline()).get("ok") is not None) +PY + +# a clean shutdown from inside, so that everything written is on the disk the clones copy: a +# `tart stop` of a running guest loses the last writes +guest 'sync; echo admin | sudo -S shutdown -h now' || true +wait "$run" || true +echo "build-golden: done; $golden is stopped and ready" diff --git a/e2e/platforms/tart_macos/provision.sh b/e2e/platforms/tart_macos/provision.sh new file mode 100755 index 00000000..4e65e969 --- /dev/null +++ b/e2e/platforms/tart_macos/provision.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Runs in the guest, as the logged-in user, to make it what a scenario clones: the agent starts at +# login from a Terminal window (which holds the grants, so the agent and the daemon it starts are +# attributed to it), nothing from earlier runs is left, and Terminal does not reopen old windows. +set -eu + +pkill -x komorebi || true +pkill -f komorebi-e2e-agent || true +pkill -f komorebi-testwin || true +killall Terminal 2>/dev/null || true +sleep 1 + +rm -rf "$HOME/Library/Saved Application State/com.apple.Terminal.savedState" \ + "$HOME/Library/Application Support/komorebi" +defaults write com.apple.Terminal NSQuitAlwaysKeepsWindows -bool false +defaults write -g ApplePersistenceIgnoreState -bool true + +mkdir -p "$HOME/e2e/bin" "$HOME/e2e/tmp" "$HOME/Library/LaunchAgents" + +# what the launch agent opens: a Terminal window that runs the agent +cat > "$HOME/e2e/agent.command" < "$HOME/Library/LaunchAgents/dev.komorebi.e2e-agent.plist" < + + + Labeldev.komorebi.e2e-agent + ProgramArguments + /usr/bin/open-aTerminal + $HOME/e2e/agent.command + + RunAtLoad + LimitLoadToSessionTypeAqua + +PLIST +plutil -lint "$HOME/Library/LaunchAgents/dev.komorebi.e2e-agent.plist"