wslc-wip 0.17.6 → 0.17.7
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 +1 -1
- data/lib/wip/command_builder.rb +4 -2
- data/lib/wip/compose_file.rb +33 -21
- data/lib/wip/config.rb +13 -0
- data/lib/wip/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f6c715d39600201d79b4181892f546555b4ccb48fb6046815c2dd27d2e4d203
|
|
4
|
+
data.tar.gz: 4aab099d2fa77a875c8037fb865739d9858d59abda28ae4d3bda83d85b2ac7aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75beec87e7d2b2d4b9a2e71f72a0d126d4e407840a3c9d4fc06a63af80d5fe7d0b4ae68cfbaaf85b9fe749356f9f455953118110cc2c955db5bb152c9eed5dab
|
|
7
|
+
data.tar.gz: 3ac4feb3c65bef872f8b86027ecff32f9f3891a6f3c630b481167e87fb18e7b17a1fed631a23731a9445275e37feb521cb2a0ecf98adf82d7e0a7df9f515536d
|
data/README.md
CHANGED
|
@@ -370,7 +370,7 @@ valid compose.yml over sections it doesn't need to look at:
|
|
|
370
370
|
| `wip version` | wip's version, plus WSLC's if it can be detected |
|
|
371
371
|
| `wip doctor` | Diagnose WSL2, interop, WSLC, config, architecture, and Git |
|
|
372
372
|
| `wip config` | Print the effective configuration (secrets masked) |
|
|
373
|
-
| `wip build [--no-cache] [-- OPTIONS]` | Build the image from the `build` definition.
|
|
373
|
+
| `wip build [--no-cache] [-- OPTIONS]` | Build the image from the `build` definition. `wslc build` reuses matching local layers by default; `--no-cache` disables that. |
|
|
374
374
|
| `wip up [-d] [--no-sync] [--no-cache]` | Start the primary `dependencies:` entry (`container:` names which one) and its sidecars (creating any that are missing, on `network:` if set). `-d` runs the main container in the background; with `sync:` configured, the source is mirrored into the volume first unless `--no-sync` |
|
|
375
375
|
| `wip down` | Stop and remove the primary container and its sidecar `dependencies:` |
|
|
376
376
|
| `wip exec [--no-interactive] COMMAND...` | Run a command in the existing container |
|
data/lib/wip/command_builder.rb
CHANGED
|
@@ -136,14 +136,16 @@ module Wip
|
|
|
136
136
|
[@wslc, 'remove', '-f', name.to_s]
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
+
# `wslc build` reuses matching local layers by default (like `docker build`
|
|
140
|
+
# without `--pull`) — it has no `--cache-from` flag to ask for that explicitly,
|
|
141
|
+
# and passing one is a hard error (unrecognized argument).
|
|
139
142
|
def build(settings:, extra: [])
|
|
140
143
|
values = primary_values.merge(settings)
|
|
141
144
|
context = values['context'] || '.'
|
|
142
145
|
tag = values['tag'] || values['image']
|
|
143
146
|
raise ConfigError, 'Build image/tag must not be empty' if tag.to_s.empty?
|
|
144
147
|
|
|
145
|
-
|
|
146
|
-
[@wslc, 'build', '-t', tag, *cache_options, *extra, context]
|
|
148
|
+
[@wslc, 'build', '-t', tag, *extra, context]
|
|
147
149
|
end
|
|
148
150
|
|
|
149
151
|
def custom(name, arguments)
|
data/lib/wip/compose_file.rb
CHANGED
|
@@ -15,20 +15,18 @@ module Wip
|
|
|
15
15
|
# "Compose mode (native)") — once wslc ships that support, or a
|
|
16
16
|
# compose-for-wslc tool reliably supports `run`.
|
|
17
17
|
class ComposeFile
|
|
18
|
-
Service = Struct.new(:image, :build, :command, :env, :ports, :volumes, :workdir, :user, :depends_on,
|
|
18
|
+
Service = Struct.new(:image, :build, :command, :env, :ports, :volumes, :workdir, :user, :depends_on, :profiles,
|
|
19
19
|
keyword_init: true)
|
|
20
20
|
|
|
21
|
-
SERVICE_KEYS = %w[image build command environment ports volumes working_dir user depends_on].freeze
|
|
21
|
+
SERVICE_KEYS = %w[image build command environment ports volumes working_dir user depends_on profiles].freeze
|
|
22
22
|
# Real Compose keys that read as meaningful here but have nothing to map onto: TTY/stdin
|
|
23
|
-
# allocation is
|
|
24
|
-
# service
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
# real `docker compose up` starts by default, which doesn't apply here since wip never starts
|
|
28
|
-
# "every service" — only `compose.service` and whatever it reaches via `depends_on`.
|
|
29
|
-
IGNORED_SERVICE_KEYS = %w[tty stdin_open networks restart cap_add profiles].freeze
|
|
23
|
+
# allocation is decided per invocation (CommandBuilder#tty?), not per service; every
|
|
24
|
+
# service already shares one project network (config.rb); and `wslc run`/`exec` has no
|
|
25
|
+
# restart-policy or capability flag to forward `restart:`/`cap_add:` to.
|
|
26
|
+
IGNORED_SERVICE_KEYS = %w[tty stdin_open networks restart cap_add].freeze
|
|
30
27
|
BUILD_KEYS = %w[context dockerfile args].freeze
|
|
31
28
|
SUPPORTED_CONDITIONS = %w[service_started].freeze
|
|
29
|
+
LIST_HINT = 'only supports short syntax ("host:container"), not long-syntax mappings'
|
|
32
30
|
|
|
33
31
|
# env interpolates compose.yml's ${VAR} references the way `docker compose` does,
|
|
34
32
|
# so values like `user: ${USER_ID}:${GROUP_ID}` reach wslc already substituted
|
|
@@ -53,9 +51,13 @@ module Wip
|
|
|
53
51
|
|
|
54
52
|
def service_names_in_dependency_order = @order.dup
|
|
55
53
|
|
|
54
|
+
# True if `name` is gated behind profiles: (wip has no --profile flag); false for an unknown name.
|
|
55
|
+
def profiled?(name) = @services[name.to_s]&.profiles&.any? || false
|
|
56
|
+
|
|
56
57
|
# name => {context:, dockerfile:, tag:} for every service with a build:.
|
|
58
|
+
# Profile-gated services are skipped — see #startable_order.
|
|
57
59
|
def build_specs
|
|
58
|
-
|
|
60
|
+
startable_order.filter_map do |name|
|
|
59
61
|
service = @services.fetch(name)
|
|
60
62
|
next unless service.build
|
|
61
63
|
|
|
@@ -66,7 +68,7 @@ module Wip
|
|
|
66
68
|
# Shaped like Config::DEPENDENCY_DEFAULTS expects: image/command/env/ports/volumes/workdir,
|
|
67
69
|
# in dependency order so callers iterating sidecars start them before their dependents.
|
|
68
70
|
def to_dependencies_hash
|
|
69
|
-
|
|
71
|
+
startable_order.to_h do |name|
|
|
70
72
|
service = @services.fetch(name)
|
|
71
73
|
[name, { 'image' => service.build ? image_tag(name, service) : service.image, 'command' => service.command,
|
|
72
74
|
'env' => service.env, 'ports' => service.ports, 'volumes' => service.volumes,
|
|
@@ -76,6 +78,10 @@ module Wip
|
|
|
76
78
|
|
|
77
79
|
private
|
|
78
80
|
|
|
81
|
+
# A profile-gated service (see #profiled?) is never among the ones wip starts
|
|
82
|
+
# on its own, but still participates in #topological_order/depends_on validation.
|
|
83
|
+
def startable_order = @order.reject { |name| profiled?(name) }
|
|
84
|
+
|
|
79
85
|
# A service naming both `build:` and `image:` builds via the former and tags the
|
|
80
86
|
# result with the latter (real Compose's own rule for that combination), instead
|
|
81
87
|
# of the auto-generated tag a build:-only service gets.
|
|
@@ -96,7 +102,8 @@ module Wip
|
|
|
96
102
|
|
|
97
103
|
def normalize_service_lists(name, entry)
|
|
98
104
|
{ ports: normalize_list(name, entry['ports'], 'ports'),
|
|
99
|
-
volumes: normalize_list(name, entry['volumes'], 'volumes')
|
|
105
|
+
volumes: normalize_list(name, entry['volumes'], 'volumes'),
|
|
106
|
+
profiles: normalize_list(name, entry['profiles'], 'profiles', hint: 'must be an array of strings') }
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
def image_or_build(name, entry)
|
|
@@ -129,10 +136,8 @@ module Wip
|
|
|
129
136
|
|
|
130
137
|
def normalize_build_mapping(name, value)
|
|
131
138
|
unknown = value.keys.map(&:to_s) - BUILD_KEYS
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"#{@path}: services.#{name}.build has unsupported key(s): #{unknown.join(', ')}"
|
|
135
|
-
end
|
|
139
|
+
raise ConfigError, "#{@path}: services.#{name}.build has unsupported key(s): #{unknown.join(', ')}" \
|
|
140
|
+
unless unknown.empty?
|
|
136
141
|
|
|
137
142
|
context = resolve_context(presence(value['context']) || '.')
|
|
138
143
|
dockerfile = presence(value['dockerfile'])
|
|
@@ -179,13 +184,12 @@ module Wip
|
|
|
179
184
|
end
|
|
180
185
|
end
|
|
181
186
|
|
|
182
|
-
def normalize_list(name, value, key)
|
|
187
|
+
def normalize_list(name, value, key, hint: LIST_HINT)
|
|
183
188
|
return [] unless value
|
|
184
189
|
raise ConfigError, "#{@path}: services.#{name}.#{key} must be an array" unless value.is_a?(Array)
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
end
|
|
190
|
+
|
|
191
|
+
nested = value.any? { |item| item.is_a?(Hash) || item.is_a?(Array) }
|
|
192
|
+
raise ConfigError, "#{@path}: services.#{name}.#{key} #{hint}" if nested
|
|
189
193
|
|
|
190
194
|
value.map(&:to_s)
|
|
191
195
|
end
|
|
@@ -209,10 +213,18 @@ module Wip
|
|
|
209
213
|
dep.to_s
|
|
210
214
|
end
|
|
211
215
|
|
|
216
|
+
# Also rejects a startable (unprofiled) service depending on a profile-gated one —
|
|
217
|
+
# with no profile activation, real Compose treats that as an invalid model, since
|
|
218
|
+
# the dependency would silently never start.
|
|
212
219
|
def validate_depends_on!
|
|
213
220
|
@services.each do |name, service|
|
|
214
221
|
service.depends_on.each do |dep|
|
|
215
222
|
raise ConfigError, "#{@path}: services.#{name} depends_on unknown service '#{dep}'" unless @services.key?(dep)
|
|
223
|
+
next if service.profiles.any? || !profiled?(dep)
|
|
224
|
+
|
|
225
|
+
raise ConfigError, "#{@path}: services.#{name} depends_on '#{dep}', gated behind profiles: " \
|
|
226
|
+
"(#{@services.fetch(dep).profiles.join(', ')}) wip never activates " \
|
|
227
|
+
'(no --profile flag)'
|
|
216
228
|
end
|
|
217
229
|
end
|
|
218
230
|
end
|
data/lib/wip/config.rb
CHANGED
|
@@ -204,6 +204,19 @@ module Wip
|
|
|
204
204
|
|
|
205
205
|
def parsed_compose_file
|
|
206
206
|
@parsed_compose_file ||= ComposeFile.load(ComposeBridge.file_path(self), env: compose_interpolation_env)
|
|
207
|
+
.tap { |file| validate_compose_service_startable!(file) }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# compose.service is what wip always starts by default (container:) — wip has no
|
|
211
|
+
# --profile flag to activate one, so naming a profile-gated service here would
|
|
212
|
+
# otherwise fail later with a misleading "No dependencies.<name> entry" instead
|
|
213
|
+
# of pointing at the real cause.
|
|
214
|
+
def validate_compose_service_startable!(file)
|
|
215
|
+
return unless file.profiled?(compose_service)
|
|
216
|
+
|
|
217
|
+
raise ConfigError, "compose.service '#{compose_service}' is gated behind profiles: in compose.yml, " \
|
|
218
|
+
'but wip has no --profile flag to activate one — pick a service with no profiles: ' \
|
|
219
|
+
'or remove profiles: from it'
|
|
207
220
|
end
|
|
208
221
|
|
|
209
222
|
# Compose interpolates ${VAR} references in compose.yml from the shell environment
|
data/lib/wip/version.rb
CHANGED