Skip to content

Commit a442ab1

Browse files
hbrodinclaude
andauthored
Release v0.5.0 (#292)
Bumps Cargo.toml + Cargo.lock to 0.5.0. Adds the v0.5.0 CHANGELOG section and removes two entries the v0.4.4 section claimed (the tmux-removal #183 and --forward-port #125 PRs landed after the v0.4.4 tag and never shipped in that release). The release workflow extracts notes from CHANGELOG.md by matching ## $TAG, so once this lands the v0.5.0 tag push will pick up the new section automatically. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e857569 commit a442ab1

3 files changed

Lines changed: 296 additions & 20 deletions

File tree

CHANGELOG.md

Lines changed: 294 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,302 @@
11
# Changelog
22

3-
## v0.4.4
3+
## v0.5.0
44

55
### Breaking changes
66

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
16300

17301
- **`coop push` / `coop pull` / `coop exec` take the instance name as a
18302
positional argument** (#90) — these three commands previously accepted
@@ -27,14 +311,6 @@
27311

28312
### New features
29313

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-
38314
- **`coop ca` / `coop claude-agents` shortcut** (#80, #82, #99, #100, #101) —
39315
Runs `claude agents` inside the VM in one command. Claude Code's daemon
40316
manages background-session lifetime itself, so closing the terminal

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "coop"
3-
version = "0.4.4"
3+
version = "0.5.0"
44
edition = "2024"
55
description = "Isolated VM environment for running Claude Code"
66

0 commit comments

Comments
 (0)