Skip to content

Development Guide

  • Rust 1.85+ (Edition 2024) — install via rustup
  • macOS or Linux — macOS for development via Lima, Linux for native /dev/kvm
  • Nix (optional) — only needed for building microVM images

Run the bootstrap script on a fresh machine:

Terminal window
./scripts/dev-setup.sh
Terminal window
# Build
just build
# Run CLI
just run -- --help
# Dev mode (auto-bootstraps Lima + Firecracker)
just run -- dev
# Release build (stripped, LTO)
just release-build
Terminal window
# Run all tests with nextest
just test
# Test a single crate
just test-crate mvm-core
# Run tests matching a filter
just test-filter "test_snapshot"
# Full CI gate (lint + test)
just ci
LocationTypeWhat it tests
crates/*/src/**/*.rs (#[cfg(test)])Unit testsInternal functions within the crate
crates/*/tests/*.rsIntegration testsPublic API of each crate
tests/cli.rsBinary testsCLI arg parsing, help output, subcommand structure
  • Unit tests go in #[cfg(test)] mod tests {} at the bottom of the source file
  • CLI binary tests go in root tests/cli.rs
  • Use #[serde(default)] when adding fields to structs used in test fixtures
Terminal window
just fmt # Format all code
just clippy # Lint (zero warnings required)
just lint # Both format check + clippy
  • Edition 2024: use statements don’t need extern crate; let chains supported
  • No clippy::too_many_arguments: never suppress this lint — refactor into a params struct
  • No format!() in format!() named args: extract to a variable first
  • Cross-crate imports: always use mvm_core::, mvm_runtime::, etc.

All Linux operations (networking, Firecracker, cgroups) run inside the Lima VM on macOS:

// This runs a bash script inside the Lima VM
mvm_runtime::shell::run_in_vm("ip link add br-tenant-1 type bridge")?;

On native Linux, run_in_vm runs directly without Lima.

  • Idempotent operations: every setup step checks if already done before acting
  • Config drive for metadata: instance metadata delivered via read-only ext4 disk
  • Vsock over SSH: guest communication uses Firecracker vsock, not sshd

When adding fields to structs in serialized state:

  1. Add #[serde(default)] to the new field for backward compatibility
  2. cargo test --workspace to find all broken test constructions
  3. Fix each one
  4. Add a unit test for the new behavior
WorkflowTriggerWhat it does
ci.ymlPush to main/feat/*, PRscheck, fmt, clippy, test (macOS + Linux), audit
release.ymlTags matching v*Builds 4 platform binaries, creates GitHub Release
publish-crates.ymlRelease publishedPublishes to crates.io in dependency order
pages.ymlPush to mainDeploys docs to GitHub Pages
Terminal window
# 1. Bump version in root Cargo.toml [workspace.package]
# 2. Update CHANGELOG.md
# 3. Commit and tag
git add -A && git commit -m "release: v0.3.0"
git tag v0.3.0
# 4. Push (triggers release.yml)
git push && git push --tags

The deploy guard (scripts/deploy-guard.sh) validates the tag matches the workspace version before publishing.