Execute CLI commands and tests inside the devcontainer. The host machine is macOS but the project binary is Linux — running CLI commands on the host will silently produce wrong results or fail. This skill prevents that mistake.
When to Use This
- Running
ss/skillsharecommands for verification - Running
go test,make test,make check - Reproducing a bug report
- Testing a feature you just implemented
- Starting the web UI dashboard
- Any command that needs the skillshare binary or Go toolchain
When NOT to Use This
- Editing source code (do that on host via Read/Edit tools)
- Running
gitcommands (git works on host) - Running
make fmt,make lint(host-safe Go toolchain commands; no container needed) - E2E test runbooks → use
cli-e2e-testskill instead (it handles ssenv isolation)
Architecture: Two Layers of Isolation
Host (macOS)
└─ Devcontainer (Linux, Debian-based)
├─ Default HOME: /home/developer (persistent volume)
├─ Source: /workspace (bind-mount of repo root)
└─ ssenv environments: ~/.ss-envs/<name>/ (isolated HOME dirs)
Devcontainer = Linux environment with Go, git, pnpm, air (hot-reload). Source code is at /workspace (bind-mount of the host repo). The ss / skillshare wrapper auto-builds from source on every invocation — no manual make build needed. Edit code on the host, then immediately docker exec to run it; the change is picked up automatically.
ssenv = Isolated HOME directories within the devcontainer. Each env gets its own ~/.config/skillshare/, ~/.claude/, etc. Use ssenv when you need a clean state (testing init, install, sync) without polluting the container's default HOME.
Zero-Rebuild Workflow
Source code is bind-mounted into the container at /workspace. The ss wrapper runs go build transparently on every invocation:
- Edit files on host (Read/Edit tools)
docker exec $CONTAINER ss <command>— picks up your changes instantly- No
make build, no restart, no rebuild step
This also applies to go test — tests always compile against the latest source. The Web UI backend uses air for hot-reload (same zero-rebuild experience).
Entering the Devcontainer
The quickest way — one command builds, initialises, and enters the shell:
make devc # build + init + interactive shell (one step)
make devc-up # start only (no shell)
make devc-down # stop
make devc-restart # restart + re-run start-dev.sh
make devc-reset # full reset (remove volumes), then `make devc` to re-init
make devc-status # show container status
Works with or without VS Code — make devc handles the full lifecycle autonomously.
Programmatic access (for docker exec workflows)
CONTAINER=$(docker compose -f .devcontainer/docker-compose.yml ps -q skillshare-devcontainer 2>/dev/null)
If $CONTAINER is empty, tell the user:
Devcontainer is not running. Start it with
make devc-up.
Then verify the binary:
docker exec $CONTAINER bash -c \
'/workspace/.devcontainer/ensure-skillshare-linux-binary.sh && ss version'
Running Commands
Simple command (uses container's default HOME)
docker exec $CONTAINER ss <command> [flags]
Good for: ss version, ss status, ss list, ss check, ss audit.
Command with isolated HOME (clean state)
ENV_NAME="test-$(date +%s)"
docker exec $CONTAINER ssenv create "$ENV_NAME" --init
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- ss status
# Cleanup when done:
docker exec $CONTAINER ssenv delete "$ENV_NAME" --force
Good for: testing init, install, sync, uninstall — anything that modifies config/state.
Multi-command sequence
docker exec $CONTAINER ssenv enter "$ENV_NAME" -- bash -c '
ss install runkids/demo-skills --track --force
ss list
ss sync
'
Always use bash -c '...' for multi-command sequences inside ssenv enter.
Go tests
# All tests (unit + integration)
docker exec $CONTAINER bash -c 'cd /workspace && make test'
# Unit tests only
docker exec $CONTAINER bash -c 'cd /workspace && make test-unit'
# Integration tests only
docker exec $CONTAINER bash -c 'cd /workspace && make test-int'
# Specific test
docker exec $CONTAINER bash -c 'cd /workspace && go test ./tests/integration -run TestInit_Fresh -count=1'
# Specific package
docker exec $CONTAINER bash -c 'cd /workspace && go test ./internal/install/... -count=1'
Always cd /workspace before Go commands — ssenv changes HOME which can break module resolution.
Go tests with auth disabled
Some tests (e.g., TestResolveToken, TestAuthEnv) need auth credentials removed:
docker exec $CONTAINER bash -c '
eval "$(credential-helper --eval off)"
cd /workspace
go test ./internal/github -run TestResolveToken -count=1
eval "$(credential-helper --eval on)"
'
Web UI Dashboard
# Start (global mode)
docker exec $CONTAINER ui
# Start (project mode — uses ~/demo-project)
docker exec $CONTAINER ui -p
# Stop
docker exec $CONTAINER ui stop
Dashboard accessible at http://localhost:5173 (Vite dev server with HMR).
API backend at http://localhost:19420.
Logs: /tmp/api-dev.log, /tmp/vite-dev.log.
ssenv Quick Reference
| Shortcut | Full form | Purpose |
|---|---|---|
ssnew <name> |
ssenv create <name> + enter |
Create and enter isolated shell |
ssuse <name> |
ssenv enter <name> |
Enter existing isolated shell |
ssrm <name> |
ssenv delete <name> --force |
Delete environment |
ssls |
ssenv list |
List all environments |
ssback |
ssenv reset |
Leave isolated context |
sshelp |
help |
Show all devcontainer commands |
For automation (non-interactive), prefer ssenv enter <name> -- <command> over ssnew/ssuse (which launch subshells).
Ports
| Port | Service | Notes |
|---|---|---|
| 5173 | Vite dev server | React dashboard with HMR |
| 19420 | Go API backend | skillshare ui server |
| 3000 | Docusaurus | docs command in devcontainer |
Common Mistakes to Avoid
- Running
sson host — macOS binary won't match Linux container; alwaysdocker exec - Forgetting
cd /workspace— Go tests fail if HOME was changed by ssenv - Using
make teston host — builds macOS binary, then tests run against wrong arch - Skipping
--initon ssenv create — env won't have config; most commands will fail - Not cleaning up ssenv —
ssenv delete <name> --forceafter done; or ask user - Running from /workspace root without -g — the
sswrapper auto-redirects to~/demo-projectin project mode; use-gfor global or setSKILLSHARE_DEV_ALLOW_WORKSPACE_PROJECT=1 - Running
make buildbefore testing — unnecessary; thesswrapper auto-builds from source every time
Rules
- All CLI execution inside devcontainer — no exceptions
- Use ssenv for stateful tests — don't pollute default HOME
- Always verify — run the command and check output; never assume it worked
- Clean up — delete ssenv environments after use (or ask user)
- Report container ID — set
$CONTAINERat the start and reuse throughout