Skip to content

Releases: gambtho/herdr-devcontainer

v0.2.0 — stop the whole Compose project

Choose a tag to compare

@gambtho gambtho released this 13 Aug 16:10

Important

prefix+S now stops more than it used to. On a Compose-based Dev Container, stop takes down every service in the project, not just the one the pane runs in. For a project with a database, cache, and gateway, that is one keystroke stopping six containers instead of one. The confirmation names every container before anything happens, and still proceeds only on an explicit y.

Stop means the whole Compose project

A Compose-based Dev Container is one service among several. Stopping only it left postgres, redis, and everything else running after the user asked to stop the Dev Container. That is also what devcontainer.json asks for: shutdownAction defaults to stopCompose for Compose configs, which is what VS Code does on disconnect.

Project membership comes from the container's own com.docker.compose.project label — the label Compose itself uses — so nothing here re-derives a project name from devcontainer.json.

stop 6 containers for /home/you/workspace/app?
  app               app_devcontainer-app-1               dc08b7aeca6f
  supabase-gateway  app_devcontainer-supabase-gateway-1  293672c080bb
  gotrue            app_devcontainer-gotrue-1            774e6d94d1c3
  redis             app_devcontainer-redis-1             f33d5de3a11e
  postgres          app_devcontainer-postgres-1          6cc33c601af0
  inbucket          app_devcontainer-inbucket-1          348a5d6e757b
[y/N]:

A single-container repository renders exactly as it did before.

Details that matter in practice

  • The Dev Container stops first, in its own docker stop call, and is waited on — so it releases its database connections before the database goes down. This needs a separate call: docker stop a b c stops the named containers in parallel, and argument order only controls the order results print.
  • docker compose run containers are left alone, matching docker compose stop. A compose run --rm app pytest running in another pane survives.
  • An exited Dev Container no longer hides a running project. If the app container exits on its own — a crash, an OOM kill, a stop from another pane — while its services keep running, v0.1.2 reported "no running dev container" and exited 0. Its survivors are now reported and offered for stopping.
  • Whether the stop worked is asked of docker, not inferred from docker stop's output. On timeout the CLI is killed and its output says nothing about what landed, so the most likely partial stop was the one that reported the least. Anything still running is named in the error.

Upgrade

herdr plugin install gambtho/herdr-devcontainer --ref v0.2.0

If you prefer the old single-container behavior, stay pinned to --ref v0.1.2.

Full changelog: v0.1.2...v0.2.0

v0.1.2 — find dev containers labelled by VS Code on Windows

Choose a tag to compare

@gambtho gambtho released this 13 Aug 14:34

Bug-fix release for a discovery defect found on real use.

The bug

stop reported "no running dev container" for a container running in front of the user, and the pane path's ambiguity check was silently checking an empty set.

Discovery matched on one label:

docker ps -a --filter label=devcontainer.local_folder=<repo-root>

Labels are written once, at container creation. VS Code on Windows creates them with local_folder holding the host's UNC view of the WSL path — \\wsl.localhost\Ubuntu\home\you\repo — which no POSIX repository root can equal.

Nothing repairs it afterward. Compose-based dev containers are reused by Compose's own project name, so devcontainer up kept handing back the VS Code-created container while discovery stayed blind to it: the container looked freshly opened by Herdr and then could not be stopped.

The fix

Discovery now also matches devcontainer.config_file, which the Dev Containers CLI resolves from inside WSL and which therefore holds a POSIX path on those same containers. Both keys stay exact-match — reconstructing the UNC form from WSL_DISTRO_NAME was rejected as guessing at how the host renders a path. Docker ANDs repeated --filter label arguments, so this is a second docker ps whose results are unioned and collapsed by container id.

Hardening on the same axis

Several paths could report a container as absent when the answer was simply unknown. All now say so instead:

  • An unreadable plugin config or an escaping repo-relative config value silently narrowed the search and reported the empty result as fact.
  • A docker ps timeout surfaced as docker command failed: with no detail.
  • A listing truncated by a read error, or by the 64 KiB capture cap, parsed cleanly and was indexed as the complete set of containers.
  • The two lookups are not atomic, so a container that started between them could be retained in its stale exited state.
  • detect already knew which config path existed and discarded it, costing an extra docker ps against a path known to be missing.
  • The multiple-containers error claimed every id carried devcontainer.local_folder=<root> — false in exactly the case it most often reports.

Install

herdr plugin install gambtho/herdr-devcontainer --ref v0.1.2

Full changelog: v0.1.1...v0.1.2

v0.1.1 — pane directory and container environment

Choose a tag to compare

@gambtho gambtho released this 12 Aug 06:44

Bug-fix release. Both fixes are in the exec path and both change observable behavior, so this is worth taking if you are on v0.1.0.

Fixed

Panes now open in the directory you were actually in. The working directory was read from the wrapper's own process, but Herdr runs a plugin pane from the plugin root — it has to, since manifest commands are plugin-relative. Every launch therefore looked like an out-of-repo checkout: every pane printed the "current directory is not under …" notice, and panes opened from a subdirectory silently started at the container workspace root. The directory now comes from Herdr's invocation context, and the notice is back to meaning what it says.

Panes now see the environment the container sets up for itself. sh -lc is not the container user's shell, and a login-but-not-interactive shell reads no rc file — so PATH entries and API endpoints written by postCreateCommand and friends were missing, and an agent pointed at a local model proxy by its container's setup script would quietly bypass it. Panes now run the container user's own shell, interactively.

New

  • repos."<root>".shell — override the shell instead of probing the container's passwd database.
  • repos."<root>".env — KEY=value assignments passed to docker exec -e, for environment a repository expresses only in devcontainer.json's remoteEnv.

Behavior worth knowing

  • bash is not run as a login shell. It reads ~/.bashrc only when interactive and not a login shell, so -l would suppress the very file this release exists to load. bash gets -i/-ic; every other known shell keeps -li/-lic and the /etc/profile values that come with it.
  • "Known" is a verified list. sh, ash, dash, busybox, zsh, ksh, mksh, pdksh, yash, and fish each accept those combined flags. Anything else is driven with bare -c, because not every shell takes them — tcsh -lic fails with "Unknown option", which would produce a pane that never opens. That fallback is the one case where a command pane is not interactive and no startup file is guaranteed.
  • rc files run now, and an rc file that changes directory wins. docker exec -w still places the shell correctly, but a cd in ~/.zshrc — some images bake one in — runs afterwards.
  • remoteEnv is still not parsed. Its effective value merges Features and image-label metadata that devcontainer up never reports, so reading devcontainer.json alone would produce a partial environment that reads as authoritative. A pane's environment comes from the image, the shell's startup files, and env — anything you depend on belongs in env.
  • Fallbacks say so. A shell probe that cannot answer prints why rather than silently using sh, and a context directory that will not resolve is named rather than quietly swapped.

Install this release

herdr plugin install gambtho/herdr-devcontainer --ref v0.1.1

Requirements

Unchanged from v0.1.0: Herdr 0.8.0+, Linux or WSL2, Git, Docker, the Dev Containers CLI, and Rust 1.74+ for the install-time build.

Known limitations

Unchanged from v0.1.0, including that a linked worktree checked out outside the main repository directory is not separately mounted, and panes opened there start at the container workspace root.

v0.1.0 — Dev Container panes for Herdr

Choose a tag to compare

@gambtho gambtho released this 12 Aug 01:35

Superseded by v0.1.1. Two defects in this release's exec path were found on first real use, so install v0.1.1 instead.

Two claims below are no longer accurate for the current plugin:

  • "Opens an interactive login shell (sh -l)" — panes now run the container user's own shell, interactively, because sh -l sources no rc file and so missed the PATH entries and API endpoints a container writes for itself.
  • "Maps a pane's current directory into remoteWorkspaceFolder when available" — in this release it never was available. The directory was read from the wrapper's own process, which Herdr runs from the plugin root, so every pane opened at the container workspace root and printed an out-of-repo notice.

The rest of this text describes v0.1.0 as shipped and is left unchanged.


First public release of a Herdr plugin for opening shell and coding-agent panes
inside a repository's existing Dev Container.

Highlights

  • Opens an interactive login shell (sh -l) inside the container.
  • Runs a configurable command inside the container; defaults to claude.
  • Uses the official Dev Containers CLI and the repository's own
    devcontainer.json — no second container format, and no editor required.
  • Detects both .devcontainer/devcontainer.json and .devcontainer.json, plus
    an alternate repo-relative config path.
  • Uses the main Git worktree as repository and container identity, so linked
    worktrees share one container instead of colliding on ports.
  • Refuses to guess when multiple running containers claim the same repository.
  • Serializes concurrent bring-up with a per-repository file lock.
  • Maps a pane's current directory into remoteWorkspaceFolder when available,
    and says so plainly when it cannot.
  • Explicit, confirmed stop — only y or yes proceeds, and the plugin never
    runs docker rm.
  • Runs host subprocesses as direct argv arrays and reports actionable errors.

Requirements

  • Herdr 0.8.0+
  • Linux or WSL2
  • Git
  • Docker with a reachable daemon
  • Dev Containers CLI (npm install -g @devcontainers/cli)
  • Rust 1.74+ and Cargo, for the install-time build

The committed Cargo.lock is resolved against Rust 1.74 rather than against the
newest published dependencies, and CI verifies that minimum on every change, so
the declared toolchain requirement is the one that actually compiles.

Install this release

herdr plugin install gambtho/herdr-devcontainer --ref v0.1.0

Installing from GitHub builds the plugin from source via the manifest's
cargo build --release hook.

Known limitations

  • macOS and native Windows are not supported.
  • GitHub installation builds from source and requires a Rust toolchain.
  • Linked worktrees share the main repository's container; a checkout outside the
    main repository directory is not separately mounted, and panes opened there
    start at the container workspace root.
  • The plugin uses its own XDG configuration path rather than Herdr's managed
    plugin config directory.
  • Nothing in the repository parses herdr-plugin.toml, so manifest validity is
    established by installing into a real Herdr, not by the Rust test suite.

Trust

Installing a Herdr plugin runs its build and runtime commands as your user, and
devcontainer up executes repository-controlled Dockerfile and lifecycle code.
This plugin inherits the Dev Containers trust model and is not a security
boundary. Review herdr-plugin.toml and the source before installing.