Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vmkit

Parallels VM system-test control plane for macOS hosts. Test real installers, trust stores, network devices, and upgrade paths on pristine end-user guest VMs (Windows / Linux / macOS), with the operating discipline that makes that reliable:

  • Golden-snapshot laddergolden (powered-off, human-made, pristine) → ready (booted once) → built (provisioned); every test starts from a revert, never from leftover state.
  • Internal-disk-only boot policy — archive copies on an external drive are clone sources, never boot targets; CI hard-fails rather than silently booting off slow storage.
  • Guarded guest execution — host-side timeouts, guest straggler cleanup, revert-collapse recovery, one-VM-at-a-time.
  • Cache-first provisioning — downloads happen once into an offline cache; the test path only reverts snapshots (metered-link safe).
  • Encoded guest quirks — SYSTEM/UNC on Windows, tar.gz-push (+ keepalive) transport on macOS (shared folder is TCC-blocked under headless exec), headless-OS capability limits (docs/CAPABILITIES.md).

The division of labor is strict and documented: a human installs the OS + Parallels Guest Tools and takes one golden snapshot per VM (docs/HUMAN-SETUP.md); vmkit owns everything after. Every failure mode this harness has hit is cataloged with its guard in docs/FAILURES.md.

Install

vmkit is internal and not distributed through Homebrew — it is not part of the Port Zero product and must not appear in the product's public tap. Clone and install:

git clone git@github.com:PortZeroNetwork/vmkit.git && cd vmkit
just install             # -> /usr/local/bin/vmkit (override: just install ~/.local)
vmkit init-host          # then edit ~/.config/vmkit/host.conf
vmkit doctor             # validate the machine against the contract

To update: git pull && just install.

Use in a repo

cd my-repo
vmkit init               # writes ./vmkit.conf — define flavors there
vmkit test linux smoke   # reset to "built", run one flavor on one platform
vmkit series lifecycle   # every configured platform in series + summary

One-time guest provisioning

Some guest config must survive the per-test reset — a toolchain, Homebrew, a Windows Defender exclusion for unsigned/network-sourced installers. It can't live in a flavor script (every test reverts to built first); it has to be baked into the checkpoint itself. vmkit provision is that reset → run-a-guest-script → re-checkpoint dance as one reusable primitive, with "preserve the pristine baseline once" built in:

vmkit provision windows vmtest/scripts/windows-add-defender-exclusions.ps1 \
    --checkpoint built --label defender    # bakes the exclusion into "built"

See docs/PROVISIONING.md for the full model (anchors, preservation, failure handling).

Claiming the host for an interactive session

ensure_only enforces one-VM-at-a-time by stopping every other running VM. That is right for a queue of harness runs and destructive for a human working inside a guest: a CI job landing mid-session powers their VM off. A GitHub concurrency: group serializes CI jobs against each other and knows nothing about your local session.

vmkit hold is the missing half — a cooperative lock every implicit VM-stopping path checks:

export VMKIT_HOLD_TOKEN=$(vmkit hold --print-token "debugging the installer" --vm windows)
vmkit hold            # who has it, until when
vmkit unhold          # release

Authorization is by token, never by VM name. A live hold blocks every VM-stopping path for everybody; only a caller carrying VMKIT_HOLD_TOKEN from the record gets through. Export it, or your own vmkit reset is refused by your own hold. --vm is documentation of what the host is being used for, nothing more.

That distinction is the whole point. An earlier version keyed on the VM name — holding a VM permitted work on that VM, so the holder could reset their own guest. It protected every VM except the one actually in use: a CI job targeting the same guest matched and reverted it mid-provision. Everyone on this host runs as the same unix user, so pid/user/VM-name cannot tell a session from a CI job; a token can.

Holds always expire (--ttl, default 4h) — a forgotten hold that wedged CI until someone noticed would be worse than the failure this prevents. VMKIT_IGNORE_HOLD=1 overrides, vmkit doctor surfaces an active one, and vmkit hold --steal takes over.

For a long session on a machine that is also a CI runner, stop the runner service too: a hold makes the job fail with the reason, whereas an offline runner makes it queue until you're done.

Host-side desktop control (agent / manual UI)

Drive a running guest's interactive desktop from the host (no guest agent):

vmkit screenshot windows                 # PNG path printed on stdout
vmkit key windows win+r                  # combos: ctrl+c, alt+tab, enter, …
vmkit type windows "notepad"
vmkit key windows enter
vmkit mouse windows click                # buttons + relative nudge only

This is a vision loop substrate (screenshot → decide → key/type). Absolute click-at-(x,y) and accessibility trees need a guest agent (planned next).

Run vmkit init-agents to generate .instructions/vmkit.md (VM inventory, snapshot ladder, test flavors, archive drive) and wire it the same way agent-toolbox instruction modules do: one @.instructions/vmkit.md line in AGENTS.md, and CLAUDE.md as a thin @AGENTS.md pointer. No marker blocks in the root agent files.

Flavor scripts live in your repo, run inside the guests, and speak a tiny greppable protocol (PHASE=… ok=true|false|SKIP, final RESULT=PASS|FAIL|SKIP). Copy the helpers from $(vmkit guest-lib)/assert.sh|.ps1 into your repo's vmtest/scripts/lib/ and source them.

Fleet management

vmkit adopt              # new/cleaned machine: clone archive VMs -> internal disk
vmkit sync macos         # mirror an internal VM bundle out to its archive copy
vmkit list               # VMs + snapshot ladders

CI

Run the self-hosted runner as a service (never interactive run.sh), give the workflow a concurrency group that queues (never cancels) VM jobs, stage CI-built artifacts where VMKIT_ARTIFACT_* points, and call vmkit series <flavor>. Split triggers so PRs run a fast smoke and pushes run the full pristine-machine suite.

Docs

  • SPEC.md — v1 specification: runtime primitives + the Vmkitfile, written as acceptance criteria for the Rust port
  • HUMAN-SETUP.md — the human/machine contract, new-machine bootstrap (including Apple Silicon arch migration)
  • CAPABILITIES.md — what guest scripts can/can't do per OS
  • FAILURES.md — failure catalog: every known failure class → its guard
  • PROVISIONING.md — bake one-time guest OS config into a checkpoint (vmkit provision)

About

Parallels VM system-test control plane: golden-snapshot discipline, offline provisioning cache, guest-exec transport, and CI harness for testing real installers on pristine VMs

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages