dockhand-cli 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dockhand_cli-0.3.1/.claude/settings.local.json +9 -0
- dockhand_cli-0.3.1/.dockhand.json +14 -0
- dockhand_cli-0.3.1/.gitignore +53 -0
- dockhand_cli-0.3.1/AGENTS.md +309 -0
- dockhand_cli-0.3.1/CLAUDE.md +309 -0
- dockhand_cli-0.3.1/LICENSE +7 -0
- dockhand_cli-0.3.1/PKG-INFO +478 -0
- dockhand_cli-0.3.1/README.md +461 -0
- dockhand_cli-0.3.1/dockhand/__init__.py +335 -0
- dockhand_cli-0.3.1/dockhand/build.py +35 -0
- dockhand_cli-0.3.1/dockhand/client/__init__.py +38 -0
- dockhand_cli-0.3.1/dockhand/client/base.py +37 -0
- dockhand_cli-0.3.1/dockhand/client/local.py +45 -0
- dockhand_cli-0.3.1/dockhand/client/ssh.py +53 -0
- dockhand_cli-0.3.1/dockhand/config.py +355 -0
- dockhand_cli-0.3.1/dockhand/constants.py +2 -0
- dockhand_cli-0.3.1/dockhand/download.py +53 -0
- dockhand_cli-0.3.1/dockhand/error.py +10 -0
- dockhand_cli-0.3.1/dockhand/history.py +171 -0
- dockhand_cli-0.3.1/dockhand/manage.py +459 -0
- dockhand_cli-0.3.1/dockhand/queue.py +174 -0
- dockhand_cli-0.3.1/dockhand/resubmit.py +49 -0
- dockhand_cli-0.3.1/dockhand/submit.py +163 -0
- dockhand_cli-0.3.1/dockhand/sync.py +50 -0
- dockhand_cli-0.3.1/dockhand/tagging.py +48 -0
- dockhand_cli-0.3.1/dockhand/transport.py +212 -0
- dockhand_cli-0.3.1/dockhand/tunnel.py +62 -0
- dockhand_cli-0.3.1/dockhand/volumes.py +149 -0
- dockhand_cli-0.3.1/media/Gemini_Generated_Image_i700fvi700fvi700.png +0 -0
- dockhand_cli-0.3.1/pyproject.toml +38 -0
- dockhand_cli-0.3.1/uv.lock +595 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sync": true,
|
|
3
|
+
"ssh": {
|
|
4
|
+
"hostname": "test.example.com",
|
|
5
|
+
"user": "testuser",
|
|
6
|
+
"identityfile": "~/.ssh/id_rsa"
|
|
7
|
+
},
|
|
8
|
+
"docker": {
|
|
9
|
+
"dockerfile": "Dockerfile",
|
|
10
|
+
"imagename": "test:latest",
|
|
11
|
+
"containerworkdir": "/workdir",
|
|
12
|
+
"volumes": []
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Project-specific
|
|
2
|
+
.dtu_hpc.json
|
|
3
|
+
.dtu_docker_history.json
|
|
4
|
+
|
|
5
|
+
# Python
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
*$py.class
|
|
9
|
+
*.so
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# Virtual environments
|
|
31
|
+
venv/
|
|
32
|
+
ENV/
|
|
33
|
+
env/
|
|
34
|
+
.venv
|
|
35
|
+
|
|
36
|
+
# IDE
|
|
37
|
+
.vscode/
|
|
38
|
+
.idea/
|
|
39
|
+
*.swp
|
|
40
|
+
*.swo
|
|
41
|
+
*~
|
|
42
|
+
.DS_Store
|
|
43
|
+
|
|
44
|
+
# Testing
|
|
45
|
+
.pytest_cache/
|
|
46
|
+
.coverage
|
|
47
|
+
htmlcov/
|
|
48
|
+
.tox/
|
|
49
|
+
|
|
50
|
+
# MyPy
|
|
51
|
+
.mypy_cache/
|
|
52
|
+
.dmypy.json
|
|
53
|
+
dmypy.json
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# AGENTS.md — dockhand Development Guide
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**dockhand** is a standalone CLI tool, originally extracted from DTU-HPC-CLI, for managing Docker containers on remote machines (or locally). It provides a unified interface to build, run, queue, manage, and monitor Docker containers via SSH or locally.
|
|
6
|
+
|
|
7
|
+
**Key philosophy:** Simple, flat command structure with sensible SSH defaults. One `.dockhand.json` config file works everywhere.
|
|
8
|
+
|
|
9
|
+
Since extraction, dockhand has grown features DTU-HPC-CLI never had: a task-spooler-based job queue, a mount-vs-bake code delivery mode, a direct (non-queued) run transport, and baked-image pruning. See [Relationship to DTU-HPC-CLI](#relationship-to-dtu-hpc-cli).
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
**Prerequisites:**
|
|
14
|
+
- Install [uv](https://docs.astral.sh/uv/): `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
|
15
|
+
|
|
16
|
+
**Installation & Setup:**
|
|
17
|
+
```bash
|
|
18
|
+
# Install dependencies and create lock file
|
|
19
|
+
uv sync
|
|
20
|
+
|
|
21
|
+
# Update lock file with latest versions (if needed)
|
|
22
|
+
uv lock --upgrade
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Run the CLI locally:**
|
|
26
|
+
```bash
|
|
27
|
+
uv run dockhand --help
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Lint:**
|
|
31
|
+
```bash
|
|
32
|
+
uv run ruff check .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Format:**
|
|
36
|
+
```bash
|
|
37
|
+
uv run ruff format .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Auto-fix lint issues:**
|
|
41
|
+
```bash
|
|
42
|
+
uv run ruff check --fix .
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Architecture
|
|
46
|
+
|
|
47
|
+
### Entry Point
|
|
48
|
+
`dockhand/__init__.py` — Defines the Typer CLI app. All commands are at the top level (flat structure, not nested under a `docker` sub-group). Each command validates config with `cli_config.check_docker()` and delegates to an `execute_*` function in the relevant module below.
|
|
49
|
+
|
|
50
|
+
**Commands at top level:**
|
|
51
|
+
- `dockhand submit` — sync code, then queue/run a container
|
|
52
|
+
- `dockhand run` — queue/run from an already-built image, no sync
|
|
53
|
+
- `dockhand install` — build the image only
|
|
54
|
+
- `dockhand logs`, `stop`, `remove`, `jobs`, `urgent`, `prune` — job/queue lifecycle (`manage.py` + `queue.py`)
|
|
55
|
+
- `dockhand history` — show past runs
|
|
56
|
+
- `dockhand volumes`, `download` — inspect and pull files from mounted volumes
|
|
57
|
+
- `dockhand resubmit` — rerun a past job with optional overrides
|
|
58
|
+
- `dockhand tunnel` — SSH-forward container ports to localhost
|
|
59
|
+
|
|
60
|
+
### Configuration System
|
|
61
|
+
`dockhand/config.py` — The `CLIConfig` class is loaded at module import time (`cli_config = CLIConfig.load()`) by walking up the directory tree to find `.dockhand.json`. Contains:
|
|
62
|
+
- `SSHConfig` — hostname, user, identity file for SSH connections
|
|
63
|
+
- `DockerConfig` — dockerfile, imagename, volumes, ports, gpus, containerworkdir, preserve_paths, code_delivery
|
|
64
|
+
- `QueueConfig` — enabled, tool, slots (task-spooler queue settings)
|
|
65
|
+
- `CLIConfig` — top-level config holder (ssh, docker, queue, sync, remote_path, profiles)
|
|
66
|
+
|
|
67
|
+
Config file: `.dockhand.json` in the project root (must also contain a `.git` — `CLIConfig.load()` errors if no git repo is found). History file: `.dockhand_history.json` (same location by default, overridable via `history_path`).
|
|
68
|
+
|
|
69
|
+
### Client Abstraction
|
|
70
|
+
`dockhand/client/` — `Client` (abstract base) has two implementations:
|
|
71
|
+
- `SSHClient` — connects via Fabric/Paramiko over SSH
|
|
72
|
+
- `LocalClient` — runs commands locally
|
|
73
|
+
|
|
74
|
+
`get_client()` picks `LocalClient` when `cli_config.ssh` is unset or the configured hostname resolves to loopback, otherwise `SSHClient`. `get_client_for_host(hostname)` does the same but for an explicit hostname (used by `logs`/`stop`/`remove`, which read the host from the job's history entry rather than current config).
|
|
75
|
+
|
|
76
|
+
### Key Modules
|
|
77
|
+
- `submit.py` — Builds the `docker run` command (code mount vs. data volumes vs. GPU/port flags), resolves code delivery, and hands off to a transport to start the job.
|
|
78
|
+
- `build.py` — Runs `docker build` on the client (optionally syncing first).
|
|
79
|
+
- `manage.py` — Job lifecycle: `logs`, `stop`, `remove`, `jobs` (`execute_stats`), and `prune` (removes baked images no longer referenced by an active job).
|
|
80
|
+
- `resubmit.py` — Looks up a history entry and re-invokes `execute_submit` with overrides; pins to the original baked image when applicable.
|
|
81
|
+
- `queue.py` — Task spooler (`tsp`) integration: submit/list/promote/remove/kill, plus `ts -l` output parsing.
|
|
82
|
+
- `transport.py` — Abstracts "how a job runs": `TaskSpoolerTransport` (queue enabled) vs. `DockerTransport` (direct `docker run -d`). Both expose the same interface (`submit`, `list_jobs`, `logs`, `stop`, `remove`) so job-management commands don't care which backend created a job; the transport used is recorded per job in history.
|
|
83
|
+
- `tagging.py` — Resolves the image tag/ref for baked code delivery (content-addressed from git commit + dirty-state hash; unique per queued submit, reused for direct runs).
|
|
84
|
+
- `history.py` — Reads/writes `.dockhand_history.json`, reserves/looks up local job IDs.
|
|
85
|
+
- `volumes.py` — Lists the container filesystem as a tree (code mount + data volumes) and resolves a workdir-relative path back to its host path.
|
|
86
|
+
- `download.py` — Uses `volumes._resolve_to_host` + rsync to pull a file/directory from a volume.
|
|
87
|
+
- `tunnel.py` — SSH local port forwarding to container ports (via Fabric).
|
|
88
|
+
- `sync.py` — Uses `rsync` over SSH to copy local files to `remote_path`, respecting `.gitignore`; prompts to confirm when the worktree is dirty.
|
|
89
|
+
- `config.py` — Configuration loading and validation.
|
|
90
|
+
- `error.py` — Centralized error reporting with rich panels.
|
|
91
|
+
- `constants.py` — Config/history filenames.
|
|
92
|
+
|
|
93
|
+
> **Known inconsistency:** `tunnel.py` still looks up history entries by a `container_id` field (`entry["container_id"]`), but `history.py` no longer writes that field — entries are keyed by `local_id` with a transport `handle`. Passing an explicit `container_id` to `dockhand tunnel` will not resolve; only the no-argument (last job) path works reliably.
|
|
94
|
+
|
|
95
|
+
### Docker History
|
|
96
|
+
Stores container runs in `.dockhand_history.json` as JSON. Each entry contains:
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"local_id": 7,
|
|
100
|
+
"timestamp": 1234567890.123,
|
|
101
|
+
"config": {
|
|
102
|
+
"gpus": "all",
|
|
103
|
+
"volumes": [...],
|
|
104
|
+
"imagename": "my-image",
|
|
105
|
+
"commands": ["python", "train.py"],
|
|
106
|
+
"ports": ["6006:6006"],
|
|
107
|
+
"image_ref": "my-image:abc123def456", // present when built (baked delivery)
|
|
108
|
+
"branch": "main" // optional, detected from git
|
|
109
|
+
},
|
|
110
|
+
"transport": "task_spooler", // or "docker"
|
|
111
|
+
"handle": 12, // tsp job id, or container name for direct runs
|
|
112
|
+
"ts_job_id": 12, // task_spooler transport only
|
|
113
|
+
"host": "remote.example.com", // or "localhost"
|
|
114
|
+
"started_at": 1234567891.0, // optional, set the first time `jobs` observes it running
|
|
115
|
+
"ended_at": 1234567895.0 // optional, set the first time `jobs` observes it finished/failed/stopped
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Used by `resubmit` (look up a previous run and re-run with overrides, pinning the original baked image when unchanged), `logs`, `stop`, `remove`, `jobs`, `urgent`, `prune` (default to latest if no ID given). Job-management commands dispatch to the transport recorded on the entry (`transport_for_entry`), so `logs`/`stop`/`remove` work the same regardless of whether a job went through the queue or ran directly.
|
|
120
|
+
|
|
121
|
+
`started_at`/`ended_at` aren't queried from tsp/docker (neither exposes exact start/end timestamps cheaply for both transports) — `jobs` and `logs` each stamp them lazily the first time they happen to observe a job in the running/terminal state, so a job never checked on while running will show no start time once it finishes. `dockhand jobs` displays these as Started/Ended (absolute, `%Y-%m-%d %H:%M:%S`) and Duration (elapsed while running, total once finished) columns; `dockhand logs` prints a one-line "running for Xm" / "finished in Xm" header before the log output.
|
|
122
|
+
|
|
123
|
+
### Design Patterns
|
|
124
|
+
|
|
125
|
+
**DockerDefault factory:**
|
|
126
|
+
In `__init__.py`, `DockerDefault` is a callable factory that pulls defaults from `cli_config.docker` at call time. Enables Typer's `default_factory` to respect active profiles.
|
|
127
|
+
|
|
128
|
+
**Transport abstraction:**
|
|
129
|
+
`get_transport()` in `transport.py` picks `TaskSpoolerTransport` or `DockerTransport` based on `cli_config.queue.enabled` at submit time. Every job-management command re-derives the correct transport per-job from the history entry (`transport_for_entry`) rather than from current config, so switching `queue.enabled` doesn't strand in-flight jobs from the other mode.
|
|
130
|
+
|
|
131
|
+
**Code delivery resolution:**
|
|
132
|
+
`DockerConfig.resolve_code_delivery(queue_enabled)` picks `mount` or `bake` when `code_delivery` isn't explicitly set: `bake` when the queue is enabled (avoids code drift while a job waits in queue), `mount` otherwise (zero-rebuild iteration). See README's [Code delivery](README.md#code-delivery-mount-vs-bake) section for the full rationale.
|
|
133
|
+
|
|
134
|
+
**Profile support:**
|
|
135
|
+
`cli_config.load_profile(name)` loads overrides from `.dockhand.json` profiles section (currently merges `history_path`, `remote_path`, `ssh`). All config classes support `validate()` for safe merging.
|
|
136
|
+
|
|
137
|
+
**Command structure:**
|
|
138
|
+
Each command:
|
|
139
|
+
1. Calls `cli_config.check_docker()` to validate config
|
|
140
|
+
2. Calls an `execute_*` function from the relevant module
|
|
141
|
+
3. Passes `cli_config.docker` (and often `cli_config.queue`) as config arguments
|
|
142
|
+
|
|
143
|
+
## Relationship to DTU-HPC-CLI
|
|
144
|
+
|
|
145
|
+
- **Flat commands** — no `docker` sub-group. Commands are top-level.
|
|
146
|
+
- **No HPC support** — SubmitConfig, InstallConfig, and all LSF/job submission code removed
|
|
147
|
+
- **Docker-focused** — all UI/UX optimized for docker workflows
|
|
148
|
+
- **Config-compatible in spirit, not in filename** — dockhand originally reused DTU-HPC-CLI's `.dtu_hpc.json`/`.dtu_docker_history.json` names; both have since been renamed to `.dockhand.json`/`.dockhand_history.json` (see `constants.py`). Old DTU-HPC-CLI config files need renaming (or a `history_path` override) to work with dockhand.
|
|
149
|
+
- **Diverged, not just trimmed** — dockhand has added its own features DTU-HPC-CLI doesn't have: a task-spooler queue (`queue.py`/`transport.py`), slot reservations, `mount`/`bake` code delivery (`tagging.py`), a direct-run transport for when the queue is off, `urgent`/`prune`/`tunnel` commands. Maintained independently; version/release cadence is separate from DTU-HPC-CLI.
|
|
150
|
+
|
|
151
|
+
## Key Files and Their Responsibilities
|
|
152
|
+
|
|
153
|
+
| File | Purpose |
|
|
154
|
+
|------|---------|
|
|
155
|
+
| `__init__.py` | CLI app definition, command routing, config defaults |
|
|
156
|
+
| `submit.py` | Builds the `docker run` command, resolves code delivery, submits via transport |
|
|
157
|
+
| `build.py` | `docker build` execution |
|
|
158
|
+
| `manage.py` | `logs`, `stop`, `remove`, `jobs`, `prune` |
|
|
159
|
+
| `resubmit.py` | Rerun a past job with overrides |
|
|
160
|
+
| `queue.py` | Task spooler (`tsp`) integration |
|
|
161
|
+
| `transport.py` | Task-spooler vs. direct-docker execution backends |
|
|
162
|
+
| `tagging.py` | Image tag resolution for baked code delivery |
|
|
163
|
+
| `history.py` | Read/write `.dockhand_history.json`, job ID allocation |
|
|
164
|
+
| `volumes.py` | Container filesystem tree, path resolution |
|
|
165
|
+
| `download.py` | rsync-based file download from volumes |
|
|
166
|
+
| `tunnel.py` | SSH port forwarding to container ports |
|
|
167
|
+
| `config.py` | Config loading, validation, profile support |
|
|
168
|
+
| `client/__init__.py` | Auto-detect and return appropriate client |
|
|
169
|
+
| `client/base.py` | Abstract Client interface |
|
|
170
|
+
| `client/local.py` | Local command execution |
|
|
171
|
+
| `client/ssh.py` | Remote execution via SSH (Fabric/Paramiko) |
|
|
172
|
+
| `sync.py` | rsync-based code synchronization |
|
|
173
|
+
| `error.py` | Error reporting |
|
|
174
|
+
| `constants.py` | Config/history file names |
|
|
175
|
+
|
|
176
|
+
## Configuration Format
|
|
177
|
+
|
|
178
|
+
`.dockhand.json` in project root (project root is also where `CLIConfig.load()` expects to find a `.git` directory):
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
{
|
|
182
|
+
"sync": true,
|
|
183
|
+
"ssh": {
|
|
184
|
+
"user": "your_username",
|
|
185
|
+
"identityfile": "~/.ssh/id_rsa",
|
|
186
|
+
"hostname": "remote.example.com"
|
|
187
|
+
},
|
|
188
|
+
"queue": {
|
|
189
|
+
"enabled": true,
|
|
190
|
+
"slots": 1
|
|
191
|
+
},
|
|
192
|
+
"docker": {
|
|
193
|
+
"dockerfile": "Dockerfile",
|
|
194
|
+
"imagename": "my-image",
|
|
195
|
+
"volumes": [
|
|
196
|
+
{
|
|
197
|
+
"hostpath": "/local/data",
|
|
198
|
+
"containerpath": "/data",
|
|
199
|
+
"permissions": "rw"
|
|
200
|
+
}
|
|
201
|
+
],
|
|
202
|
+
"ports": ["8080:80"],
|
|
203
|
+
"gpus": "all",
|
|
204
|
+
"containerworkdir": "/",
|
|
205
|
+
"preserve_paths": [".venv"],
|
|
206
|
+
"code_delivery": null
|
|
207
|
+
},
|
|
208
|
+
"remote_path": "~/my-project",
|
|
209
|
+
"profiles": {
|
|
210
|
+
"dev": {
|
|
211
|
+
"docker": {
|
|
212
|
+
"gpus": "1",
|
|
213
|
+
"dockerfile": "Dockerfile.dev"
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
All options are optional except `dockerfile`, `imagename`, and `volumes` within docker config. `queue.slots` may also be set as `docker.slots` for back-compat (deprecation warning printed). Full option tables and defaults are documented in README.md's [Configuration](README.md#configuration) section — keep both in sync when config shape changes.
|
|
221
|
+
|
|
222
|
+
## Common Workflows
|
|
223
|
+
|
|
224
|
+
**First time setting up:**
|
|
225
|
+
1. Create `.dockhand.json` with `docker` and `ssh` sections
|
|
226
|
+
2. Run `dockhand install` to build the image
|
|
227
|
+
3. Run `dockhand submit 'command'` to run a container
|
|
228
|
+
|
|
229
|
+
**Development iteration:**
|
|
230
|
+
```bash
|
|
231
|
+
uv run dockhand submit --gpus 1 'python train.py' # Sync + queue/run with 1 GPU
|
|
232
|
+
uv run dockhand logs --n 50 # Check last 50 log lines
|
|
233
|
+
uv run dockhand resubmit --gpus 2 # Re-run with 2 GPUs
|
|
234
|
+
uv run dockhand download results/model.pth # Get results back
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Quick rebuild:**
|
|
238
|
+
```bash
|
|
239
|
+
uv run dockhand install --dockerfile Dockerfile.dev # Rebuild only
|
|
240
|
+
uv run dockhand run 'bash' # Run interactively
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Using profiles:**
|
|
244
|
+
```bash
|
|
245
|
+
uv run dockhand --profile dev submit 'python train.py' # Use dev profile
|
|
246
|
+
uv run dockhand --profile prod install # Build prod image
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Queue management:**
|
|
250
|
+
```bash
|
|
251
|
+
uv run dockhand jobs # List active jobs
|
|
252
|
+
uv run dockhand urgent 5 # Promote job #5 to front of queue
|
|
253
|
+
uv run dockhand prune --dry-run # Preview unused baked images before removing
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Testing & Validation
|
|
257
|
+
|
|
258
|
+
No automated tests yet. Manual validation:
|
|
259
|
+
```bash
|
|
260
|
+
uv run dockhand --help # Check CLI structure
|
|
261
|
+
uv run dockhand submit --help # Check submit options
|
|
262
|
+
uv run ruff check . # Lint
|
|
263
|
+
uv run ruff format --check . # Format check
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Important Design Decisions
|
|
267
|
+
|
|
268
|
+
1. **Flat commands** — No `docker` sub-group. Simpler for a docker-only tool.
|
|
269
|
+
2. **SSH by default** — Assumes docker host is remote. Auto-detects local by resolving the configured hostname to a loopback address.
|
|
270
|
+
3. **History in JSON** — Simple, human-readable, easily editable.
|
|
271
|
+
4. **Profile support** — Allows per-project config variants.
|
|
272
|
+
5. **Minimal dependencies** — typer, fabric, paramiko, gitpython, rich.
|
|
273
|
+
6. **Transport abstraction over queue state** — job-management commands read the transport from each job's own history entry rather than current `queue.enabled`, so toggling the queue mid-flight doesn't orphan existing jobs.
|
|
274
|
+
7. **Code-delivery default follows queue mode** — `bake` when queued (avoids code drift while waiting), `mount` when not (zero-rebuild iteration); explicit `code_delivery` overrides either way.
|
|
275
|
+
|
|
276
|
+
## Extraction History
|
|
277
|
+
|
|
278
|
+
- **Source:** DTU-HPC-CLI (github.com/ChrisFugl/DTU-HPC-CLI)
|
|
279
|
+
- **Extracted:** 2026-04-09
|
|
280
|
+
- **Initial changes:**
|
|
281
|
+
- Removed: SubmitConfig, InstallConfig, types.py, all HPC modules
|
|
282
|
+
- Modified: config.py (trimmed), __init__.py (flat commands)
|
|
283
|
+
- Unchanged at the time: docker.py, sync.py, error.py, client/*, constants.py
|
|
284
|
+
- **Since extraction:** the original monolithic `docker.py` was split into `build.py`, `submit.py`, `manage.py`, `resubmit.py`, `history.py`, `download.py`, `volumes.py`, `tunnel.py`; `queue.py`, `transport.py`, and `tagging.py` were added for the task-spooler queue, transport abstraction, and baked code delivery. Config/history filenames moved from `.dtu_hpc.json`/`.dtu_docker_history.json` to `.dockhand.json`/`.dockhand_history.json`.
|
|
285
|
+
|
|
286
|
+
## Future Enhancements
|
|
287
|
+
|
|
288
|
+
Potential improvements specific to dockhand (volume inspection is already implemented — see `volumes.py`):
|
|
289
|
+
- Docker Compose support (`dockhand compose up`)
|
|
290
|
+
- Kubernetes pod management
|
|
291
|
+
- Local-only mode (drop SSH dependency for pure local docker)
|
|
292
|
+
- Config auto-generation wizard
|
|
293
|
+
- Container registry integration (Docker Hub, ECR, GCR)
|
|
294
|
+
- Multi-container orchestration helpers
|
|
295
|
+
- Fix `tunnel.py`'s stale `container_id` history lookup (see [Known inconsistency](#key-modules) above)
|
|
296
|
+
|
|
297
|
+
## Contributing
|
|
298
|
+
|
|
299
|
+
When working on dockhand:
|
|
300
|
+
1. Keep the flat command structure — don't re-introduce `docker` sub-groups
|
|
301
|
+
2. Maintain `.dockhand.json` compatibility
|
|
302
|
+
3. Test manually with `uv run dockhand <command> --help` and actual docker operations
|
|
303
|
+
4. Follow ruff lint/format rules (use `uv run ruff check . && uv run ruff format .`)
|
|
304
|
+
5. Document new features in README.md and AGENTS.md
|
|
305
|
+
6. Update this file's Architecture section when modules are added, split, or renamed — it has drifted from actual code structure before
|
|
306
|
+
|
|
307
|
+
## Maintenance
|
|
308
|
+
|
|
309
|
+
dockhand is maintained independently from DTU-HPC-CLI. Version bumps and releases are separate. If you need HPC support, use the original DTU-HPC-CLI package.
|