wslc-wip 0.3.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/README.md +70 -40
- data/lib/wip/cli.rb +36 -10
- data/lib/wip/command_builder.rb +7 -2
- data/lib/wip/command_runner.rb +16 -1
- data/lib/wip/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d9b186a9b511381675ce6b5c7073292bc3ba8401287807c2f088d722f7cc1d7c
|
|
4
|
+
data.tar.gz: 84a5b9b0ed0c9a3ada3ffe06eb793c12192d36d76b22197c9310776ddbe28c18
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b46b8daa014806f40f361a5a8fb696cfbfa66f7e0652be23de5d328aaa13401248d640126767c39f1d87ca0329e2428e4a3abe7e19fb5d94e139209e43421646
|
|
7
|
+
data.tar.gz: ed2eb9f43b08a29f8ac48d16e2820f7aa088bd8d8eb2e8db5f27c22b145f4e26e082f6b0efda81db9e04d395dc7a90a96a9b211b5d723ebd859fd0605899f28d
|
data/README.md
CHANGED
|
@@ -1,37 +1,49 @@
|
|
|
1
1
|
# wip
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/slidict/wip/actions/workflows/test.yml)
|
|
4
|
+
[](https://rubygems.org/gems/wslc-wip)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](wslc-wip.gemspec)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
`wip` is a Ruby-built OSS CLI wrapper that brings a [`dip`](https://github.com/bibendi/dip)-like
|
|
9
|
+
workflow to Microsoft WSLC. It collects a project's container, image, environment variables, and
|
|
10
|
+
commands into a single `wip.yml`, and forwards them to `wslc.exe` / `wslc` as safe argument arrays
|
|
11
|
+
(no shell interpolation).
|
|
6
12
|
|
|
7
|
-
|
|
13
|
+

|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
> **Status:** early release. Expect to track WSLC's own interface as it evolves.
|
|
16
|
+
|
|
17
|
+
## Requirements & installation
|
|
18
|
+
|
|
19
|
+
Ruby 3.2+, WSL2, and Microsoft WSLC.
|
|
10
20
|
|
|
11
21
|
```bash
|
|
12
|
-
gem install wip
|
|
22
|
+
gem install wslc-wip
|
|
13
23
|
```
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
From source: `bundle install && bundle exec exe/wip version`.
|
|
16
26
|
|
|
17
|
-
##
|
|
27
|
+
## Quick start
|
|
18
28
|
|
|
19
29
|
```bash
|
|
20
|
-
gem install wip
|
|
30
|
+
gem install wslc-wip
|
|
21
31
|
cd my-project
|
|
22
32
|
wip doctor
|
|
23
33
|
wip build
|
|
34
|
+
wip up -d
|
|
24
35
|
wip rails console
|
|
25
36
|
```
|
|
26
37
|
|
|
27
|
-
##
|
|
38
|
+
## Full configuration example
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
Put a `wip.yml` in your project root. Running from a subdirectory walks up to find it, or pass
|
|
41
|
+
`--config PATH` to point at one explicitly.
|
|
30
42
|
|
|
31
43
|
```yaml
|
|
32
44
|
version: 1
|
|
33
45
|
wslc:
|
|
34
|
-
command: auto # wslc.exe
|
|
46
|
+
command: auto # tries wslc.exe, wslc, then System32; an absolute path also works
|
|
35
47
|
defaults:
|
|
36
48
|
container: app
|
|
37
49
|
image: slidict/slidict:development
|
|
@@ -46,7 +58,8 @@ defaults:
|
|
|
46
58
|
volumes:
|
|
47
59
|
- ".:/app"
|
|
48
60
|
up:
|
|
49
|
-
command: server # `wip up`
|
|
61
|
+
command: server # command `wip up` passes to the image when it creates the container
|
|
62
|
+
# (omit to use the image's default CMD)
|
|
50
63
|
commands:
|
|
51
64
|
rails:
|
|
52
65
|
type: exec
|
|
@@ -74,46 +87,53 @@ commands:
|
|
|
74
87
|
tag: slidict/slidict:development
|
|
75
88
|
```
|
|
76
89
|
|
|
77
|
-
`env`
|
|
90
|
+
`env` values are stringified. `wip config` masks any key matching token, password, secret,
|
|
91
|
+
credential, or auth. Keep real secrets out of the config file and in your runtime environment
|
|
92
|
+
instead.
|
|
78
93
|
|
|
79
|
-
##
|
|
94
|
+
## Commands
|
|
80
95
|
|
|
81
|
-
|
|
|
96
|
+
| Command | Description |
|
|
82
97
|
|---|---|
|
|
83
|
-
| `wip version` | wip
|
|
84
|
-
| `wip doctor` | WSL2
|
|
85
|
-
| `wip config` |
|
|
86
|
-
| `wip build -- --no-cache` | build
|
|
87
|
-
| `wip up [-d]` | `defaults.container`
|
|
88
|
-
| `wip down` | `defaults.container`
|
|
89
|
-
| `wip exec [--no-interactive] COMMAND...` |
|
|
90
|
-
| `wip run [--no-interactive] COMMAND...` | `--rm`
|
|
91
|
-
| `wip shell` |
|
|
92
|
-
| `wip NAME ARGS...` | `commands.NAME
|
|
93
|
-
|
|
94
|
-
TTY
|
|
98
|
+
| `wip version` | wip's version, plus WSLC's if it can be detected |
|
|
99
|
+
| `wip doctor` | Diagnose WSL2, interop, WSLC, config, architecture, and Git |
|
|
100
|
+
| `wip config` | Print the effective configuration (secrets masked) |
|
|
101
|
+
| `wip build -- --no-cache` | Build the image from the `build` definition |
|
|
102
|
+
| `wip up [-d]` | Start `defaults.container` (creating it with `up.command` if missing). `-d` runs it in the background |
|
|
103
|
+
| `wip down` | Stop and remove `defaults.container` |
|
|
104
|
+
| `wip exec [--no-interactive] COMMAND...` | Run a command in the existing container |
|
|
105
|
+
| `wip run [--no-interactive] COMMAND...` | Run a command in a new `--rm` container |
|
|
106
|
+
| `wip shell` | Open the configured shell, falling back to `bash` then `sh` |
|
|
107
|
+
| `wip NAME ARGS...` | Run `commands.NAME`, appending any extra arguments |
|
|
108
|
+
|
|
109
|
+
TTY allocation is decided by combining the command's config, the CLI option, and whether both
|
|
110
|
+
stdin and stdout are real TTYs. Set `WIP_DEBUG=1` to print the `Shellwords`-joined command before
|
|
111
|
+
running it.
|
|
95
112
|
|
|
96
113
|
## doctor
|
|
97
114
|
|
|
98
|
-
`[OK]
|
|
115
|
+
Each check prints as `[OK]`, `[WARN]`, or `[FAIL]`. Warnings alone exit 0; a WSL2, interop, WSLC,
|
|
116
|
+
or config problem that blocks execution exits 1. Git being unreachable from the real build
|
|
117
|
+
environment is only a warning.
|
|
99
118
|
|
|
100
|
-
##
|
|
119
|
+
## Common errors
|
|
101
120
|
|
|
102
|
-
### WSLC
|
|
121
|
+
### WSLC not found
|
|
103
122
|
|
|
104
|
-
|
|
123
|
+
Install or update the WSL container tooling, then run `wip doctor`. `auto` looks for `wslc.exe`,
|
|
124
|
+
`wslc`, then `/mnt/c/Windows/System32/wslc.exe`, in that order.
|
|
105
125
|
|
|
106
|
-
### Docker Hub
|
|
126
|
+
### Docker Hub authentication
|
|
107
127
|
|
|
108
|
-
`pull access denied`
|
|
128
|
+
When `pull access denied` (or similar) is detected, wip suggests how to log in:
|
|
109
129
|
|
|
110
130
|
```bash
|
|
111
131
|
wslc registry login -u <username> docker.io
|
|
112
132
|
```
|
|
113
133
|
|
|
114
|
-
### CPU
|
|
134
|
+
### CPU architecture mismatch
|
|
115
135
|
|
|
116
|
-
|
|
136
|
+
Check the image and publish a multi-arch (amd64/arm64) image:
|
|
117
137
|
|
|
118
138
|
```bash
|
|
119
139
|
docker buildx imagetools inspect <image>
|
|
@@ -123,10 +143,10 @@ docker buildx build \
|
|
|
123
143
|
--push .
|
|
124
144
|
```
|
|
125
145
|
|
|
126
|
-
##
|
|
146
|
+
## Development
|
|
127
147
|
|
|
128
148
|
```bash
|
|
129
|
-
git clone
|
|
149
|
+
git clone https://github.com/slidict/wip.git
|
|
130
150
|
cd wip
|
|
131
151
|
bundle install
|
|
132
152
|
bundle exec rspec
|
|
@@ -134,11 +154,21 @@ bundle exec rubocop
|
|
|
134
154
|
bundle exec rake
|
|
135
155
|
```
|
|
136
156
|
|
|
137
|
-
|
|
157
|
+
The test suite doesn't need WSLC — the resolution, build, and execution layers are all
|
|
158
|
+
swappable. GitHub Actions runs RSpec and RuboCop on Ruby 3.2, 3.3, 3.4, and 4.0.
|
|
159
|
+
|
|
160
|
+
## Contributing
|
|
161
|
+
|
|
162
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/slidict/wip). See
|
|
163
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) for commit conventions, versioning policy, and the PR
|
|
164
|
+
checklist.
|
|
138
165
|
|
|
139
|
-
##
|
|
166
|
+
## Not in the initial release
|
|
140
167
|
|
|
141
|
-
Compose
|
|
168
|
+
Compose compatibility, a resident/daemon process, a GUI, PowerShell-specific tuning, direct
|
|
169
|
+
registry API/manifest parsing, self-update, and plugins are all unimplemented. Likely future
|
|
170
|
+
additions: a richer config schema, lifecycle hooks, multi-container support, platform selection,
|
|
171
|
+
and more detailed diagnostics.
|
|
142
172
|
|
|
143
173
|
## License
|
|
144
174
|
|
data/lib/wip/cli.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
4
|
require 'yaml'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'stringio'
|
|
5
7
|
|
|
6
8
|
module Wip
|
|
7
9
|
# Thor-based command-line interface for wip.
|
|
@@ -54,8 +56,15 @@ module Wip
|
|
|
54
56
|
desc 'up', 'Start the configured container, creating it if necessary'
|
|
55
57
|
option :detach, type: :boolean, default: false, aliases: '-d'
|
|
56
58
|
def up
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
container = load_config.defaults['container']
|
|
60
|
+
interactive = builder.tty?(!options[:detach])
|
|
61
|
+
if container_exists?
|
|
62
|
+
warn "wip: starting existing container '#{container}'"
|
|
63
|
+
execute(builder.start(detach: options[:detach]), interactive: interactive)
|
|
64
|
+
else
|
|
65
|
+
warn "wip: container '#{container}' not found, creating it"
|
|
66
|
+
execute(builder.up(detach: options[:detach]), interactive: interactive)
|
|
67
|
+
end
|
|
59
68
|
end
|
|
60
69
|
|
|
61
70
|
desc 'down', 'Stop and remove the configured container'
|
|
@@ -67,24 +76,32 @@ module Wip
|
|
|
67
76
|
desc 'exec COMMAND...', 'Execute a command in the running container'
|
|
68
77
|
option :interactive, type: :boolean, default: true
|
|
69
78
|
def exec(*command)
|
|
70
|
-
|
|
79
|
+
tty = builder.tty?(options[:interactive])
|
|
80
|
+
execute(builder.exec(command, interactive: options[:interactive]), interactive: tty)
|
|
71
81
|
end
|
|
72
82
|
|
|
73
83
|
desc 'run COMMAND...', 'Run a command in a new container'
|
|
74
84
|
option :interactive, type: :boolean, default: true
|
|
75
85
|
def run_command(*command)
|
|
76
|
-
|
|
86
|
+
tty = builder.tty?(options[:interactive])
|
|
87
|
+
execute(builder.run(command, interactive: options[:interactive]), interactive: tty)
|
|
77
88
|
end
|
|
78
89
|
map 'run' => :run_command
|
|
79
90
|
|
|
80
91
|
desc 'shell', 'Open a shell in the configured container'
|
|
81
92
|
def shell_command
|
|
82
93
|
configured = load_config.command('shell')
|
|
83
|
-
|
|
94
|
+
if configured
|
|
95
|
+
tty = builder.tty?(configured.fetch('interactive', false))
|
|
96
|
+
return execute(builder.custom('shell', []), interactive: tty)
|
|
97
|
+
end
|
|
84
98
|
|
|
99
|
+
tty = builder.tty?(true)
|
|
85
100
|
code = execute(builder.exec(['bash'], settings: { 'interactive' => true }, interactive: true),
|
|
86
|
-
exit_on_failure: false)
|
|
87
|
-
|
|
101
|
+
interactive: tty, exit_on_failure: false)
|
|
102
|
+
return if code.zero?
|
|
103
|
+
|
|
104
|
+
execute(builder.exec(['sh'], settings: { 'interactive' => true }, interactive: true), interactive: tty)
|
|
88
105
|
end
|
|
89
106
|
map 'shell' => :shell_command
|
|
90
107
|
|
|
@@ -92,7 +109,8 @@ module Wip
|
|
|
92
109
|
def dispatch(name = nil, *arguments)
|
|
93
110
|
raise ConfigError, 'A command is required' unless name
|
|
94
111
|
|
|
95
|
-
|
|
112
|
+
values = load_config.command(name) || raise(ConfigError, "Unknown command: #{name}")
|
|
113
|
+
execute(builder.custom(name, arguments), interactive: builder.tty?(values.fetch('interactive', false)))
|
|
96
114
|
end
|
|
97
115
|
|
|
98
116
|
private
|
|
@@ -102,10 +120,18 @@ module Wip
|
|
|
102
120
|
def resolver = CommandResolver.new
|
|
103
121
|
def builder = CommandBuilder.new(wslc: resolver.resolve(load_config.wslc_command), config: load_config)
|
|
104
122
|
|
|
105
|
-
def execute(command, exit_on_failure: true)
|
|
106
|
-
code = CommandRunner.new.run(command)
|
|
123
|
+
def execute(command, interactive: false, exit_on_failure: true)
|
|
124
|
+
code = CommandRunner.new.run(command, interactive: interactive)
|
|
107
125
|
exit(code) if exit_on_failure && !code.zero?
|
|
108
126
|
code
|
|
109
127
|
end
|
|
128
|
+
|
|
129
|
+
def container_exists?
|
|
130
|
+
out = StringIO.new
|
|
131
|
+
code = CommandRunner.new(stdout: out, stderr: StringIO.new).run(builder.find)
|
|
132
|
+
code.zero? && !JSON.parse(out.string).empty?
|
|
133
|
+
rescue JSON::ParserError
|
|
134
|
+
false
|
|
135
|
+
end
|
|
110
136
|
end
|
|
111
137
|
end
|
data/lib/wip/command_builder.rb
CHANGED
|
@@ -42,6 +42,11 @@ module Wip
|
|
|
42
42
|
command
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def find
|
|
46
|
+
container = required(@config.defaults, 'container')
|
|
47
|
+
[@wslc, 'list', '--all', '--filter', "name=#{container}", '--format', 'json']
|
|
48
|
+
end
|
|
49
|
+
|
|
45
50
|
def down
|
|
46
51
|
[@wslc, 'stop', required(@config.defaults, 'container')]
|
|
47
52
|
end
|
|
@@ -68,6 +73,8 @@ module Wip
|
|
|
68
73
|
public_send(type, base + arguments, settings: values, interactive: values.fetch('interactive', false))
|
|
69
74
|
end
|
|
70
75
|
|
|
76
|
+
def tty?(requested) = requested && @environment.interactive?
|
|
77
|
+
|
|
71
78
|
private
|
|
72
79
|
|
|
73
80
|
def options(values, include_container: false, include_publish: true)
|
|
@@ -88,7 +95,5 @@ module Wip
|
|
|
88
95
|
|
|
89
96
|
value
|
|
90
97
|
end
|
|
91
|
-
|
|
92
|
-
def tty?(requested) = requested && @environment.interactive?
|
|
93
98
|
end
|
|
94
99
|
end
|
data/lib/wip/command_runner.rb
CHANGED
|
@@ -13,8 +13,10 @@ module Wip
|
|
|
13
13
|
@interpreter = interpreter
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def run(command, env: {})
|
|
16
|
+
def run(command, env: {}, interactive: false)
|
|
17
17
|
@stderr.puts "+ #{Shellwords.join(command)}" if ENV['WIP_DEBUG']
|
|
18
|
+
return run_attached(command, env) if interactive
|
|
19
|
+
|
|
18
20
|
captured = +''
|
|
19
21
|
status = nil
|
|
20
22
|
Open3.popen3(env, *command) do |input, output, error, wait|
|
|
@@ -33,6 +35,19 @@ module Wip
|
|
|
33
35
|
|
|
34
36
|
private
|
|
35
37
|
|
|
38
|
+
# Piping stdin/stdout/stderr (as `run` does above) closes the child's
|
|
39
|
+
# stdin immediately, which breaks anything that reads from the terminal
|
|
40
|
+
# (a shell, `rails console`, ...). Inherit the real file descriptors
|
|
41
|
+
# instead so the child gets a genuine TTY.
|
|
42
|
+
def run_attached(command, env)
|
|
43
|
+
pid = Process.spawn(env, *command)
|
|
44
|
+
_, status = Process.wait2(pid)
|
|
45
|
+
status.exitstatus
|
|
46
|
+
rescue Errno::ENOENT => e
|
|
47
|
+
@stderr.puts e.message
|
|
48
|
+
127
|
|
49
|
+
end
|
|
50
|
+
|
|
36
51
|
def pump(source, destination, captured)
|
|
37
52
|
Thread.new do
|
|
38
53
|
source.each(4096) do |chunk|
|
data/lib/wip/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wslc-wip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Wip contributors
|
|
@@ -44,11 +44,14 @@ files:
|
|
|
44
44
|
- lib/wip/error_interpreter.rb
|
|
45
45
|
- lib/wip/errors.rb
|
|
46
46
|
- lib/wip/version.rb
|
|
47
|
-
homepage: https://github.com/
|
|
47
|
+
homepage: https://github.com/slidict/wip
|
|
48
48
|
licenses:
|
|
49
49
|
- MIT
|
|
50
50
|
metadata:
|
|
51
51
|
rubygems_mfa_required: 'true'
|
|
52
|
+
source_code_uri: https://github.com/slidict/wip
|
|
53
|
+
changelog_uri: https://github.com/slidict/wip/releases
|
|
54
|
+
bug_tracker_uri: https://github.com/slidict/wip/issues
|
|
52
55
|
rdoc_options: []
|
|
53
56
|
require_paths:
|
|
54
57
|
- lib
|