diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f79c22..8e50d15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,16 @@ `DVM_USER=${USER:-developer}`. Bundled `share/dvm/config.sh` is now fully commented; uncomment values to override defaults. - Changed `dvm init` to render the bundled VM template, substituting host CPU/memory - ceilings into inline comments and listing every available recipe (auto-detected - from `share/dvm/recipes` and `~/.config/dvm/recipes`) commented out by default. + ceilings into inline comments. +- Added a `use_tools` helper in `share/dvm/config.sh`. The bundled VM template calls + it, so every general-purpose recipe (excluding service recipes like `llama` and + `cloudflared`) is selected from a single global location. Define more helpers + (`use_data_tools`, …) and mix them per VM as needed. - Rewrote `share/dvm/vms/app.sh` as a fully commented self-documenting template. -- Added `# Description: ` to bundled recipes so `dvm init` can show them in - the available-recipes block. +- Added `# Description: ` to bundled recipes as a one-liner docstring. +- Moved `DVM_CHEZMOI_REPO`, `DVM_CHEZMOI_SIGNING_KEY`, and `DVM_CHEZMOI_DEPLOY_KEY` to + global config; per-VM config only opts in via `use chezmoi` (per-VM override still + works through the source order). - Added a `bat` recipe that installs bat from Fedora and runs `bat cache --build`. - Added VM config validation before Lima template rendering for VM names, users, sizing, code directories, host IPs, and port forwards. diff --git a/README.md b/README.md index fb295f9..2637bf9 100644 --- a/README.md +++ b/README.md @@ -85,9 +85,11 @@ dvm init app `dvm init` writes a fully commented template; uncomment what you need. Defaults are `DVM_CPUS=2`, `DVM_MEMORY=2GiB`, `DVM_DISK=10GiB`, `DVM_CODE_DIR=~/code/$DVM_NAME`, -empty `DVM_PORTS`. The template also lists every available recipe (auto-detected from -bundled and user recipes) with a one-line description, and shows the host's CPU and -memory ceilings as inline comments. +empty `DVM_PORTS`. The template shows the host's CPU and memory ceilings as inline +comments and calls `use_tools`, a helper defined in your global config that holds the +recipes shared across every app VM. Edit `~/.config/dvm/config.sh` once to choose +your toolset; new VMs pick it up automatically. Per-VM configs can add extra +recipes, define more helpers, or comment the `use_tools` line for a minimal VM. `~` in DVM variables means the guest user's home. Host project directories are not mounted into the VM. VM names use lowercase letters, numbers, and hyphens, starting diff --git a/docs/config.md b/docs/config.md index 3b1478d..3d6a41b 100644 --- a/docs/config.md +++ b/docs/config.md @@ -62,9 +62,8 @@ dvm init myapp dvm sync myapp ``` -`dvm init` writes a fully commented template that lists every recipe (auto-detected -from `share/dvm/recipes` and `~/.config/dvm/recipes`) and shows host CPU/memory limits. -Uncomment what you need: +`dvm init` writes a fully commented template that shows host CPU/memory limits and +calls `use_tools` (the helper defined in global config). Uncomment what you need: ```bash # DVM_CPUS=4 # default 2 (host max: 14) @@ -72,16 +71,18 @@ Uncomment what you need: # DVM_DISK=80GiB # default 10GiB # DVM_CODE_DIR="~/code/$DVM_NAME" # DVM_PORTS="3000:3000 5173:5173" -# DVM_CHEZMOI_REPO="https://github.com/YOUR_USER/dotfiles.git" - -# use node # Node.js, npm, corepack -# use python # Python and uv -# use agent-user # dvm-agent user with Bubblewrap sandbox for AI tools -# use codex # Codex CLI -# use claude # Claude Code CLI -# use chezmoi # public dotfiles via chezmoi over HTTPS + +use_tools ``` +## Toolsets via Helpers + +`share/dvm/config.sh` ships a `use_tools` function with every general-purpose recipe +listed and commented out. Uncomment the recipes you want every app VM to install. +Define more helpers (`use_data_tools`, `use_ml_tools`, …) for groups of VMs and call +them from per-VM configs. Service recipes (`llama`, `cloudflared`) are not in +`use_tools`; they belong in their dedicated VM templates. + ## Variables - `DVM_CPUS`, `DVM_MEMORY`, `DVM_DISK`: Lima VM sizing. Default `2`, `2GiB`, `10GiB`. @@ -102,12 +103,14 @@ Uncomment what you need: Set `DVM_CLAUDE_BYPASS=0` to leave Claude permission prompts enabled. - `DVM_COREPACK_VERSION`: Corepack npm package version for the `node` recipe, normally `0.34.0`. -- `DVM_CHEZMOI_REPO`: public HTTPS dotfiles repo. +- `DVM_CHEZMOI_REPO`: public HTTPS dotfiles repo. Required when any VM uses the + `chezmoi` recipe. Usually set in global config; per-VM config can override. - `DVM_CHEZMOI_ROLE`, `DVM_CHEZMOI_NAME`, `DVM_CHEZMOI_EMAIL`: optional shared chezmoi - `[data]` values. -- `DVM_CHEZMOI_SIGNING_KEY`, `DVM_CHEZMOI_DEPLOY_KEY`: optional per-VM chezmoi - `[data]` key path overrides. When unset, generated chezmoi data uses - `~/.ssh/id_ed25519_dvm_signing.pub` and `~/.ssh/id_ed25519_dvm.pub`. + `[data]` values. Usually set in global config; per-VM config can override. +- `DVM_CHEZMOI_SIGNING_KEY`, `DVM_CHEZMOI_DEPLOY_KEY`: optional chezmoi `[data]` key + path overrides. Usually set in global config when `dvm ssh-key` is configured with + custom key names; per-VM config can override. When unset, generated chezmoi data + uses `~/.ssh/id_ed25519_dvm_signing.pub` and `~/.ssh/id_ed25519_dvm.pub`. - `DVM_CHEZMOI_CONFIG_TOML`: optional full chezmoi config written to `~/.config/chezmoi/chezmoi.toml`; when set, it takes over the generated chezmoi data config rather than merging with it. diff --git a/docs/dotfiles.md b/docs/dotfiles.md index d4118ff..1da70e0 100644 --- a/docs/dotfiles.md +++ b/docs/dotfiles.md @@ -9,9 +9,20 @@ for public dotfiles. If dotfiles must be private, add a separate SSH recipe late ## Config +Put chezmoi settings in global config (`~/.config/dvm/config.sh`) since the dotfiles +identity is usually the same across every VM. A per-VM config can still override any +of these when needed (per-VM config sources last and wins). + ```bash DVM_CHEZMOI_REPO="https://github.com/YOUR_USER/dotfiles.git" +DVM_CHEZMOI_ROLE="vm" +DVM_CHEZMOI_NAME="Your Name" +DVM_CHEZMOI_EMAIL="you@example.com" +``` + +Then opt VMs in via the per-VM config: +```bash use chezmoi ``` @@ -19,14 +30,6 @@ The recipe installs `chezmoi`, writes `~/.config/chezmoi/chezmoi.toml` through a temporary file when configured, initializes `~/.local/share/chezmoi` when missing, pulls updates when already initialized, and runs `chezmoi apply`. -For common dotfiles template data, put shared identity values in global DVM config: - -```bash -DVM_CHEZMOI_ROLE="vm" -DVM_CHEZMOI_NAME="Your Name" -DVM_CHEZMOI_EMAIL="you@example.com" -``` - The generated data uses the default key paths created by `dvm ssh-key `: ```toml @@ -34,7 +37,8 @@ signingKey = "~/.ssh/id_ed25519_dvm_signing.pub" deployKey = "~/.ssh/id_ed25519_dvm.pub" ``` -Override the paths in VM config only when you use custom key names: +Override (in global config, or per-VM if a single VM needs different keys) only when +`dvm ssh-key` is configured with custom names: ```bash DVM_CHEZMOI_SIGNING_KEY="~/.ssh/id_ed25519_project_signing.pub" diff --git a/docs/recipes.md b/docs/recipes.md index 1fa466c..5d95f38 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -6,8 +6,10 @@ that boundary clear. ## Host Config Host config lives in `~/.config/dvm/vms/.sh`. `dvm init` writes a fully commented -template (built-in defaults: `DVM_CPUS=2`, `DVM_MEMORY=2GiB`, `DVM_DISK=10GiB`). -Uncomment what you need: +template (built-in defaults: `DVM_CPUS=2`, `DVM_MEMORY=2GiB`, `DVM_DISK=10GiB`) that +calls `use_tools`, a helper defined in `~/.config/dvm/config.sh`. Uncomment recipes +in `use_tools` once and every app VM picks them up. Per-VM configs can add extra +`use ` lines for VM-specific recipes: ```bash # DVM_CPUS=4 # default 2 (host max shown in template) @@ -15,9 +17,8 @@ Uncomment what you need: # DVM_CODE_DIR="~/code/$DVM_NAME" # DVM_PORTS="3000:3000" -# use node # Node.js, npm, corepack -# use agent-user # dvm-agent user with Bubblewrap sandbox for AI tools -# use codex # Codex CLI +use_tools +use codex # extra recipe just for this VM ``` `use ` only selects `recipes/.sh`; it does not run the recipe on the host. @@ -43,8 +44,7 @@ Rules: - Use `DVM_CODE_DIR` for project code. - Keep tool-specific config close to the recipe that uses it. - Do not add recipe metadata, dependency graphs, registries, or versioning. -- Add `# Description: ` near the top of a recipe so `dvm init` shows it in - the auto-generated "available recipes" comment block. +- Add `# Description: ` near the top of a recipe as a one-liner docstring. ## Built-In Recipes @@ -172,16 +172,16 @@ $DVM_CODE_DIR/.dvm/sync.sh That hook runs after baseline and selected recipes, inside the guest. -`chezmoi` applies public dotfiles over HTTPS: +`chezmoi` applies public dotfiles over HTTPS. Put `DVM_CHEZMOI_REPO` and the optional +identity values in `~/.config/dvm/config.sh` (they are the same across all VMs); the +per-VM config only opts in: ```bash -DVM_CHEZMOI_REPO="https://github.com/YOUR_USER/dotfiles.git" use chezmoi ``` -Shared chezmoi template data such as `DVM_CHEZMOI_ROLE`, `DVM_CHEZMOI_NAME`, and -`DVM_CHEZMOI_EMAIL` usually belongs in `~/.config/dvm/config.sh`; generated key data -uses the default paths from `dvm ssh-key ` unless overridden globally or per VM. +Generated key data uses the default paths from `dvm ssh-key ` unless +`DVM_CHEZMOI_SIGNING_KEY` / `DVM_CHEZMOI_DEPLOY_KEY` are set in global config. `llama` installs the llama service. Configure a dedicated VM: diff --git a/share/dvm/config.sh b/share/dvm/config.sh index 5c74097..022c620 100644 --- a/share/dvm/config.sh +++ b/share/dvm/config.sh @@ -22,7 +22,39 @@ # Set to 0 to enable Claude permission prompts. # DVM_CLAUDE_BYPASS=1 -# Optional chezmoi [data] values for VMs that use the chezmoi recipe: +# Settings for the chezmoi recipe. DVM_CHEZMOI_REPO is required when any VM uses +# `use chezmoi`. The signing/deploy key paths default to those created by +# `dvm ssh-key `; override only if you use custom key names. +# DVM_CHEZMOI_REPO="https://github.com/YOUR_USER/dotfiles.git" # DVM_CHEZMOI_ROLE="vm" # DVM_CHEZMOI_NAME="Your Name" # DVM_CHEZMOI_EMAIL="you@example.com" +# DVM_CHEZMOI_SIGNING_KEY="~/.ssh/id_ed25519_dvm_signing.pub" +# DVM_CHEZMOI_DEPLOY_KEY="~/.ssh/id_ed25519_dvm.pub" + +# Default toolset shared across app VMs. The bundled VM template calls +# `use_tools`, so any recipe uncommented here is installed in every VM that +# keeps that call. Define more helpers (e.g. `use_data_tools`) and call them +# from per-VM configs to mix and match. +use_tools() { + : # placeholder; remove once at least one `use` line below is uncommented + # use zsh # zsh shell (sets as login shell) + # use git # git + # use helix # Helix editor + # use lazygit # lazygit TUI + # use starship # starship prompt + # use fzf # fzf fuzzy finder + # use bat # bat (cat with syntax highlighting) + # use git-delta # delta diff pager + # use just # just task runner + # use tmux # tmux terminal multiplexer + # use yazi # yazi file manager + # use node # Node.js, npm, corepack + # use python # Python and uv + # use agent-user # dvm-agent user with Bubblewrap sandbox for AI tools + # use codex # Codex CLI + # use claude # Claude Code CLI + # use opencode # OpenCode CLI + # use mistral # Mistral CLI + # use chezmoi # public dotfiles via chezmoi over HTTPS +} diff --git a/share/dvm/lib/core.sh b/share/dvm/lib/core.sh index ee3ba3f..9771223 100644 --- a/share/dvm/lib/core.sh +++ b/share/dvm/lib/core.sh @@ -141,43 +141,12 @@ host_max_memory() { printf '?\n' } -available_recipes_block() { - local file name desc dir - { - for dir in "$DVM_SHARE/recipes" "$DVM_CONFIG/recipes"; do - [ -d "$dir" ] || continue - for file in "$dir"/*.sh; do - [ -f "$file" ] || continue - name="$(basename "$file" .sh)" - case "$name" in - _* | baseline) continue ;; - esac - printf '%s\n' "$name" - done - done - } | sort -u | while IFS= read -r name; do - file="$DVM_CONFIG/recipes/$name.sh" - [ -f "$file" ] || file="$DVM_SHARE/recipes/$name.sh" - desc="$(awk '/^# Description:/ {sub(/^# Description: */, ""); print; exit}' "$file" 2>/dev/null || true)" - if [ -n "$desc" ]; then - printf '# use %-12s # %s\n' "$name" "$desc" - else - printf '# use %s\n' "$name" - fi - done -} - render_vm_template() { local src="$1" dst="$2" - local max_cpus max_memory recipes_block line + local max_cpus max_memory line max_cpus="$(host_max_cpus)" max_memory="$(host_max_memory)" - recipes_block="$(available_recipes_block)" while IFS= read -r line || [ -n "$line" ]; do - if [ "$line" = "__DVM_AVAILABLE_RECIPES__" ]; then - printf '%s\n' "$recipes_block" - continue - fi case "$line" in *__DVM_HOST_MAX_CPUS__*) line="${line//__DVM_HOST_MAX_CPUS__/$max_cpus}" ;; esac diff --git a/share/dvm/vms/app.sh b/share/dvm/vms/app.sh index 2bd1915..9abe5cc 100644 --- a/share/dvm/vms/app.sh +++ b/share/dvm/vms/app.sh @@ -13,12 +13,8 @@ # Forwarded host:guest ports, space-separated. Empty by default. # DVM_PORTS="3000:3000 5173:5173" -# Public dotfiles repo for the chezmoi recipe. -# DVM_CHEZMOI_REPO="https://github.com/YOUR_USER/dotfiles.git" +# Default toolset (defined in ~/.config/dvm/config.sh). Comment the line below +# for a minimal VM, or define more helpers globally and call them here. +use_tools -# Override SSH key paths only when ssh-key was created with custom names: -# DVM_CHEZMOI_SIGNING_KEY="~/.ssh/id_ed25519_dvm_signing.pub" -# DVM_CHEZMOI_DEPLOY_KEY="~/.ssh/id_ed25519_dvm.pub" - -# Available recipes. Uncomment to enable. -__DVM_AVAILABLE_RECIPES__ +# Per-VM additions go below. diff --git a/tests/smoke.sh b/tests/smoke.sh index 8ebee96..d2265fa 100755 --- a/tests/smoke.sh +++ b/tests/smoke.sh @@ -178,16 +178,11 @@ grep -Fq 'dvm init [template]' "$TMP/install-help.out" "$ROOT/bin/dvm" init newapp [ -f "$TMP/config/vms/newapp.sh" ] grep -Fq 'DVM_CODE_DIR="~/code/$DVM_NAME"' "$TMP/config/vms/newapp.sh" -grep -Fq '# use chezmoi' "$TMP/config/vms/newapp.sh" -grep -Fq '# use node' "$TMP/config/vms/newapp.sh" +grep -Eq '^use_tools$' "$TMP/config/vms/newapp.sh" if grep -Fq '__DVM_HOST_MAX_CPUS__' "$TMP/config/vms/newapp.sh"; then printf 'init left __DVM_HOST_MAX_CPUS__ placeholder unsubstituted\n' >&2 exit 1 fi -if grep -Fq '__DVM_AVAILABLE_RECIPES__' "$TMP/config/vms/newapp.sh"; then - printf 'init left __DVM_AVAILABLE_RECIPES__ placeholder unsubstituted\n' >&2 - exit 1 -fi "$ROOT/bin/dvm" init llama llama [ -f "$TMP/config/vms/llama.sh" ] grep -Fq 'use llama' "$TMP/config/vms/llama.sh"