|
1 | 1 | # Changelog |
2 | 2 |
|
3 | | -## v0.4.4 |
| 3 | +## v0.5.0 |
4 | 4 |
|
5 | 5 | ### Breaking changes |
6 | 6 |
|
7 | | -- **tmux session wrapping removed** (#183) — `coop shell`, `coop claude`, |
8 | | - and `coop codex` no longer wrap the remote session in tmux, and the |
9 | | - `--session <name>` / `--no-tmux` flags are gone. `tmux` is no longer |
10 | | - installed in the guest image either. Users who want detachable sessions |
11 | | - can run `coop shell` and start tmux themselves, after installing it as |
12 | | - an extra package (`--extra-packages tmux` on `coop setup`) or via a |
13 | | - custom post-install script. Claude Code's background-agent daemon |
14 | | - (`coop claude-agents`) already provides session persistence without a |
15 | | - terminal multiplexer. |
| 7 | +- **`coop build` removed; creation flags moved off `coop start`** (#269, |
| 8 | + #281) — `coop start` no longer accepts `--profile`, `--git-repo`, |
| 9 | + `--vcpus`, `--mem`, `--disk`, `--mount`, `--image`, or `--exclude-git`; |
| 10 | + those flags now live on `coop up`. clap reports them as unexpected |
| 11 | + arguments instead of accepting them and bailing at runtime. The |
| 12 | + standalone `coop build` command and the `scripts/fetch-kernel.sh` |
| 13 | + helper are removed — `coop setup` and `coop up --profile` cover image |
| 14 | + creation end-to-end. `scripts/build-rootfs.sh` stays as a documented |
| 15 | + manual fallback. Restart still works against existing instances; the |
| 16 | + flags above only take effect at create time (via `coop up`). |
| 17 | + |
| 18 | +- **`--git-repo` moved from `coop start` to `coop up`** (#264) — |
| 19 | + `coop up --git-repo <url>` clones a remote repository into |
| 20 | + `/workspace` and is idempotent across re-runs, matching the rest of |
| 21 | + `up`: an instance already associated with the URL is reused if |
| 22 | + running, restarted if stopped, and created otherwise. The instance |
| 23 | + name is derived from the repo basename when `--name` is not given. |
| 24 | + |
| 25 | +- **tmux session wrapping removed** (#183, #184) — `coop shell`, |
| 26 | + `coop claude`, and `coop codex` no longer wrap the remote session in |
| 27 | + tmux, and the `--session <name>` / `--no-tmux` flags are gone. The |
| 28 | + `tmux` package is also dropped from the guest base image. Claude |
| 29 | + Code's `claude agents` daemon (`coop claude-agents`) already |
| 30 | + provides session persistence without a terminal multiplexer; users |
| 31 | + who still want detachable terminals can install tmux themselves via |
| 32 | + `coop setup --extra-packages tmux` and start it manually inside |
| 33 | + `coop shell`. |
| 34 | + |
| 35 | +- **Devcontainer auto-discovery prompts on `up` / `setup --workspace`** |
| 36 | + (#129, #130, #242) — When the workspace contains |
| 37 | + `.devcontainer/devcontainer.json` and no escape hatch flag is set, |
| 38 | + coop prompts before applying it. Non-TTY callers must pass |
| 39 | + `--devcontainer <path>`, `--no-devcontainer`, or `--dry-run`; the |
| 40 | + prompt never silently chooses. Precedence is |
| 41 | + `CLI > devcontainer.json > defaults`, and the report column makes |
| 42 | + overrides explicit. |
| 43 | + |
| 44 | +### New features |
| 45 | + |
| 46 | +- **`coop up` — project-oriented environment command** (#230, #264) — |
| 47 | + `coop up [DIR]` ensures an environment for a project directory |
| 48 | + exists and is running, idempotently. `DIR` defaults to the current |
| 49 | + directory and is canonicalized for affinity; a matching instance is |
| 50 | + reconnected if running, restarted if stopped, or created otherwise. |
| 51 | + `--git-repo <url>` uses a remote repository as the workspace instead |
| 52 | + of a local directory. Creation-time flags (`--vcpus`, `--mem`, |
| 53 | + `--disk`, `--image`, `--profile`, `--extra-mount`, |
| 54 | + `--devcontainer`, `--exclude-git`) only take effect when the |
| 55 | + instance is created and are rejected against existing instances with |
| 56 | + a `coop destroy` hint; runtime flags (`--forward-port`, |
| 57 | + `--post-start`, `--env`) take effect on create or restart but are |
| 58 | + ignored when reconnecting to a running VM. |
| 59 | + |
| 60 | +- **`coop quickstart` — one-shot setup + start + claude** (#74, #213) |
| 61 | + — Chains `ensure-image → ensure-instance → launch-claude`, |
| 62 | + short-circuiting any step that's already done. Setup runs only when |
| 63 | + the `default` template image is missing; workspace affinity |
| 64 | + reconnects to a running instance for the current directory (or |
| 65 | + restarts a stopped one) instead of allocating fresh. `--no-workspace` |
| 66 | + opts out of the cwd mount; sensitive directories (`$HOME`, `/`) |
| 67 | + prompt default-no on a TTY and bail non-interactively. |
| 68 | + |
| 69 | +- **`devcontainer.json` support** (#129, #130, #131, #134) — A new |
| 70 | + translator maps recognised keys to coop's existing primitives: |
| 71 | + `hostRequirements` → `--vcpus`/`--mem`/`--disk`, `containerEnv` → |
| 72 | + guest env, `forwardPorts` → `--forward-port`, `postStartCommand` → |
| 73 | + `post_start`, `features` → built-in profiles, `mounts` → |
| 74 | + `--mount`, `remoteUser` → `--guest-user`. New flags on |
| 75 | + `setup` / `up`: `--devcontainer PATH` (opt in to a specific file), |
| 76 | + `--no-devcontainer` (opt out entirely), and `--dry-run` (print the |
| 77 | + per-key report and exit before any side effects). JSONC (`//`, |
| 78 | + `/* */` comments, trailing commas) parses cleanly. CLI `--env` and |
| 79 | + devcontainer `containerEnv` are persisted to `guest_env.json` so |
| 80 | + `coop shell` / `coop exec` see the same values without re-parsing |
| 81 | + config. |
| 82 | + |
| 83 | +- **OCI devcontainer features** (#245) — Supported public |
| 84 | + `ghcr.io/devcontainers/features/*` entries declared in |
| 85 | + `devcontainer.json` are resolved at `coop setup` and baked into the |
| 86 | + image alongside the existing built-in profiles. `coop up --profile` |
| 87 | + continues to compose the built-in profile set. |
| 88 | + |
| 89 | +- **`coop devcontainer` subcommands** (#234, #247, #287) — |
| 90 | + `coop devcontainer check <path>` translates a `devcontainer.json` |
| 91 | + and prints the report without running any setup or VM work |
| 92 | + (`--stage setup|start|both`). `coop devcontainer ignore <project>` |
| 93 | + persistently opts a project out of discovery; `... status` lists |
| 94 | + current opt-outs; `... clear` removes one. Symlinked project |
| 95 | + prefixes are resolved when clearing, so a directory that was moved |
| 96 | + or unlinked doesn't leave a stale opt-out behind. |
| 97 | + |
| 98 | +- **`coop setup --guest-user` and `remoteUser` handling** (#217, #218) |
| 99 | + — Bake a configurable guest username (default `ubuntu`, validated |
| 100 | + against POSIX rules and not `root`) into the image at setup time. |
| 101 | + `start`/`shell`/`exec` read the value from the image's |
| 102 | + `template_config.json`. A `devcontainer.json` that declares a |
| 103 | + different `remoteUser` is honored when its value matches the |
| 104 | + persisted setup user; mismatches surface an actionable diagnostic |
| 105 | + instead of silently writing a wrong home path. Backward-compatible |
| 106 | + for pre-existing images. |
| 107 | + |
| 108 | +- **`--forward-port` / `forward_ports` config** (#125, #128) — Forward |
| 109 | + guest TCP ports to the host for the lifetime of the VM. |
| 110 | + `coop up --forward-port 3000` exposes guest 3000 on host 3000; |
| 111 | + `3000:18080` remaps to a different host port. The flag is |
| 112 | + repeatable, supported by a config-level `forward_ports = [...]` |
| 113 | + default, persisted across `stop`/`start`, and torn down cleanly on |
| 114 | + `coop stop`. Collision with an already-bound host port fails fast |
| 115 | + before the VM is created. |
| 116 | + |
| 117 | +- **`post_start` hook** (#123, #126) — `post_start` in `config.toml` |
| 118 | + (or `--post-start <cmd>` on `coop up` / `coop start`) runs a shell |
| 119 | + command in the guest after SSH is ready and before any interactive |
| 120 | + shell or agent launch. Maps to `postStartCommand` in |
| 121 | + `devcontainer.json`. Failure is logged at WARN and does not fail |
| 122 | + the start, so a transient hook problem can't strand the VM. |
| 123 | + |
| 124 | +- **`[guest_env]` config block + `--env KEY=VALUE`** (#127, #131, |
| 125 | + #134) — Set literal env vars inside the guest without forwarding |
| 126 | + from the host. CLI overrides win over config and devcontainer |
| 127 | + values; `--env` is persisted to `guest_env.json` so |
| 128 | + `coop shell` / `coop exec` see the same values without rerunning |
| 129 | + `start`. `coop start --env` and `--devcontainer`-derived |
| 130 | + `containerEnv` survive `stop`/`start` cycles. |
| 131 | + |
| 132 | +- **Build profile images on demand from `coop up`** (#235) — |
| 133 | + `coop up --profile <list>` derives an image name from the sorted |
| 134 | + profile list, runs the same stale-image check as `coop setup`, and |
| 135 | + builds or rebuilds that image if needed. Explicit named images |
| 136 | + (`--image`) are unchanged. |
| 137 | + |
| 138 | +- **Submodule discovery in `coop github setup-pat`** (#219, #221, |
| 139 | + #223) — The wizard pre-discovers submodule repositories via the |
| 140 | + local `gh` install (no network round-trip per submodule) and offers |
| 141 | + to set up a PAT for each one. |
| 142 | + |
| 143 | +- **Guest PATH set via `/etc/environment`** (#248, #249) — |
| 144 | + `~/.local/bin` is now reliably on `PATH` for non-interactive SSH |
| 145 | + sessions, including `coop claude` and Claude Code's Bash-tool |
| 146 | + subshells. Previously these sessions skipped `~/.profile` and |
| 147 | + `~/.bashrc`'s PATH export, hiding `uv`/`pipx`/user tools and |
| 148 | + triggering `claude doctor` warnings. `pam_env` reads |
| 149 | + `/etc/environment` for every PAM session regardless of shell mode, |
| 150 | + so the prepend reaches remote commands, their subshells, and |
| 151 | + VS Code remote sessions. |
| 152 | + |
| 153 | +- **Docker buildx in guest images** (#288) — `docker buildx` is |
| 154 | + installed in every coop image so multi-arch and `buildkit`-driven |
| 155 | + builds work without manual setup. |
| 156 | + |
| 157 | +- **Secrets redacted from `Debug` output** (#79, #121, #262) — A new |
| 158 | + `Secret<T>` newtype with redacting `Debug`, transparent serde, and |
| 159 | + an explicit `expose()` accessor wraps `ClaudeConfig.api_key`, |
| 160 | + `CodexConfig.api_key`, and `PatEntry.token`. MCP header secrets |
| 161 | + and forwarded env values render as `<redacted>` in error chains, |
| 162 | + tracing events, `dbg!`, and panics. |
| 163 | + |
| 164 | +- **Hint at `--workspace`/`--mount` when `start` name looks like a |
| 165 | + path** (#226) — Running `coop start ./my-project` now surfaces a |
| 166 | + hint to use `coop up` (which interprets `DIR` correctly) instead of |
| 167 | + failing with an opaque "instance not found". |
| 168 | + |
| 169 | +- **Warn when `--mount` source is a live git repo** (#102, #122) — |
| 170 | + Live mounts share filesystem state with the host, so in-guest |
| 171 | + changes affect the host checkout immediately. coop now warns when |
| 172 | + the mount source looks like a working tree. |
| 173 | + |
| 174 | +### Fixes |
| 175 | + |
| 176 | +- **Survive mid-session SSH exit 255 in interactive sessions** (#224, |
| 177 | + #227) — A network blip that produced SSH exit 255 mid-session no |
| 178 | + longer kills the wrapping coop process; the session reattaches. |
| 179 | + |
| 180 | +- **Interactive SSH escape path hardened** (#232) — Closes a regression |
| 181 | + in escape-sequence handling on the interactive path. |
| 182 | + |
| 183 | +- **`workspace.json` parse errors surfaced** (#225) — Three call sites |
| 184 | + previously swallowed parse errors; all now report them with context. |
| 185 | + |
| 186 | +- **Bare `coop start --mount` allocates a fresh instance** (#214, |
| 187 | + #215) — Earlier this collided with the auto-resolve path and |
| 188 | + produced a confusing error. |
| 189 | + |
| 190 | +- **`coop up` workspace sync no longer drops `--extra-mount` on |
| 191 | + Firecracker** (#264) — The workspace-sync block now always syncs |
| 192 | + extra mounts; only mount-only instances record the workspace |
| 193 | + identity. Also fixes a tar-pipe fallback that extracted to |
| 194 | + `/workspace` regardless of the mount's guest path. |
| 195 | + |
| 196 | +- **`coop start` devcontainer lifecycle ordering** (#241) — Devcontainer |
| 197 | + translation now runs before the SSH-ready probe, so per-key |
| 198 | + reporting reflects what will actually be applied. |
| 199 | + |
| 200 | +- **Skip redundant `is_running()` in stop / stream-logs** (#195, |
| 201 | + #205) — These were probing the running state twice on the |
| 202 | + SSH-readiness path; collapsed to a single probe. |
| 203 | + |
| 204 | +- **Direct-probe `resolve_instance` fast path for by-name lookups** |
| 205 | + (#202, #203, #211, #212) — Defers `CoopConfig::validate` until |
| 206 | + commands that touch probed paths actually need it, and skips the |
| 207 | + generic search when an instance name is supplied. |
| 208 | + |
| 209 | +- **Lima SSH-readiness probe via `ssh.config` instead of |
| 210 | + `limactl list`** (#201, #210) — Cuts a 0.4–1.5 s `limactl` |
| 211 | + invocation off the resolve fast path on macOS. |
| 212 | + |
| 213 | +- **Firecracker CI kernel fallback** (#290) — When the latest |
| 214 | + Firecracker CI minor has no assets published yet (transient |
| 215 | + release-pipeline gap), setup falls back to the previous minor |
| 216 | + instead of failing. |
| 217 | + |
| 218 | +- **Integration tests** — Forward-port test uses a `python3` listener |
| 219 | + instead of `nc` (Firecracker CI rootfs ships no netcat) (#132). |
| 220 | + `test_list_empty` tolerates pre-existing instances on a shared |
| 221 | + host (#133). `--forward-port` collision test reads `$HARNESS_ERR` |
| 222 | + instead of an outer redirect (#135). Devcontainer opt-out test |
| 223 | + assertions match the actual message text (#286). Stop idempotency |
| 224 | + test reuses a stopped instance (#240). Bare `coop start --mount` |
| 225 | + `pipefail`+`grep -q` race fixed (#220). |
| 226 | + |
| 227 | +### Internal |
| 228 | + |
| 229 | +- **VM lifecycle type-state** (#153, #180, #275) — `RunningInstance` |
| 230 | + and `StoppedInstance` carry lifecycle state in the type, eliminating |
| 231 | + runtime state checks on operations like `resize_disk`, `status`, |
| 232 | + and `stop`. The `RunningInstance` proof extends across both |
| 233 | + backends. |
| 234 | + |
| 235 | +- **Newtype / enum survey across module boundaries** — |
| 236 | + `InstanceName` (#192, #199), `RepoSlug` (#149, #171), |
| 237 | + `ImageName` (#157, #181), `Hostname` and `SshUser` (#154, #187), |
| 238 | + `GuestUser` (#217, #218), `ToolName` and `AccountName` (#165, |
| 239 | + #190), `EnvVarName` (#151, #177, #196, #206), `MemorySpec` (#163, |
| 240 | + #188), `MiB`/`GiB` (#194, #207), `Sha256Hash` (#161, #189), |
| 241 | + `Architecture` (#169, #173), `PortForward` and `Mount` (#179), |
| 242 | + `HostInterface` (#159, #186), `SubnetMask(u8)` (#150, #176), |
| 243 | + `InstanceIndex` 0..=252 bound (#162, #175), `TmuxSessionName` + |
| 244 | + `RemoteCommand` (#166, #182, later removed with #183), |
| 245 | + `GuestPath` round-trips dropped (#193, #204). Profile names |
| 246 | + resolved at the config boundary (#156, #172). |
| 247 | + `--mount`/`--env`/`--forward-port` and disk size / devcontainer |
| 248 | + path / repo slug parsed by clap value-parsers (#179, #285). |
| 249 | + `redacted_args: Vec<usize>` replaced with typed `Arg` variants |
| 250 | + (#160, #185). `McpServerDef` replaced with a transport-tagged |
| 251 | + enum (#148, #178). `WorkspaceState` optionals collapsed into |
| 252 | + `WorkspaceSource` variants (#170). `restart: bool` and |
| 253 | + `follow: bool` replaced with two-variant enums (#174). |
| 254 | + Correlated `bool` fields replaced with enums (#266, #280). |
| 255 | + `cmd:` string reverse-parsing replaced with a structured |
| 256 | + `CmdToken` (#277). Guest-env precedence merge unified (#276). |
| 257 | + PAT repo probe typed with a `ProbeError` enum (#282). Dead |
| 258 | + wrappers removed and over-broad visibility tightened (#283). |
| 259 | + Near-identical function pairs extracted into shared helpers |
| 260 | + (#284). Newtypes threaded through boundaries instead of decaying |
| 261 | + to `String` (#279). A four-PR survey of smaller newtype / struct |
| 262 | + refactors (#191). |
| 263 | + |
| 264 | +- **Mutation-testing baseline on `config.rs`** (#136, #137, #138, |
| 265 | + #139, #140, #141, #143, #144, #145, #146) — Adds the |
| 266 | + mutation-testing guidance now in `CLAUDE.md`, pins |
| 267 | + `CoopConfig::validate`, `Instance::is_running`, |
| 268 | + `is_firecracker_process`, and `MiB::as_gib_f64` with tests, and |
| 269 | + annotates equivalent mutants (`fmt::Display` impls, default-value |
| 270 | + getters, serde `Visitor` methods) with `#[mutants::skip]` and a |
| 271 | + one-line justification. |
| 272 | + |
| 273 | +- **Port-forward integration test covers restart re-establishment** |
| 274 | + (#260). |
| 275 | + |
| 276 | +- **`[guest_env]` config block integration phase** (#263). |
| 277 | + |
| 278 | +- **OCI devcontainer feature install integration phase** (#261). |
| 279 | + |
| 280 | +### Documentation |
| 281 | + |
| 282 | +- **`coop up`, `coop quickstart`, and the devcontainer workflow** |
| 283 | + documented in `docs/commands.md`, `docs/getting-started.md`, and |
| 284 | + `docs/devcontainer.md` (#213, #230, #234, #257). |
| 285 | + |
| 286 | +- **Guest user, guest PATH, and startup flag pointers** (#259). |
| 287 | + |
| 288 | +- **Submodule discovery in setup-pat wizard** (#258). |
| 289 | + |
| 290 | +- **Mutation-testing guidance** (#136) added to `CLAUDE.md`. |
| 291 | + |
| 292 | +### Dependencies |
| 293 | + |
| 294 | +- `serde_json` 1.0.149 → 1.0.150 (#228) |
| 295 | +- `zizmorcore/zizmor-action` 0.5.3 → 0.5.5 → 0.5.6 (#198, #229) |
| 296 | + |
| 297 | +## v0.4.4 |
| 298 | + |
| 299 | +### Breaking changes |
16 | 300 |
|
17 | 301 | - **`coop push` / `coop pull` / `coop exec` take the instance name as a |
18 | 302 | positional argument** (#90) — these three commands previously accepted |
|
27 | 311 |
|
28 | 312 | ### New features |
29 | 313 |
|
30 | | -- **`--forward-port` / `forward_ports` config** (#125) — Forward guest |
31 | | - TCP ports to the host for the lifetime of the VM. `coop start |
32 | | - --forward-port 3000` exposes guest 3000 on host 3000; `3000:18080` |
33 | | - remaps to a different host port. The flag is repeatable, supported |
34 | | - by a config-level `forward_ports = [...]` default, persisted across |
35 | | - `stop`/`start`, and torn down cleanly on `coop stop`. Collision with |
36 | | - an already-bound host port fails fast before the VM is created. |
37 | | - |
38 | 314 | - **`coop ca` / `coop claude-agents` shortcut** (#80, #82, #99, #100, #101) — |
39 | 315 | Runs `claude agents` inside the VM in one command. Claude Code's daemon |
40 | 316 | manages background-session lifetime itself, so closing the terminal |
|
0 commit comments