@procedures/docker 0.0.0-stage → 0.1.0
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.
- package/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +752 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +16 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +183 -0
- package/dist/module.js.map +1 -0
- package/dist/procedures/docker/build.d.ts +12 -0
- package/dist/procedures/docker/build.d.ts.map +1 -0
- package/dist/procedures/docker/build.js +34 -0
- package/dist/procedures/docker/build.js.map +1 -0
- package/dist/procedures/docker/down.d.ts +12 -0
- package/dist/procedures/docker/down.d.ts.map +1 -0
- package/dist/procedures/docker/down.js +25 -0
- package/dist/procedures/docker/down.js.map +1 -0
- package/dist/procedures/docker/exec.d.ts +12 -0
- package/dist/procedures/docker/exec.d.ts.map +1 -0
- package/dist/procedures/docker/exec.js +30 -0
- package/dist/procedures/docker/exec.js.map +1 -0
- package/dist/procedures/docker/index.d.ts +11 -0
- package/dist/procedures/docker/index.d.ts.map +1 -0
- package/dist/procedures/docker/index.js +11 -0
- package/dist/procedures/docker/index.js.map +1 -0
- package/dist/procedures/docker/logs.d.ts +16 -0
- package/dist/procedures/docker/logs.d.ts.map +1 -0
- package/dist/procedures/docker/logs.js +40 -0
- package/dist/procedures/docker/logs.js.map +1 -0
- package/dist/procedures/docker/ps.d.ts +12 -0
- package/dist/procedures/docker/ps.d.ts.map +1 -0
- package/dist/procedures/docker/ps.js +22 -0
- package/dist/procedures/docker/ps.js.map +1 -0
- package/dist/procedures/docker/pull.d.ts +12 -0
- package/dist/procedures/docker/pull.d.ts.map +1 -0
- package/dist/procedures/docker/pull.js +17 -0
- package/dist/procedures/docker/pull.js.map +1 -0
- package/dist/procedures/docker/rm.d.ts +12 -0
- package/dist/procedures/docker/rm.d.ts.map +1 -0
- package/dist/procedures/docker/rm.js +21 -0
- package/dist/procedures/docker/rm.js.map +1 -0
- package/dist/procedures/docker/run.d.ts +21 -0
- package/dist/procedures/docker/run.d.ts.map +1 -0
- package/dist/procedures/docker/run.js +56 -0
- package/dist/procedures/docker/run.js.map +1 -0
- package/dist/procedures/docker/shared.d.ts +23 -0
- package/dist/procedures/docker/shared.d.ts.map +1 -0
- package/dist/procedures/docker/shared.js +51 -0
- package/dist/procedures/docker/shared.js.map +1 -0
- package/dist/procedures/docker/stop.d.ts +12 -0
- package/dist/procedures/docker/stop.d.ts.map +1 -0
- package/dist/procedures/docker/stop.js +20 -0
- package/dist/procedures/docker/stop.js.map +1 -0
- package/dist/procedures/docker/up.d.ts +12 -0
- package/dist/procedures/docker/up.d.ts.map +1 -0
- package/dist/procedures/docker/up.js +28 -0
- package/dist/procedures/docker/up.js.map +1 -0
- package/dist/register.d.ts +9 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +16 -0
- package/dist/register.js.map +1 -0
- package/dist/types.d.ts +130 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +207 -0
- package/dist/types.js.map +1 -0
- package/package.json +81 -3
- package/src/index.ts +75 -0
- package/src/module.ts +237 -0
- package/src/procedures/docker/build.ts +46 -0
- package/src/procedures/docker/down.ts +31 -0
- package/src/procedures/docker/exec.ts +37 -0
- package/src/procedures/docker/index.ts +10 -0
- package/src/procedures/docker/logs.ts +52 -0
- package/src/procedures/docker/ps.ts +27 -0
- package/src/procedures/docker/pull.ts +23 -0
- package/src/procedures/docker/rm.ts +28 -0
- package/src/procedures/docker/run.ts +65 -0
- package/src/procedures/docker/shared.ts +62 -0
- package/src/procedures/docker/stop.ts +29 -0
- package/src/procedures/docker/up.ts +35 -0
- package/src/register.ts +20 -0
- package/src/types.ts +351 -0
package/README.md
CHANGED
|
@@ -1,3 +1,753 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @procedures/docker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@procedures/docker)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
> Docker container management as procedures using client-shell
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
`client-docker` wraps Docker CLI commands as procedures using `client-shell`. It provides a comprehensive set of procedures for container management, image building, Docker Compose orchestration, and more - all accessible through the procedure system's RPC interface.
|
|
11
|
+
|
|
12
|
+
### Key Features
|
|
13
|
+
|
|
14
|
+
- **Comprehensive Docker Coverage**: Run, build, pull, exec, stop, rm, ps, logs commands
|
|
15
|
+
- **Docker Compose Support**: Up and down operations for multi-container applications
|
|
16
|
+
- **Type-Safe Inputs**: Zod schemas for all inputs with full TypeScript support
|
|
17
|
+
- **Shell Integration**: Leverages `client-shell` for reliable CLI execution
|
|
18
|
+
- **Consistent Output**: Standardized output format across all procedures
|
|
19
|
+
- **CLI Wrapper Pattern**: One procedure per Docker command with full option support
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
graph TB
|
|
25
|
+
subgraph "client-docker"
|
|
26
|
+
REG[Registration Layer]
|
|
27
|
+
PROC[Docker Procedures]
|
|
28
|
+
TYPES[Type System]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
subgraph "Procedures"
|
|
32
|
+
RUN[docker.run]
|
|
33
|
+
BUILD[docker.build]
|
|
34
|
+
PULL[docker.pull]
|
|
35
|
+
EXEC[docker.exec]
|
|
36
|
+
STOP[docker.stop]
|
|
37
|
+
RM[docker.rm]
|
|
38
|
+
PS[docker.ps]
|
|
39
|
+
LOGS[docker.logs]
|
|
40
|
+
UP[docker.compose.up]
|
|
41
|
+
DOWN[docker.compose.down]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
subgraph "client-shell"
|
|
45
|
+
SHELL[shell.exec]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
REG --> PROC
|
|
49
|
+
PROC --> RUN
|
|
50
|
+
PROC --> BUILD
|
|
51
|
+
PROC --> PULL
|
|
52
|
+
PROC --> EXEC
|
|
53
|
+
PROC --> STOP
|
|
54
|
+
PROC --> RM
|
|
55
|
+
PROC --> PS
|
|
56
|
+
PROC --> LOGS
|
|
57
|
+
PROC --> UP
|
|
58
|
+
PROC --> DOWN
|
|
59
|
+
|
|
60
|
+
RUN --> SHELL
|
|
61
|
+
BUILD --> SHELL
|
|
62
|
+
PULL --> SHELL
|
|
63
|
+
EXEC --> SHELL
|
|
64
|
+
STOP --> SHELL
|
|
65
|
+
RM --> SHELL
|
|
66
|
+
PS --> SHELL
|
|
67
|
+
LOGS --> SHELL
|
|
68
|
+
UP --> SHELL
|
|
69
|
+
DOWN --> SHELL
|
|
70
|
+
|
|
71
|
+
style REG fill:#4a90e2
|
|
72
|
+
style PROC fill:#7ed321
|
|
73
|
+
style SHELL fill:#f5a623
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Data Flow
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
sequenceDiagram
|
|
80
|
+
participant Client
|
|
81
|
+
participant docker.run
|
|
82
|
+
participant shell.exec
|
|
83
|
+
participant Docker CLI
|
|
84
|
+
|
|
85
|
+
Client->>docker.run: { image, ports, env, ... }
|
|
86
|
+
docker.run->>docker.run: Build docker run command
|
|
87
|
+
docker.run->>shell.exec: { command: "docker run ..." }
|
|
88
|
+
shell.exec->>Docker CLI: Execute command
|
|
89
|
+
Docker CLI-->>shell.exec: stdout/stderr/exitCode
|
|
90
|
+
shell.exec-->>docker.run: ShellResult
|
|
91
|
+
docker.run->>docker.run: Format as DockerCommandOutput
|
|
92
|
+
docker.run-->>Client: { exitCode, stdout, stderr, success, duration }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Installation
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install @procedures/docker
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or with pnpm:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
pnpm add @procedures/docker
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Dependencies
|
|
108
|
+
|
|
109
|
+
This package depends on:
|
|
110
|
+
- `@procedures/client` - Core client/procedure system
|
|
111
|
+
- `@procedures/shell` - Shell execution for CLI commands
|
|
112
|
+
- `zod` - Schema validation and type inference
|
|
113
|
+
|
|
114
|
+
### System Requirements
|
|
115
|
+
|
|
116
|
+
- Docker must be installed and available in PATH
|
|
117
|
+
- For Docker Compose procedures: Docker Compose must be installed
|
|
118
|
+
|
|
119
|
+
## Usage
|
|
120
|
+
|
|
121
|
+
### Basic Container Operations
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { Client, LocalTransport, PROCEDURE_REGISTRY } from "@procedures/client";
|
|
125
|
+
import "@procedures/docker"; // registers its procedures
|
|
126
|
+
|
|
127
|
+
const client = new Client(new LocalTransport({ registry: PROCEDURE_REGISTRY }));
|
|
128
|
+
|
|
129
|
+
// Run a container
|
|
130
|
+
const result = await client.exec(["docker", "run"], {
|
|
131
|
+
image: "nginx:latest",
|
|
132
|
+
name: "my-nginx",
|
|
133
|
+
ports: ["8080:80"],
|
|
134
|
+
detach: true,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
console.log(result);
|
|
138
|
+
// {
|
|
139
|
+
// exitCode: 0,
|
|
140
|
+
// stdout: "container_id_here\n",
|
|
141
|
+
// stderr: "",
|
|
142
|
+
// success: true,
|
|
143
|
+
// duration: 1234
|
|
144
|
+
// }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Building Images
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Build an image from a Dockerfile
|
|
151
|
+
await client.exec(["docker", "build"], {
|
|
152
|
+
context: ".",
|
|
153
|
+
tag: "my-app:latest",
|
|
154
|
+
dockerfile: "Dockerfile",
|
|
155
|
+
buildArgs: {
|
|
156
|
+
NODE_VERSION: "20",
|
|
157
|
+
APP_ENV: "production",
|
|
158
|
+
},
|
|
159
|
+
noCache: false,
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Executing Commands in Containers
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// Execute a command in a running container
|
|
167
|
+
await client.exec(["docker", "exec"], {
|
|
168
|
+
container: "my-nginx",
|
|
169
|
+
command: "nginx -t",
|
|
170
|
+
interactive: false,
|
|
171
|
+
tty: false,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Interactive shell session
|
|
175
|
+
await client.exec(["docker", "exec"], {
|
|
176
|
+
container: "my-app",
|
|
177
|
+
command: "/bin/bash",
|
|
178
|
+
interactive: true,
|
|
179
|
+
tty: true,
|
|
180
|
+
env: {
|
|
181
|
+
DEBUG: "true",
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Container Management
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// List all containers
|
|
190
|
+
const containers = await client.exec(["docker", "ps"], {
|
|
191
|
+
all: true,
|
|
192
|
+
format: "json",
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Stop containers
|
|
196
|
+
await client.exec(["docker", "stop"], {
|
|
197
|
+
containers: ["nginx-1", "nginx-2"],
|
|
198
|
+
time: 10, // Grace period in seconds
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Remove containers
|
|
202
|
+
await client.exec(["docker", "rm"], {
|
|
203
|
+
containers: ["nginx-1"],
|
|
204
|
+
force: true,
|
|
205
|
+
volumes: true,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Get container logs
|
|
209
|
+
const logs = await client.exec(["docker", "logs"], {
|
|
210
|
+
container: "my-app",
|
|
211
|
+
tail: 100,
|
|
212
|
+
timestamps: true,
|
|
213
|
+
follow: false,
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Docker Compose
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// Start services
|
|
221
|
+
await client.exec(["docker", "compose", "up"], {
|
|
222
|
+
file: "docker-compose.yml",
|
|
223
|
+
detach: true,
|
|
224
|
+
build: true,
|
|
225
|
+
forceRecreate: false,
|
|
226
|
+
services: ["web", "db"], // Optional: specific services
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Stop and remove services
|
|
230
|
+
await client.exec(["docker", "compose", "down"], {
|
|
231
|
+
file: "docker-compose.yml",
|
|
232
|
+
volumes: true,
|
|
233
|
+
removeOrphans: true,
|
|
234
|
+
rmi: "local", // Remove images built locally
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## API Reference
|
|
239
|
+
|
|
240
|
+
### Common Output Type
|
|
241
|
+
|
|
242
|
+
All Docker procedures return a standardized output format:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
interface DockerCommandOutput {
|
|
246
|
+
exitCode: number;
|
|
247
|
+
stdout: string;
|
|
248
|
+
stderr: string;
|
|
249
|
+
success: boolean; // true if exitCode === 0
|
|
250
|
+
duration: number; // milliseconds
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Procedures
|
|
255
|
+
|
|
256
|
+
#### `docker.run`
|
|
257
|
+
|
|
258
|
+
Run a Docker container.
|
|
259
|
+
|
|
260
|
+
**Input:**
|
|
261
|
+
```typescript
|
|
262
|
+
{
|
|
263
|
+
image: string; // Required: Docker image
|
|
264
|
+
name?: string; // Container name
|
|
265
|
+
ports?: string[]; // Port mappings ["host:container", ...]
|
|
266
|
+
volumes?: string[]; // Volume mappings ["/host:/container", ...]
|
|
267
|
+
env?: Record<string, string>; // Environment variables
|
|
268
|
+
detach?: boolean; // Run in background (default: false)
|
|
269
|
+
rm?: boolean; // Auto-remove when stopped (default: false)
|
|
270
|
+
command?: string; // Command to run in container
|
|
271
|
+
cwd?: string; // Working directory for command
|
|
272
|
+
timeout?: number; // Command timeout in ms
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Example:**
|
|
277
|
+
```typescript
|
|
278
|
+
await client.exec(["docker", "run"], {
|
|
279
|
+
image: "postgres:15",
|
|
280
|
+
name: "my-db",
|
|
281
|
+
ports: ["5432:5432"],
|
|
282
|
+
env: {
|
|
283
|
+
POSTGRES_PASSWORD: "secret",
|
|
284
|
+
POSTGRES_DB: "myapp",
|
|
285
|
+
},
|
|
286
|
+
detach: true,
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### `docker.build`
|
|
291
|
+
|
|
292
|
+
Build a Docker image from a Dockerfile.
|
|
293
|
+
|
|
294
|
+
**Input:**
|
|
295
|
+
```typescript
|
|
296
|
+
{
|
|
297
|
+
context: string; // Required: Build context path
|
|
298
|
+
tag: string; // Required: Image tag
|
|
299
|
+
dockerfile?: string; // Dockerfile path (relative to context)
|
|
300
|
+
buildArgs?: Record<string, string>; // Build arguments
|
|
301
|
+
noCache?: boolean; // Don't use cache (default: false)
|
|
302
|
+
cwd?: string; // Working directory
|
|
303
|
+
timeout?: number; // Build timeout in ms
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Example:**
|
|
308
|
+
```typescript
|
|
309
|
+
await client.exec(["docker", "build"], {
|
|
310
|
+
context: ".",
|
|
311
|
+
tag: "my-app:v1.0.0",
|
|
312
|
+
buildArgs: {
|
|
313
|
+
NODE_VERSION: "20",
|
|
314
|
+
},
|
|
315
|
+
noCache: true,
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### `docker.pull`
|
|
320
|
+
|
|
321
|
+
Pull a Docker image from a registry.
|
|
322
|
+
|
|
323
|
+
**Input:**
|
|
324
|
+
```typescript
|
|
325
|
+
{
|
|
326
|
+
image: string; // Required: Image name
|
|
327
|
+
tag?: string; // Image tag (default: "latest")
|
|
328
|
+
cwd?: string; // Working directory
|
|
329
|
+
timeout?: number; // Pull timeout in ms
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
**Example:**
|
|
334
|
+
```typescript
|
|
335
|
+
await client.exec(["docker", "pull"], {
|
|
336
|
+
image: "nginx",
|
|
337
|
+
tag: "alpine",
|
|
338
|
+
});
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### `docker.exec`
|
|
342
|
+
|
|
343
|
+
Execute a command in a running container.
|
|
344
|
+
|
|
345
|
+
**Input:**
|
|
346
|
+
```typescript
|
|
347
|
+
{
|
|
348
|
+
container: string; // Required: Container name or ID
|
|
349
|
+
command: string; // Required: Command to execute
|
|
350
|
+
interactive?: boolean; // Allocate stdin (default: false)
|
|
351
|
+
tty?: boolean; // Allocate pseudo-TTY (default: false)
|
|
352
|
+
workdir?: string; // Working directory inside container
|
|
353
|
+
env?: Record<string, string>; // Environment variables
|
|
354
|
+
cwd?: string; // Working directory (host)
|
|
355
|
+
timeout?: number; // Command timeout in ms
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**Example:**
|
|
360
|
+
```typescript
|
|
361
|
+
await client.exec(["docker", "exec"], {
|
|
362
|
+
container: "my-app",
|
|
363
|
+
command: "npm run migrate",
|
|
364
|
+
env: {
|
|
365
|
+
DATABASE_URL: "postgres://...",
|
|
366
|
+
},
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
#### `docker.stop`
|
|
371
|
+
|
|
372
|
+
Stop running containers.
|
|
373
|
+
|
|
374
|
+
**Input:**
|
|
375
|
+
```typescript
|
|
376
|
+
{
|
|
377
|
+
containers: string[]; // Required: Container names/IDs
|
|
378
|
+
time?: number; // Seconds to wait before killing (default: 10)
|
|
379
|
+
cwd?: string; // Working directory
|
|
380
|
+
timeout?: number; // Command timeout in ms
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Example:**
|
|
385
|
+
```typescript
|
|
386
|
+
await client.exec(["docker", "stop"], {
|
|
387
|
+
containers: ["web-1", "web-2", "worker-1"],
|
|
388
|
+
time: 30,
|
|
389
|
+
});
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
#### `docker.rm`
|
|
393
|
+
|
|
394
|
+
Remove containers.
|
|
395
|
+
|
|
396
|
+
**Input:**
|
|
397
|
+
```typescript
|
|
398
|
+
{
|
|
399
|
+
containers: string[]; // Required: Container names/IDs
|
|
400
|
+
force?: boolean; // Force removal (default: false)
|
|
401
|
+
volumes?: boolean; // Remove volumes (default: false)
|
|
402
|
+
cwd?: string; // Working directory
|
|
403
|
+
timeout?: number; // Command timeout in ms
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Example:**
|
|
408
|
+
```typescript
|
|
409
|
+
await client.exec(["docker", "rm"], {
|
|
410
|
+
containers: ["temp-container"],
|
|
411
|
+
force: true,
|
|
412
|
+
volumes: true,
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### `docker.ps`
|
|
417
|
+
|
|
418
|
+
List containers.
|
|
419
|
+
|
|
420
|
+
**Input:**
|
|
421
|
+
```typescript
|
|
422
|
+
{
|
|
423
|
+
all?: boolean; // Show all containers (default: false)
|
|
424
|
+
filter?: string; // Filter output (e.g., "status=running")
|
|
425
|
+
format?: string; // Format output (e.g., "json")
|
|
426
|
+
cwd?: string; // Working directory
|
|
427
|
+
timeout?: number; // Command timeout in ms
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Example:**
|
|
432
|
+
```typescript
|
|
433
|
+
const result = await client.exec(["docker", "ps"], {
|
|
434
|
+
all: true,
|
|
435
|
+
format: "json",
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const containers = JSON.parse(result.stdout);
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
#### `docker.logs`
|
|
442
|
+
|
|
443
|
+
Get container logs.
|
|
444
|
+
|
|
445
|
+
**Input:**
|
|
446
|
+
```typescript
|
|
447
|
+
{
|
|
448
|
+
container: string; // Required: Container name or ID
|
|
449
|
+
follow?: boolean; // Follow log output (default: false)
|
|
450
|
+
tail?: number; // Number of lines from end
|
|
451
|
+
timestamps?: boolean; // Show timestamps (default: false)
|
|
452
|
+
cwd?: string; // Working directory
|
|
453
|
+
timeout?: number; // Command timeout in ms
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**Example:**
|
|
458
|
+
```typescript
|
|
459
|
+
const logs = await client.exec(["docker", "logs"], {
|
|
460
|
+
container: "my-app",
|
|
461
|
+
tail: 50,
|
|
462
|
+
timestamps: true,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
console.log(logs.stdout);
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
#### `docker.compose.up`
|
|
469
|
+
|
|
470
|
+
Start services with Docker Compose.
|
|
471
|
+
|
|
472
|
+
**Input:**
|
|
473
|
+
```typescript
|
|
474
|
+
{
|
|
475
|
+
file?: string; // Compose file path (default: "docker-compose.yml")
|
|
476
|
+
services?: string[]; // Services to start (default: all)
|
|
477
|
+
detach?: boolean; // Run in background (default: false)
|
|
478
|
+
build?: boolean; // Build images before starting (default: false)
|
|
479
|
+
forceRecreate?: boolean; // Force recreate containers (default: false)
|
|
480
|
+
cwd?: string; // Working directory
|
|
481
|
+
timeout?: number; // Command timeout in ms
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**Example:**
|
|
486
|
+
```typescript
|
|
487
|
+
await client.exec(["docker", "compose", "up"], {
|
|
488
|
+
file: "docker-compose.prod.yml",
|
|
489
|
+
services: ["web", "db"],
|
|
490
|
+
detach: true,
|
|
491
|
+
build: true,
|
|
492
|
+
});
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### `docker.compose.down`
|
|
496
|
+
|
|
497
|
+
Stop and remove services with Docker Compose.
|
|
498
|
+
|
|
499
|
+
**Input:**
|
|
500
|
+
```typescript
|
|
501
|
+
{
|
|
502
|
+
file?: string; // Compose file path (default: "docker-compose.yml")
|
|
503
|
+
volumes?: boolean; // Remove volumes (default: false)
|
|
504
|
+
removeOrphans?: boolean; // Remove orphan containers (default: false)
|
|
505
|
+
rmi?: "all" | "local"; // Remove images (all or local only)
|
|
506
|
+
cwd?: string; // Working directory
|
|
507
|
+
timeout?: number; // Command timeout in ms
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Example:**
|
|
512
|
+
```typescript
|
|
513
|
+
await client.exec(["docker", "compose", "down"], {
|
|
514
|
+
volumes: true,
|
|
515
|
+
removeOrphans: true,
|
|
516
|
+
rmi: "local",
|
|
517
|
+
});
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## Types
|
|
521
|
+
|
|
522
|
+
### Input Types
|
|
523
|
+
|
|
524
|
+
All input types are exported with Zod schemas for validation:
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
import {
|
|
528
|
+
DockerRunInput,
|
|
529
|
+
DockerRunInputSchema,
|
|
530
|
+
DockerBuildInput,
|
|
531
|
+
DockerBuildInputSchema,
|
|
532
|
+
// ... etc
|
|
533
|
+
} from "@procedures/docker";
|
|
534
|
+
|
|
535
|
+
// Validate input
|
|
536
|
+
const parsed = DockerRunInputSchema.parse({
|
|
537
|
+
image: "nginx",
|
|
538
|
+
ports: ["80:80"],
|
|
539
|
+
});
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Output Type
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
export interface DockerCommandOutput {
|
|
546
|
+
/** Exit code of the command */
|
|
547
|
+
exitCode: number;
|
|
548
|
+
/** Standard output */
|
|
549
|
+
stdout: string;
|
|
550
|
+
/** Standard error */
|
|
551
|
+
stderr: string;
|
|
552
|
+
/** Whether command succeeded (exit code 0) */
|
|
553
|
+
success: boolean;
|
|
554
|
+
/** Duration in milliseconds */
|
|
555
|
+
duration: number;
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
## Integration with Ecosystem
|
|
560
|
+
|
|
561
|
+
### With bundle-dev
|
|
562
|
+
|
|
563
|
+
The `bundle-dev` package includes `client-docker` for development workflows:
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
import "@procedures/bundle-dev"; // registers its procedures
|
|
567
|
+
|
|
568
|
+
// docker.* procedures are now available
|
|
569
|
+
await client.exec(["docker", "run"], { ... });
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### With client-shell
|
|
573
|
+
|
|
574
|
+
`client-docker` uses `client-shell` under the hood:
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
// Internally, docker.run calls:
|
|
578
|
+
await ctx.client.call(["shell", "exec"], {
|
|
579
|
+
command: "docker run -d -p 8080:80 --name my-nginx nginx",
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### With client-fs
|
|
584
|
+
|
|
585
|
+
Combine with `client-fs` for Dockerfile management:
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
// Write Dockerfile
|
|
589
|
+
await client.exec(["fs", "write"], {
|
|
590
|
+
path: "Dockerfile",
|
|
591
|
+
content: `
|
|
592
|
+
FROM node:20-alpine
|
|
593
|
+
WORKDIR /app
|
|
594
|
+
COPY . .
|
|
595
|
+
RUN npm ci --production
|
|
596
|
+
CMD ["node", "index.js"]
|
|
597
|
+
`.trim(),
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// Build image
|
|
601
|
+
await client.exec(["docker", "build"], {
|
|
602
|
+
context: ".",
|
|
603
|
+
tag: "my-app:latest",
|
|
604
|
+
});
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
## Advanced Usage
|
|
608
|
+
|
|
609
|
+
### Multi-Container Applications
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
// Start entire stack
|
|
613
|
+
await client.exec(["docker", "compose", "up"], {
|
|
614
|
+
detach: true,
|
|
615
|
+
build: true,
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// Check service status
|
|
619
|
+
const containers = await client.exec(["docker", "ps"], {
|
|
620
|
+
all: false,
|
|
621
|
+
format: "json",
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
// View logs for specific service
|
|
625
|
+
const webLogs = await client.exec(["docker", "logs"], {
|
|
626
|
+
container: "myapp-web-1",
|
|
627
|
+
tail: 100,
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
// Clean up
|
|
631
|
+
await client.exec(["docker", "compose", "down"], {
|
|
632
|
+
volumes: true,
|
|
633
|
+
});
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### Custom Build Process
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
// Multi-stage build with build args
|
|
640
|
+
await client.exec(["docker", "build"], {
|
|
641
|
+
context: ".",
|
|
642
|
+
tag: "my-app:production",
|
|
643
|
+
dockerfile: "Dockerfile.prod",
|
|
644
|
+
buildArgs: {
|
|
645
|
+
NODE_VERSION: "20",
|
|
646
|
+
NPM_TOKEN: process.env.NPM_TOKEN,
|
|
647
|
+
BUILD_ENV: "production",
|
|
648
|
+
},
|
|
649
|
+
noCache: true,
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// Tag for registry
|
|
653
|
+
await client.exec(["shell", "exec"], {
|
|
654
|
+
command: "docker tag my-app:production registry.example.com/my-app:latest",
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// Push to registry
|
|
658
|
+
await client.exec(["shell", "exec"], {
|
|
659
|
+
command: "docker push registry.example.com/my-app:latest",
|
|
660
|
+
});
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Container Health Checks
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
// Wait for container to be healthy
|
|
667
|
+
async function waitForHealthy(container: string, maxAttempts = 30) {
|
|
668
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
669
|
+
const result = await client.exec(["docker", "ps"], {
|
|
670
|
+
filter: `name=${container}`,
|
|
671
|
+
format: "json",
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const containers = JSON.parse(result.stdout);
|
|
675
|
+
if (containers[0]?.Status?.includes("healthy")) {
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
throw new Error(`Container ${container} did not become healthy`);
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
## Error Handling
|
|
687
|
+
|
|
688
|
+
```typescript
|
|
689
|
+
try {
|
|
690
|
+
await client.exec(["docker", "run"], {
|
|
691
|
+
image: "nonexistent:image",
|
|
692
|
+
detach: true,
|
|
693
|
+
});
|
|
694
|
+
} catch (error) {
|
|
695
|
+
// Handle Docker errors
|
|
696
|
+
if (error.stderr?.includes("Unable to find image")) {
|
|
697
|
+
console.error("Image not found");
|
|
698
|
+
} else if (error.stderr?.includes("port is already allocated")) {
|
|
699
|
+
console.error("Port conflict");
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Check success field
|
|
704
|
+
const result = await client.exec(["docker", "build"], {
|
|
705
|
+
context: ".",
|
|
706
|
+
tag: "my-app",
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
if (!result.success) {
|
|
710
|
+
console.error("Build failed:", result.stderr);
|
|
711
|
+
throw new Error("Docker build failed");
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
## Performance Considerations
|
|
716
|
+
|
|
717
|
+
- **Detached Mode**: Use `detach: true` for long-running containers to avoid blocking
|
|
718
|
+
- **Build Cache**: Avoid `noCache: true` unless necessary to speed up builds
|
|
719
|
+
- **Timeouts**: Set appropriate timeouts for long-running operations (builds, pulls)
|
|
720
|
+
- **Cleanup**: Use `rm: true` for one-off containers to avoid accumulation
|
|
721
|
+
- **Docker Compose**: Prefer `docker.compose.up` for multi-container apps vs. multiple `docker.run` calls
|
|
722
|
+
|
|
723
|
+
## Testing
|
|
724
|
+
|
|
725
|
+
The package includes both unit and integration tests:
|
|
726
|
+
|
|
727
|
+
```bash
|
|
728
|
+
# Run all tests
|
|
729
|
+
pnpm test
|
|
730
|
+
|
|
731
|
+
# Unit tests only
|
|
732
|
+
pnpm test:unit
|
|
733
|
+
|
|
734
|
+
# Integration tests (requires Docker)
|
|
735
|
+
pnpm test:integration
|
|
736
|
+
|
|
737
|
+
# Watch mode
|
|
738
|
+
pnpm test:watch
|
|
739
|
+
|
|
740
|
+
# Coverage
|
|
741
|
+
pnpm test:coverage
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
## License
|
|
745
|
+
|
|
746
|
+
MIT
|
|
747
|
+
|
|
748
|
+
## Related Packages
|
|
749
|
+
|
|
750
|
+
- `@procedures/client` - Core client/procedure system
|
|
751
|
+
- `@procedures/shell` - Shell command execution
|
|
752
|
+
- `@procedures/bundle-dev` - Development bundle
|
|
753
|
+
- `@procedures/fs` - Filesystem operations
|