Releases: webcane/docker-deploy
Release list
v0.6.0
v0.3.0
Phase 7: v2 Leftovers
Goal: Ship a wave of small v2 quality-of-life improvements: expand the built-in exclude list to cover common dev-tooling directories, add a --skip-env flag so operators can preserve remote secrets across deploys, and add a --verbose flag for detailed deploy output.
Status: Verified ✓ (7/7 observable truths; live SSH end-to-end pending human confirmation)
What's New
--skip-env flag
Operators can now preserve remote secrets across deploys:
docker deploy --host ssh://user@host:22 --skip-env- Excludes
.envfrom SFTP upload, leaving the remote copy untouched - Configurable via
deploy.yamlwithskip_env: true - CLI flag takes precedence when both are set
- Additive — appends
.envto the effective exclude list without replacing other excludes - Prints a visible warning when active:
WARNING: .env not uploaded — remote .env left unchanged - Remote
.envis preserved across atomic swap — backed up to/tmp/docker-deploy-env-<ts>before the swap and restored after
--verbose flag
Detailed deploy output on demand:
docker deploy --host ssh://user@host:22 --verbose- Per-file transfer lines (
-> path/to/file) - SSH command lines (
[ssh] docker compose up -d) - Exit code lines (
→ exit 0) - Preflight checklist lines (
[PASS] docker-compose-v2,[WARN] passwordless-sudo) - Without
--verbose: output remains terse; a single rollup line appears if any warnings accumulated
Expanded default exclude list
10 new entries added to the built-in exclude list (from 6 → 16 total):
| Added | Purpose |
|---|---|
.claude/ |
Claude AI context files |
.github/ |
GitHub Actions / workflows |
.planning/ |
GSD planning artifacts |
.idea/ |
JetBrains IDE settings |
.vscode/ |
VS Code settings |
*.swp, *.swo |
Vim swap files |
coverage/ |
Test coverage output |
dist/ |
Build artifacts |
.terraform/ |
Terraform state |
These are silently skipped unless the user explicitly re-includes them.
Warning rollup system
Non-verbose mode now accumulates warnings and prints a single summary line at the end:
WARN: there are some warnings during deployment. For more details use --verbose flag
Bug Fixes
- Remote
.envpreserved across atomic swap when--skip-envis set — previously the atomic mv would overwrite the remote.enveven when skip-env was active; now backs up and restores - Warning rollup fires in both verbose and non-verbose mode — fixed a guard bug where the rollup was only shown in verbose mode
- Dead
sshExecVerbosefunction removed — replaced by the inline[ssh]logging approach - Deploy complete status message format — omit colon when using default SSH port (
:22case)
Internal Changes
config.FlagOptsstruct replaces the 10-positional-argResolve()signature — cleaner call sites, easier to extendUpload()andRunCompose()gainverbose boolparam propagated fromresolved.VerbosebuildDeployCmd()extracted frommain()for cobra flag registration testability- All packages: 100% passing unit tests (33 commits, 6 testable packages)
Requirements Addressed
| Req | Description |
|---|---|
| FILES-02 | Default exclude list: .git/, node_modules/, vendor/, *.log, .DS_Store, __pycache__/ (+ 10 new entries) |
Full Changelog
v0.2.0
Summary
Phase 4: Core Deploy Loop
Goal: A developer can deploy a local compose project to a remote VPS with a single command and see compose output streamed to their terminal.
Status: Verified ✓
This release wires together file copy (Phase 3) and compose execution into a single docker deploy command. Deploy to any SSH-accessible VPS — no git on the remote required. Output is streamed line-by-line; the plugin exits non-zero on any failure.
Changes
Plan 01: Config ComposeFile Resolution
Config package extended with three-tier compose file selection: --compose-file flag → deploy.yaml → auto-detect (compose.yaml then docker-compose.yml).
Key files: internal/config/config.go, internal/config/config_test.go
Plan 02: RunCompose SSH Execution
New internal/compose package with RunCompose(). Executes docker compose -f <path> up -d --remove-orphans on the remote host. PTY detection routes output: allocated xterm-256color PTY when running in a terminal; two goroutines drain stdout/stderr in CI/piped mode.
Key files: internal/compose/run.go, internal/compose/run_test.go
Plan 03: Integration & Wiring
Full deploy loop wired into main.go: --compose-file flag, basename injection guard, Upload() then RunCompose(). Verified end-to-end against a real SSH host across 6 scenarios including compose validation errors and non-zero exit code propagation.
Key files: cmd/docker-deploy/main.go
Plan 04: Auth Fallback Sequence
Structured three-stage auth fallback in Upload(): direct copy → passwordless sudo (sudo -n) → interactive password prompt (3 retry attempts). Password collected once and reused across all privileged operations (mkdir, mv, rm). Previously, deployments to /opt or other root-owned directories would fail; now they proceed via sudo with clear per-stage warnings.
Key files: internal/filetransfer/upload.go, internal/filetransfer/upload_test.go, cmd/docker-deploy/main.go
Requirements Addressed
- DEPLOY-01 —
docker deploy --host ssh://user@hostcompletes the full copy-then-compose cycle without additional flags - DEPLOY-04 —
docker compose up -d --remove-orphansexecutes on the remote host after files are copied - DEPLOY-05 — Plugin exits non-zero if file copy fails, compose fails, or SSH connectivity is lost mid-deploy
- DEPLOY-06 — Compose output is streamed line-by-line to the local terminal (PTY or piped)
- DEPLOY-07 — Auth fallback sequence: direct copy → passwordless sudo → interactive password prompt
Verification
- Automated: 53 tests pass, 5 skipped (interactive password tests — stdin mocking deferred), 0 failures
-
go build ./...andgo vet ./...clean - Human UAT: 6 end-to-end scenarios verified against real SSH host (192.168.1.99)
- No
InsecureIgnoreHostKeyin production code - No TBD/FIXME/XXX debt markers in production code
Key Decisions
RunCompose()usessession.Start()notsession.Run()— allowswg.Wait()beforesession.Wait()to ensure pipe drain before exit status check- Basename validation (
filepath.Base(composeFile) == composeFile) placed inrunDeploy()at the trust boundary, not insideRunCompose() context.Background()passed toRunCompose— compose up -d is detached and returns quickly; no separate deadline needed- Password collected once via
golang.org/x/term.ReadPasswordand reused across all sudo operations in a single deploy - Staging in
/tmp/docker-deploy-<ts>avoids/optpermission issues; target dir creation is a separate step with sudo fallback
Quick Fixes (also in this release)
| Fix | Commit | Description |
|---|---|---|
| 260519-c216 | 833cf07 | Eliminate hanging goroutines in RunCompose tests — refactor mock SSH server, fix defer ordering |
| 260519-oax | 40dc518 | Deduplicate "passwordless sudo not configured" warning — show once by default |
| 260519-q02 | 703d819 | Fix health check docker inspect exit status 1 for containers with no HEALTHCHECK directive |