1
0
Fork
You've already forked e2e
0
Runs your existing test/build commands inside a macOS VM over SSH + rsync, so UI automation stays isolated from your host desktop.
  • Swift 95.7%
  • Python 4.3%
2026年02月27日 07:09:07 +01:00
.codex/skills Add create-release skill for SemVer-tagged release flow 2026年02月25日 08:55:30 +01:00
Sources/e2e Add --dry-run / -n to exec 2026年02月25日 22:08:20 +01:00
Tests Add --dry-run / -n to exec 2026年02月25日 22:08:20 +01:00
.gitignore Initialize Swift package scaffold 2026年02月24日 22:31:22 +01:00
AGENTS.md Initialize Swift package scaffold 2026年02月24日 22:31:22 +01:00
CHANGELOG.md Update changelog for exec dry-run option 2026年02月27日 07:09:07 +01:00
CONTRIBUTING.md Update contributing guide for subprocess runtime and test paths 2026年02月25日 16:44:13 +01:00
LICENSE chore: add MIT license 2026年02月25日 09:05:18 +01:00
Package.resolved Record swift-subprocess dependency resolution 2026年02月25日 09:16:12 +01:00
Package.swift Add e2e doctor command with validation checks and tests 2026年02月25日 12:58:13 +01:00
README.md Improve config set error messages and documentation 2026年02月25日 21:47:14 +01:00
SPEC.md Add verbose flag and enforce SSH connect timeout 2026年02月25日 09:32:58 +01:00

e2e

"Automation Running"? Not on my machine!

e2e runs your existing test/build commands inside a macOS VM over SSH + rsync, so UI automation stays isolated from your host desktop.

Why use this

  • Keep host mouse/keyboard free while XCUITests run in the guest.
  • Reuse your existing project commands (make, xcodebuild, scripts).
  • Get deterministic orchestration: sync -> run -> pull artifacts.
  • Capture machine-readable JSONL traces for debugging and regression tests.

Quick start

e2e init # scaffold .e2e.env and e2e/excludes.rsync
nano .e2e.env # fill in SSH host, paths, etc.
e2e doctor # verify host, guest, and connectivity
e2e config # inspect resolved configuration
e2e exec -- make smoke # sync, run, pull artifacts

Commands

e2e init

e2e init

Creates starter config files without overwriting existing ones:

  • .e2e.env — template with all required and optional variables.
  • e2e/excludes.rsync — default rsync exclude patterns (.git/, .build/, .e2e.env, etc.).

Idempotent: existing files are preserved.

e2e doctor

e2e doctor [--env-file <path>] [--timeout-ssh <seconds>]

Runs a checklist that validates your setup end-to-end:

# Check What it does
1 config loaded parses config and checks required keys
2 host tools present looks for ssh, rsync, xcodebuild on PATH
3 host rsync --mkpath runs rsync --mkpath --version to verify support
4 SSH connectivity ssh <opts> user@host "echo ok"
5 guest tools reachable checks rsync, xcodebuild, mise over SSH
6 host xcodebuild readable runs xcodebuild -version locally
7 host/guest Xcode parity compares xcodebuild -version output; fails on mismatch

Output uses [x] / [ ] prefixes with hints on failure:

[x] config loaded
[x] host tool present: ssh
[x] host tool present: rsync
[ ] host tool present: xcodebuild
 hint: install xcodebuild and ensure it is on PATH before running e2e doctor

Exits 0 when all checks pass, 1 if any check fails.

e2e config

e2e config [--env-file <path>] [<key>]

Prints the resolved configuration after merging dotenv, environment variables, and defaults.

  • With no arguments, prints all KEY=value pairs (one per line).
  • With a key name, prints just the value for that key.

Output goes to stdout so it's pipeable:

e2e config # all keys
e2e config E2E_SSH_HOST # just the value
HOST=$(e2e config E2E_SSH_HOST) # capture in a variable
e2e config --env-file .e2e.local # use alternate env file

e2e config set

e2e config set [--env-file <path>] [KEY VALUE]

Updates a configuration key in the dotenv file.

  • With KEY VALUE, writes the specified value non-interactively.
  • With no arguments, walks through every configuration key in order, showing the current value as the default. Press Enter to keep it, or type a new value. Only keys whose values change are written back to the file.

Validates types: integers for port/timeout keys, booleans for toggle keys.

e2e config set E2E_SSH_HOST vm.local # set a single key
e2e config set E2E_SSH_PORT 2222 # validated as integer
e2e config set --env-file .e2e.local E2E_SSH_PORT 2222
e2e config set # interactive walkthrough

e2e exec

e2e exec [options] -- <command...>

The main command. Syncs your project to the guest, runs a command there, and pulls artifacts back.

Options:

Flag Default Description
--no-clear-out clears skip clearing E2E_GUEST_OUT before execution
--verbose off print the SSH command being executed
--env-file <path> .e2e.env path to dotenv config file
--timeout-ssh <seconds> 5 SSH connection timeout

Pipeline phases:

  1. Preflight — SSH connectivity check (ssh ... "echo ok").
  2. Sync host → guestrsync the project from E2E_HOST_ROOT to E2E_GUEST_ROOT.
  3. Clear guest outputrm -rf + mkdir -p on E2E_GUEST_OUT (unless --no-clear-out).
  4. Guest exec — runs your command via ssh ... "cd <guest_root> && <command>".
  5. Pull guest → hostrsync artifacts from E2E_GUEST_OUT to E2E_HOST_OUT.

Pull always runs even if the guest command fails, so you still get test artifacts. If the guest command succeeded but pull fails, the pull exit code is returned. If the guest command failed, its exit code is returned regardless of pull outcome.

Status lines go to stderr:

[e2e] preflight completed in 0.42s
[e2e] sync host->guest completed in 2.34s
[e2e] guest_exec failed in 18.91s
[e2e] pull guest->host completed in 0.87s

Examples:

e2e exec -- make smoke
e2e exec --no-clear-out --verbose -- make xctest
e2e exec --env-file .e2e.local --timeout-ssh 10 -- make smoke

e2e provision

e2e provision --script <path> [--env-file <path>] [--timeout-ssh <seconds>]

Runs a provisioning script on the guest. The script must be inside E2E_HOST_ROOT so it gets synced over before execution.

Under the hood this is e2e exec with:

  • the command set to bash <relative-script-path>
  • guest output clearing disabled
e2e provision --script setup/bootstrap.sh

Exit codes

Code Meaning
0 success
1 runtime or config error
2 usage / argument error

For exec, the guest command's exit code is forwarded directly. For config set, exit code 2 indicates an unknown key and exit code 1 indicates an invalid value or a write failure.

Configuration

Load order (highest priority first):

  1. CLI flags
  2. Process environment
  3. .e2e.env (or --env-file path)

Required keys:

Key Description
E2E_SSH_HOST hostname or IP of guest VM
E2E_SSH_USER SSH username
E2E_HOST_ROOT local project directory to sync
E2E_GUEST_ROOT guest-side project directory
E2E_GUEST_OUT guest-side output/artifact directory
E2E_HOST_OUT local directory to pull artifacts into

Optional keys:

Key Default Description
E2E_SSH_PORT 22 SSH port
E2E_SSH_OPTS -o BatchMode=yes -o StrictHostKeyChecking=accept-new extra SSH flags
E2E_RSYNC_OPTS -az --delete --mkpath rsync flags
E2E_RSYNC_EXCLUDES comma-separated exclude patterns
E2E_RSYNC_EXCLUDE_FILE e2e/excludes.rsync if present path for --exclude-from
E2E_CLEAR_GUEST_OUT 1 clear guest output before exec (0/1)
E2E_TIMEOUT_SSH 5 SSH timeout in seconds
E2E_PRINT_COMMAND 0 print SSH commands being executed (0/1)
E2E_TRACE_FILE path to JSONL trace file

JSONL tracing

Set E2E_TRACE_FILE to capture structured events:

E2E_TRACE_FILE=.e2e-trace.jsonl e2e exec -- make smoke

Each line is a JSON object with this shape:

  • kind: phase_start | phase_end | command_start | command_exit | command_error | process_exit
  • phase: optional phase name (preflight, sync_host_to_guest, clear_guest_out, guest_exec, pull_guest_to_host)
  • argv: optional command argv array
  • exitCode: optional integer
  • durationMs: optional integer duration in milliseconds (present for phase_end and process_exit)
  • timestamp: ISO-8601 timestamp
  • note: optional string

Example lines:

{"kind":"phase_start","phase":"preflight","argv":null,"exitCode":null,"timestamp":"2026年02月24日T23:24:00Z","note":null}
{"kind":"command_start","phase":"preflight","argv":["ssh","-p","22","runner@vm.local","echo ok"],"exitCode":null,"timestamp":"2026年02月24日T23:24:00Z","note":null}
{"kind":"command_exit","phase":"preflight","argv":["ssh","-p","22","runner@vm.local","echo ok"],"exitCode":0,"timestamp":"2026年02月24日T23:24:00Z","note":null}
{"kind":"process_exit","phase":null,"argv":null,"exitCode":0,"timestamp":"2026年02月24日T23:24:01Z","note":null}