dkit 0.2.0 → 0.3.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -0
- data/LICENSE +21 -0
- data/README.md +135 -0
- data/bin/dkit +33 -2
- metadata +10 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff6fc0e53b34c039584e579f8b866d5097ee8ef9ff3b5951e3fa6a905ef6320e
|
|
4
|
+
data.tar.gz: cc0cf53bb9df918be82d7e38542175ef10421b522f06b746380f7c2a4fc74bea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 153b4417073e89d4b28da38ee174049e16439406e1915bc631e41b81d7986bf91dcd8effa28db55b861f0f563378ea63417562889e737b9b8435a4835a781d5e
|
|
7
|
+
data.tar.gz: 9e8b738739abfd0c65b1815d7d30f948bdbe3bf26e5af42a8582f60712e71be02639f739ac30293de22e1b6d0fa27fab8836c0e802e73fe9844d616591e4c28f
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.3.1] - 2026-04-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Verbose fallback message: when a command falls back to the host (container not running), prints `[dkit] <cmd> → host` to stderr if verbose is enabled
|
|
12
|
+
- Fallback messages respect the same config as container messages: `verbose: false` in `.devcontainer/dkit-intercept` or `DKIT_VERBOSE=0` env var suppress them
|
|
13
|
+
|
|
14
|
+
### Notes
|
|
15
|
+
- Requires `exec zsh` after gem update to reload the shell hook with the updated function templates
|
|
16
|
+
|
|
17
|
+
## [0.3.0] - 2026-04-13
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Verbose routing messages: when a command is intercepted, prints `[dkit] <cmd> → <container>` to stderr before executing
|
|
21
|
+
- `verbose_enabled?` helper that checks both env var and intercept file directive
|
|
22
|
+
- Disable per-project: add `verbose: false` to `.devcontainer/dkit-intercept` (committed, shared with team)
|
|
23
|
+
- Disable personally: set `DKIT_VERBOSE=0` env var (takes precedence over intercept file)
|
|
24
|
+
- `dkit init` now includes `# verbose: false` comment in the generated intercept file
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- `dkit help` documents verbose configuration options
|
|
28
|
+
|
|
29
|
+
### Notes
|
|
30
|
+
- Compatible with Linux and macOS (no platform-specific dependencies)
|
|
31
|
+
|
|
32
|
+
## [0.2.0] - 2026-04-13
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- Per-project intercept file (`.devcontainer/dkit-intercept`) replaces global config
|
|
36
|
+
- `dkit init` auto-detects Ruby and Node projects and seeds intercept file
|
|
37
|
+
- `dkit intercept add/remove/list` subcommands for managing per-project commands
|
|
38
|
+
- `dkit root` — print project root without requiring a running container
|
|
39
|
+
- `dkit code` — open VS Code attached to devcontainer via `vscode-remote://` URI
|
|
40
|
+
- `dkit claude` — run claude interactively inside the container
|
|
41
|
+
- `dkit exec` — non-TTY variant of `dkit run` for scripting
|
|
42
|
+
- Shell hook (`dkit hook`) with fast-path `chpwd` to avoid redundant root lookups
|
|
43
|
+
- `code` and `claude` always intercepted when a devcontainer is active
|
|
44
|
+
- `DKIT_PROJECT_ROOT` env var to override project root resolution
|
|
45
|
+
- Distributed as a Ruby gem
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- Container name resolution now tries three strategies: compose YAML `container_name`, docker label query, then `docker compose ps -q`
|
|
49
|
+
- Intercept configuration moved from `~/.config/dkit/intercept` to per-project `.devcontainer/dkit-intercept`
|
|
50
|
+
|
|
51
|
+
## [0.1.0] - 2025-01-01
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
- Initial release: basic command routing into devcontainer
|
|
55
|
+
- Global intercept file at `~/.config/dkit/intercept`
|
|
56
|
+
- `dkit run`, `dkit shell`, `dkit up`, `dkit down`, `dkit logs`
|
|
57
|
+
- Shell hook with `chpwd` integration
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Augusto Stroligo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# dkit
|
|
2
|
+
|
|
3
|
+
[](https://rubygems.org/gems/dkit)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
|
|
6
|
+
DevKit CLI — routes shell commands transparently into a running devcontainer.
|
|
7
|
+
|
|
8
|
+
When you `cd` into a project, dkit intercepts configured commands (e.g. `rails`, `bundle`, `rspec`) and executes them inside the devcontainer instead of on the host, preserving the correct working directory. When no container is running the commands fall through to the host as normal.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- macOS or Linux
|
|
13
|
+
- Ruby >= 2.7
|
|
14
|
+
- Docker with Compose v2 (`docker compose`)
|
|
15
|
+
- A project with `.devcontainer/devcontainer.json` using `dockerComposeFile` + `service`
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
gem install dkit
|
|
21
|
+
echo 'eval "$(dkit hook)"' >> ~/.zshrc && exec zsh
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### From source
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
gem build dkit.gemspec && gem install dkit-*.gem
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Shell integration
|
|
31
|
+
|
|
32
|
+
The hook registers a `chpwd` listener that loads and unloads command shims as you navigate between projects. Add it once to `~/.zshrc`:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
eval "$(dkit hook)"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`code` and `claude` are always intercepted when a devcontainer is active — no configuration needed.
|
|
39
|
+
|
|
40
|
+
## Project setup
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
cd ~/projects/my-app
|
|
44
|
+
dkit init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`dkit init` detects the project type (Ruby, Node) and creates `.devcontainer/dkit-intercept` with sensible defaults. Commit that file so your whole team gets the same behavior:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
git add .devcontainer/dkit-intercept
|
|
51
|
+
git commit -m "chore: add dkit intercept config"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Managing intercepted commands
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
dkit intercept list # show active commands for this project
|
|
58
|
+
dkit intercept add terraform # add a command
|
|
59
|
+
dkit intercept remove terraform # remove a command
|
|
60
|
+
exec zsh # reload shell to apply changes
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Verbose routing messages
|
|
64
|
+
|
|
65
|
+
By default, dkit prints a line to stderr whenever it intercepts a command:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
[dkit] rails server → myapp-dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
To disable **per project** (committed, shared with the team), add to `.devcontainer/dkit-intercept`:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
verbose: false
|
|
75
|
+
rails
|
|
76
|
+
bundle
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
To disable **personally** regardless of project config:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
export DKIT_VERBOSE=0 # add to ~/.zshrc
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
dkit exec <cmd> [args] Run command without TTY (scripting/CI)
|
|
89
|
+
dkit run <cmd> [args] Run command interactively (TTY)
|
|
90
|
+
dkit shell Open interactive zsh shell in container
|
|
91
|
+
dkit code [path] Open VS Code attached to devcontainer
|
|
92
|
+
dkit claude [args] Run claude inside container
|
|
93
|
+
|
|
94
|
+
dkit status Show resolved devcontainer context
|
|
95
|
+
dkit status --quiet Exit 0 if running, 1 otherwise (for scripting)
|
|
96
|
+
dkit root Print project root (no docker required)
|
|
97
|
+
|
|
98
|
+
dkit up [service] docker compose up -d
|
|
99
|
+
dkit down [flags] docker compose down
|
|
100
|
+
dkit logs [service] docker compose logs -f
|
|
101
|
+
|
|
102
|
+
dkit init Create .devcontainer/dkit-intercept
|
|
103
|
+
dkit intercept list|add|remove <cmd>
|
|
104
|
+
dkit hook Emit shell hook for ~/.zshrc
|
|
105
|
+
dkit version
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## How it works
|
|
109
|
+
|
|
110
|
+
1. On `cd`, the shell hook calls `dkit root` to find the nearest `.devcontainer/devcontainer.json`.
|
|
111
|
+
2. It reads `.devcontainer/dkit-intercept` and defines a shell function for each listed command.
|
|
112
|
+
3. Each function calls `dkit status --quiet` to check if the container is running. If yes, it delegates to `dkit run <cmd>`; otherwise it calls the host binary.
|
|
113
|
+
4. `dkit run` resolves the container name from the devcontainer config (via compose YAML, docker labels, or `docker compose ps`) and execs into it at the mirrored working directory.
|
|
114
|
+
|
|
115
|
+
## devcontainer.json requirements
|
|
116
|
+
|
|
117
|
+
dkit reads the following fields:
|
|
118
|
+
|
|
119
|
+
| Field | Default | Purpose |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `service` | `"app"` | Compose service name |
|
|
122
|
+
| `dockerComposeFile` | — | Path(s) to compose file(s) |
|
|
123
|
+
| `workspaceFolder` | `"/workspace"` | Container working directory root |
|
|
124
|
+
| `remoteUser` | `"root"` | User for `docker exec` |
|
|
125
|
+
|
|
126
|
+
## Environment variables
|
|
127
|
+
|
|
128
|
+
| Variable | Purpose |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `DKIT_PROJECT_ROOT` | Override project root resolution (useful in scripts) |
|
|
131
|
+
| `DKIT_VERBOSE` | Set to `0` to suppress routing messages globally |
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
data/bin/dkit
CHANGED
|
@@ -16,7 +16,7 @@ require 'pathname'
|
|
|
16
16
|
require 'shellwords'
|
|
17
17
|
require 'fileutils'
|
|
18
18
|
|
|
19
|
-
VERSION = "0.
|
|
19
|
+
VERSION = "0.3.1"
|
|
20
20
|
DC_CONFIG = ".devcontainer/devcontainer.json"
|
|
21
21
|
DC_INTERCEPT = ".devcontainer/dkit-intercept"
|
|
22
22
|
|
|
@@ -92,6 +92,13 @@ def intercept_remove(project_root, cmd)
|
|
|
92
92
|
puts "dkit: removed '#{cmd}' — reload shell to deactivate (exec zsh)"
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
def verbose_enabled?(project_root)
|
|
96
|
+
return false if ENV["DKIT_VERBOSE"] == "0"
|
|
97
|
+
f = intercept_file(project_root)
|
|
98
|
+
return true unless File.exist?(f)
|
|
99
|
+
!File.readlines(f, chomp: true).any? { |l| l.strip == "verbose: false" }
|
|
100
|
+
end
|
|
101
|
+
|
|
95
102
|
# ── Devcontainer config ────────────────────────────────────────────────────────
|
|
96
103
|
|
|
97
104
|
def load_dc_config(project_root)
|
|
@@ -207,7 +214,7 @@ def cmd_init
|
|
|
207
214
|
cmds += %w[yarn node npx] if File.exist?(File.join(root, "package.json"))
|
|
208
215
|
cmds = %w[bash] if cmds.empty?
|
|
209
216
|
|
|
210
|
-
File.write(f, cmds.join("\n") + "\n")
|
|
217
|
+
File.write(f, "# verbose: false # uncomment to suppress routing messages\n" + cmds.join("\n") + "\n")
|
|
211
218
|
puts "dkit: created #{f}"
|
|
212
219
|
puts "Commands: #{cmds.join(", ")}"
|
|
213
220
|
puts "Tip: commit this file to share with your team"
|
|
@@ -245,6 +252,12 @@ def cmd_hook
|
|
|
245
252
|
if dkit status --quiet 2>/dev/null; then
|
|
246
253
|
dkit run ${cmd} \"\$@\"
|
|
247
254
|
else
|
|
255
|
+
if [[ \"\${DKIT_VERBOSE}\" != \"0\" && -n \"\${_DKIT_ROOT}\" ]]; then
|
|
256
|
+
local _ic=\"\${_DKIT_ROOT}/.devcontainer/dkit-intercept\"
|
|
257
|
+
if ! grep -qxF 'verbose: false' \"\$_ic\" 2>/dev/null; then
|
|
258
|
+
print -u2 \"[dkit] ${cmd} → host\"
|
|
259
|
+
fi
|
|
260
|
+
fi
|
|
248
261
|
command ${cmd} \"\$@\"
|
|
249
262
|
fi
|
|
250
263
|
}"
|
|
@@ -270,6 +283,12 @@ def cmd_hook
|
|
|
270
283
|
if dkit status --quiet 2>/dev/null; then
|
|
271
284
|
dkit code "$@"
|
|
272
285
|
else
|
|
286
|
+
if [[ "${DKIT_VERBOSE}" != "0" && -n "${_DKIT_ROOT}" ]]; then
|
|
287
|
+
local _ic="${_DKIT_ROOT}/.devcontainer/dkit-intercept"
|
|
288
|
+
if ! grep -qxF 'verbose: false' "$_ic" 2>/dev/null; then
|
|
289
|
+
print -u2 "[dkit] code → host"
|
|
290
|
+
fi
|
|
291
|
+
fi
|
|
273
292
|
command code "$@"
|
|
274
293
|
fi
|
|
275
294
|
}
|
|
@@ -278,6 +297,12 @@ def cmd_hook
|
|
|
278
297
|
if dkit status --quiet 2>/dev/null; then
|
|
279
298
|
dkit claude "$@"
|
|
280
299
|
else
|
|
300
|
+
if [[ "${DKIT_VERBOSE}" != "0" && -n "${_DKIT_ROOT}" ]]; then
|
|
301
|
+
local _ic="${_DKIT_ROOT}/.devcontainer/dkit-intercept"
|
|
302
|
+
if ! grep -qxF 'verbose: false' "$_ic" 2>/dev/null; then
|
|
303
|
+
print -u2 "[dkit] claude → host"
|
|
304
|
+
fi
|
|
305
|
+
fi
|
|
281
306
|
command claude "$@"
|
|
282
307
|
fi
|
|
283
308
|
}
|
|
@@ -290,12 +315,14 @@ end
|
|
|
290
315
|
|
|
291
316
|
def cmd_exec(ctx, args)
|
|
292
317
|
abort_err("exec: no command given") if args.empty?
|
|
318
|
+
warn "[dkit] #{args.join(" ")} → #{ctx.container}" if verbose_enabled?(ctx.project_root)
|
|
293
319
|
system("docker", "exec", "--user", ctx.user, "--workdir", ctx.cwd, ctx.container, *args)
|
|
294
320
|
exit $?.exitstatus
|
|
295
321
|
end
|
|
296
322
|
|
|
297
323
|
def cmd_run(ctx, args)
|
|
298
324
|
abort_err("run: no command given") if args.empty?
|
|
325
|
+
warn "[dkit] #{args.join(" ")} → #{ctx.container}" if verbose_enabled?(ctx.project_root)
|
|
299
326
|
exec("docker", "exec", "-it", "--user", ctx.user, "--workdir", ctx.cwd, ctx.container, *args)
|
|
300
327
|
end
|
|
301
328
|
|
|
@@ -374,6 +401,10 @@ def cmd_help
|
|
|
374
401
|
dkit intercept add <cmd> Add command to current project's intercept list
|
|
375
402
|
dkit intercept remove <cmd> Remove command from current project's intercept list
|
|
376
403
|
|
|
404
|
+
Verbose routing messages (on by default):
|
|
405
|
+
Add 'verbose: false' to .devcontainer/dkit-intercept (per project, committed)
|
|
406
|
+
Export DKIT_VERBOSE=0 (personal override)
|
|
407
|
+
|
|
377
408
|
dkit hook Emit shell hook code for ~/.zshrc
|
|
378
409
|
dkit version Print version
|
|
379
410
|
dkit help Show this help
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dkit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Augusto Stroligo
|
|
@@ -19,11 +19,18 @@ executables:
|
|
|
19
19
|
extensions: []
|
|
20
20
|
extra_rdoc_files: []
|
|
21
21
|
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE
|
|
24
|
+
- README.md
|
|
22
25
|
- bin/dkit
|
|
23
|
-
homepage:
|
|
26
|
+
homepage: https://github.com/ggstroligo/dkit
|
|
24
27
|
licenses:
|
|
25
28
|
- MIT
|
|
26
|
-
metadata:
|
|
29
|
+
metadata:
|
|
30
|
+
source_code_uri: https://github.com/ggstroligo/dkit
|
|
31
|
+
changelog_uri: https://github.com/ggstroligo/dkit/blob/main/CHANGELOG.md
|
|
32
|
+
bug_tracker_uri: https://github.com/ggstroligo/dkit/issues
|
|
33
|
+
rubygems_mfa_required: 'true'
|
|
27
34
|
post_install_message:
|
|
28
35
|
rdoc_options: []
|
|
29
36
|
require_paths:
|