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"