Skip to content
Agentmart
Submit a tool

docker-test-shell

Use when testing in a clean throwaway container.

Skill by gitricko

IndexedMIT★ 1
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/gitricko-docker-test-shell

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Use when testing in a clean throwaway container.

Use cases: testing, devops

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/docker-test-shell

What's inside

Skill name
docker-test-shell
Description
Use when testing in a clean throwaway container.
License
MIT
Instructions
127 lines in SKILL.md · view on GitHub

Before installing

  • Instructions call the network

Agent Skills spec issues

  • files: referenced but missing: scripts/dts.sh
  • body: contains an absolute path from the author's machine

Files (2)

  • SKILL.md
  • dts-usage.md · references/

SKILL.md (first lines)

# Docker Test Shell (`dts`)

Test ANY tool inside a fresh, throwaway Linux container — never trust the host.

## The one concept

> Test inside a throwaway container so your own environment's installed libs, stale state, and services do not pollute the test.

That is the entire tool. No test framework, no schema, no verify primitives. Just: get me into a clean container with my repo mounted in, and let me run commands. Target logic is PLAIN SHELL composed of `dts exec` + bash checks.

## Quickstart

```bash
# from the repo you want to test (it gets bind-mounted at /src)
./scripts/dts.sh up
./scripts/dts.sh exec "bash /src/install.sh"
./scripts/dts.sh exec "my-tool --version"
./scripts/dts.sh clean
```

For convenience, add the script dir to PATH or alias `dts`:
```bash
export PATH="$PWD/scripts:$PATH"   # or symlink dts.sh -> /usr/local/bin/dts
```

## Commands

| Command | Job |
|---------|-----|
| `dts up` | Create container (fresh base, repo bind-mounted at `/src`, uid-1000 user). Installs NOTHING. |
| `dts exec "<cmd>"` | Run `<cmd>` inside the container as the uid-1000 user, `bash -l`. TTY-aware. |
| `dts apt "<pkgs>"` | Run `apt-get update && install -y <pkgs>` as ROOT. The one privileged path (apt needs root; normal user can't). |
| `dts shell` | Drop into an interactive shell (`-it`). For a human at a terminal. |
| `dts clean` | Remove the container (no orphans). |
| `dts status` | Show container state + note the mount is present. |

## When to use `dts exec` vs raw `docker exec`

| Scenario | Use | Why | Notes |
|----------|-----|-----|-------|
| Normal test commands (build, version check, API call) | `dts exec` | Runs as uid-1000, `bash -l`, `-i` no `-t` — matches CI/Codespace parity | |
| Install apt packages (`apt-get install`) | `dts apt` | Dedicated root path; apt needs root, uid-1000 can't | |
| Debug interactively (human at terminal) | `dts shell` | `-it` gives full interactive TTY | |
| One-off root inspection (logs, sqlite, config files) | `docker exec -u 0:0 dts-test <cmd>` | Root-only operations outside apt; explicit `docker exec -u 0:0` signals intent | |
| Multi-step stateful commands from a script | `dts exec` with piped stdin | `printf 'cmd1\ncmd2\n' \| dts exec bash` — stdin stays open, runs sequentially | |

**Rule of thumb:** `dts exec` = the test harness path (uid-1000, parity). Raw `docker exec -u 0:0` = the escape hatch for root-only ops that aren't apt installs (e.g. reading sqlite DBs, checking process ports, killing stuck processes). Don't run test commands as root — it masks permission bugs that would fail in CI/Codespace.

## Config

- **Base image**: configurable via `IMAGE`, defaults to `ubuntu:24.04`:
  ```bash
  IMAGE=debian:12 dts up
  IMAGE=node:22 dts up
  ```
- **Container name**: `CONTAINER` (default `dts-test`).
- **Mount target**: always `/src`. Static, predictable.
- **uid/gid**: `CONTAINER_UID`/`CONTAINER_GID` (default 1000).

## The golden rules (each verified by live testing)

1. **Never trust the host.** Always repro in the container. The host has stale packages, leftover venvs, running services — a passing host test proves nothing about a fresh install.
2. **Drive with `docker exec -i`, never `-it`, when scripted.** `-it` fails with "the input device is not a TTY" when stdin is piped (an agent's normal mode). `-i` keeps stdin open, runs scripted AND stateful REPL-style commands fine. Reserve `-it` for a real human typing.
3. **Use `bash -l` (login shell).** Plain `bash -c` is non-interactive and doesn't source `.profile`. Login shell gives host/CI parity.
4. **Run as uid 1000, never root.** CI runner + Codespaces both run uid 1000; root masks permission bugs. `dts` does this automatically.
5. **Install apt packages with `dts apt`, never `dts exec`** — apt (and other package managers) need root, but the container's normal user runs as uid 1000 and can't. Use the dedicated root path:
   ```bash
   dts apt "curl g++ make"          # apt-get update && install -y, as root
   ```
   `dts exec` is the uid-1000 path for the actual test commands. Different targets need different prereqs; keep the tool minimal and make the choice explicit per test.
6. **Verify the mount with md5, not just listing.** Listing can lie; md5 proves same file (host write appears in container, container write appears on host, file md5-identical both sides).
7. **Clean up after yourself.** No orphan containers. `dts clean`.

## Verification is plain shell

There are no `verify` subcommands. Check exit codes, grep output, curl endpoints:

```bash
dts exec "hermes --version" && echo "install OK"
dts exec "curl -sf http://127.0.0.1:20128/healthz && echo server-up"
```

## Non-goals

- No test-definition / YAML schema (target logic = a short bash script).
- No `verify` subcommands.
- No Dockerfile / image build (bind-mount + base image, no rebuild cycle).
- No persistent SSH-like daemon (`docker exec -i` + piped stdin reproduces a stateful session).
- No bundled apt prereqs.

## How to teach/perpetuate this

1. **Never trust the host** — always repro in a fresh container.
2. **Learn the target iteratively** in `shell` mode (or one-off `exec`): poke, observe, confirm, then codify.
3. **Codify as a short bash script** of `dts exec` + checks — repeatable, and arguably CI-run-able.
4. **Record each pitfall** with root cause + fix (as references/ here or a wiki article).

## Pitfalls table (hard-won)

| Pitfall | Root cause | Fix |
|---------|-----------|-----|
| "input device is not a TTY" when scripted | `docker exec -it` needs a host PTY | Use `-i` (no `-t`) when stdin is piped; `-it` only for a real human |
| No `.profile` sourced | `bash -c` is non-interactive | Use `bash -l` (login shell) |
| Permission bugs masked | Tested as root | Always run as uid 1000 (CI/Codespace parity) |
| Mount "works" but file is stale | Bind-mount created once; container cached | Verify with md5 both sides; never trust a listing |
| Orphan containers eat disk | No cleanup | `dts clean` after each session |
| `rm -rf ~/.minions` fails "Device or resource busy" in DTS | Host ollama cache mounted at `$MINIONS_HOME/lib/ollama` via `-v /tmp/...:/home/.../lib/ollama` — `rm -rf` cannot remove a mountpoint | Mount cache outside the tree (`/tmp/cache:ro`) instead; if legacy mount remains, unmount with `umount -l` plus `sudo umount -l` fallback (container runs as uid 1000) |
| `mkdir -p lib/ollama` fails "Read-only filesystem" through symlink | Dir symlink `lib/ollama -> /tmp/cache:ro` makes `mkdir -p` traverse the ro target | Symlink files not dirs: `lib/ollama/ollama -> /tmp/cache/ollama`; `mkdir -p lib/ollama` then creates a real dir |
| `mkdir ~/.minions` fails "Permission denied" in DTS | Base image pre-creates `$HOME` owned by root; conditional `[ -d $home ] or chown` skipped the fix | Always `mkdir -p $home && chown $uid:$gid $home` unconditionally in `dts up` |
| Cache not mounted so install re-downloads and fails | Version string `v0.40.0` vs cache dir `ollama-0.40.0-...` — un-stripped `v` leaves `OLLAMA_HOST_CACHE` empty | Normalize `version=${version#v}` and map arch aliases before building the cache path |
| `bash install.sh \| tee log` reports success while install failed | Pipe exit is `tee`'s 0 when `pipefail` is off, masking left-side failure | Prefix with `set -o pipefail; cmd \| tee log` |
| `npm install` OOM `JavaScript heap out of memory` | `NODE_OPTIONS --max-old-space-size=512` too small for large package in constrained DTS/CI | Raise to 2048 in shared `install_npm_package` wrapper |
| `.tar.zst` fails `stdin: not in gzip format` or `zstd required` | Missing `zstd` in DTS container and `tar -xzf` used for `.tar.zst` (needs `--zstd`/`-I zstd`) | Install `zstd` in `dts apt` prereqs; extract via `zstd -dc \| tar -xf` or `tar --zstd -xf`; prefer host cache copy `/tmp/ollama-cache/ollama` on reinstall to avoid re-download |
| New optional binary re-downloads every DTS run | No `*_HOST_CACHE` mount for the new dep — same trap as Ollama before `OLLAMA_HOST_CACHE` | Mirror the Ollama pattern: mount host cache at `/tmp/<name>-cache:ro` (outside `MINIONS_HOME`), add `TAILSCALE_HOST_CACHE` handling in `scripts/dts.sh` and a fast-path `if [ -f /tmp/<name>-cache/<bin> ]` in `lib/<name>.sh` |
| Daemon needs TUN/`NET_ADMIN` but tests run as uid 1000 | `tailscaled` fails to create TUN, or second daemon collides on control socket when systemd already runs one | Default to `--tun=userspace-networking` with `--statedir` + `--socket` under `MINIONS_HOME` (`TAILSCALE_SOCKET`), presence-gate boot on `command -v`, guard `systemctl is-active`/`pgrep` before `start_service`, and keep `status.sh`/`self-check.sh` `set -e`-safe |

## Support files

- `scripts/dts.sh` — the tool itself (up/exec/shell/clean/status).

From gitricko/.minions, MIT.

More from gitricko/.minions

Signals

Stars
1
Forks
2
Open issues
3
Last commit (this folder)
Oct 11, 2026 (18 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from gitricko

Cite this page

docker-test-shell skill. Agent Mart. https://agentmart.dev/skills/gitricko-docker-test-shell (last updated 2026-10-24)