squared 0.4.19 → 0.5.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.
@@ -4,7 +4,7 @@ module Squared
4
4
  module Workspace
5
5
  module Project
6
6
  class Docker < Base
7
- COMPOSEFILE = %w[compose.yaml compose.yml docker-compose.yaml docker-compose.yml].freeze
7
+ COMPOSEFILE = %w[compose.yaml compose.yml docker-compose.yaml compose.yml docker-compose.yml].freeze
8
8
  BAKEFILE = %w[docker-bake.json docker-bake.hcl docker-bake.override.json docker-bake.override.hcl].freeze
9
9
  DIR_DOCKER = (COMPOSEFILE + BAKEFILE).freeze
10
10
  OPT_DOCKER = {
@@ -12,59 +12,57 @@ module Squared
12
12
  tlskey=p].freeze,
13
13
  buildx: {
14
14
  common: %w[builder=b D|debug],
15
- build: %w[add-host=q annotation=q attest=q build-arg=qq build-context=qq cache-from=q cache-to=q
16
- cgroup-parent=b ent=q iidfile=p label=q a-file=p network=b no-cache-filter=b o|output=q platform=b
17
- q|quiet secret=qq shm-size=b ssh=qq t|tag=b target=b ulimit=q].freeze,
18
- bake: %w[print list=q set=q].freeze,
19
- shared: %w[check load no-cache pull push allow=q call=b? f|file=p metadata-file=p progress=b provenance=q
20
- sbom=q].freeze
15
+ build: %w[load pull push add-host=q annotation=q attest=q build-arg=qq ent=q iidfile=p label=q a-file=p
16
+ network=b no-cache-filter=b o|output=q platform=b q|quiet secret=qq shm-size=b ssh=qq t|tag=b
17
+ target=b ulimit=q].freeze,
18
+ bake: %w[load print pull push list=q metadata-file=p set=q].freeze,
19
+ shared: %w[check no-cache allow=q call=b? f|file=p progress=b provenance=q sbom=q].freeze
21
20
  }.freeze,
22
21
  compose: {
23
- common: %w[all-resources compatibility dry-run ansi|b env-file=p f|file=p parallel=n profile=b progress=b
22
+ common: %w[all-resources compatibility dry-run ansi|b env-file=p f|file=p parallel=b profile=b progress=b
24
23
  project-directory=p p|project-name=e].freeze,
25
- build: %w[check no-cache print pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b
26
- provenance=q sbom=q ssh=qq].freeze,
27
- exec: %w[d|detach privileged e|env=qq index=i T|no-TTY=b? user=e w|workdir=q].freeze,
28
- run: %w[build d|detach no-deps q|quiet quiet-build quiet-pull remove-orphans rm P|service-ports use-aliases
29
- cap-add=b cap-drop=b q e|env=qq env-from-file=p i|interactive=b? l|label=q name=b T|no-TTY=b?
30
- p|publish=e pull=b u|user=e v|volume=q w|workdir=q].freeze,
31
- up: %w[abort-on-container-exit abort-on-container-failure always-recreate-deps attach-dependencies build
32
- d|detach force-recreate menu no-build no-color no-deps no-log-prefix no-recreate no-start quiet-build
33
- quiet-pull remove-orphans V|renew-anon-volumes timestamps wait w|watch y|yes attach=b
34
- exit-code-from=b no-attach=b pull=b scale=i t|timeout=i wait-timeout=i].freeze,
35
- down: %w[remove-orphans v|volumes rmi=b t|timeout=i].freeze
24
+ build: %w[no-cache pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b ssh=qq].freeze,
25
+ exec: %w[dry-run privileged d|detach e|env=qq index=i T|no-TTY=b? user=e w|workdir=q].freeze,
26
+ run: %w[build dry-run no-deps quiet-pull remove-orphans rm P|service-ports use-aliases cap-add=b cap-drop=b
27
+ d|detach entrypoint=q e|env=qq i|interactive=b? l|label=q name=b T|no-TTY=b? p|publish=e pull=b
28
+ u|user=e v|volume=q w|workdir=q].freeze,
29
+ up: %w[y abort-on-container-exit abort-on-container-failure always-recreate-deps attach-dependencies build
30
+ d|detach dry-run force-recreate menu no-build no-color no-deps no-log-prefix no-recreate no-start
31
+ quiet-pull remove-orphans V|renew-anon-volumes timestamps wait w|watch attach=b exit-code-from=b
32
+ no-attach=b pull=b scale=i t|timeout=i wait-timeout=i].freeze
36
33
  }.freeze,
37
34
  container: {
38
35
  create: %w[init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet read-only
39
- rm runtime t|tty use-api-socket io-maxbandwidth=b io-maxiops=b add-host=q annotation=q a|attach=b
40
- blkio-weight=i blkio-weight-device=i cap-add=b cap-drop=b cgroup-parent=b cgroupns=b cidfile=p
41
- device=q device-cgroup-rule=q device-read-bps=q device-read-iops=q device-write-bps=q
36
+ rm runtime t|tty use-api-socket io-maxbandwidth=b io-maxiops=b add-host=q annotation=q
37
+ a|attach=b blkio-weight=i blkio-weight-device=i cap-add=b cap-drop=b cgroup-parent=b cgroupns=b
38
+ cidfile=p device=q device-cgroup-rule=q device-read-bps=q device-read-iops=q device-write-bps=q
42
39
  device-write-iops=q disable-content-trust=b? dns=e dns-option=e dns-search=e domainname=b
43
- entrypoint=q e|env=qq env-file=p expose=e gpus=q group-add=b health-cmd=q health-interval=b ip6=e
44
- ipc=b isolation=b kernel-memory=b l|label=q label-file=p link=b link-local-ip=b log-driver=b
45
- log-opt=q mac-address=e m|memory=b memory-reservation=b memory-swap=n memory-swappiness=n
46
- mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b pids-limit=n platform=b
47
- p|publish=e pull=b restart=b runtime=b security-opt=q shm-size=b stop-signal=b stop-timeout=i
48
- storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b v|volume=q volume-driver=b
49
- volumes-from=b w|workdir=q].freeze,
40
+ entrypoint=q e|env=qq env-file=p expose=e gpus=q group-add=b health-cmd=q health-interval=b
41
+ health-retries=i health-start-interval=b health-start-period=b health-timeout=b h|hostname=e ip=b
42
+ ip6=e ipc=b isolation=b kernel-memory=b l|label=q label-file=p link=b link-local-ip=b
43
+ log-driver=b log-opt=q mac-address=e m|memory=b memory-reservation=b memory-swap=n
44
+ memory-swappiness=n mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b pids-limit=n
45
+ platform=b p|publish=e pull=b restart=b runtime=b security-opt=q shm-size=b stop-signal=b
46
+ stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b v|volume=q
47
+ volume-driver=b volumes-from=b w|workdir=q].freeze,
50
48
  run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
49
+ exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p user=e
50
+ w|workdir=q].freeze,
51
51
  update: %w[blkio-weight=i cpu-period=i cpu-quota=i cpu-rt-period=i cpu-rt-runtime=i c|cpu-shares=i cpus=f
52
52
  cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=b pids-limit=n
53
53
  restart=q].freeze,
54
- exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p user=e
55
- w|workdir=q].freeze,
56
54
  commit: %w[a|author=q c|change=q m|message=q pause=b?].freeze,
57
55
  inspect: %w[s|size f|format=q].freeze,
58
56
  start: %w[a|attach i|interactive detach-keys=q].freeze,
59
- stop: %w[s|signal=b t|time=i t|timeout=i].freeze,
60
- restart: %w[s|signal=b t|time=i t|timeout=i].freeze,
57
+ stop: %w[s|signal=b t|time=i].freeze,
58
+ restart: %w[s|signal=b t|time=i].freeze,
61
59
  kill: %w[s|signal=b].freeze,
62
- stats: %w[a|all no-stream no-trunc format|q].freeze
60
+ stats: %w[no-trunc format|q].freeze
63
61
  }.freeze,
64
62
  image: {
65
- list: %w[a|all q|quiet digests no-trunc tree f|filter=q format=q].freeze,
63
+ list: %w[a|all digests no-trunc f|filter=q format=q].freeze,
66
64
  push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
67
- rm: %w[f|force no-prune platform=b].freeze,
65
+ rm: %w[f|force no-prune].freeze,
68
66
  save: %w[o|output=p platform=b].freeze
69
67
  }.freeze,
70
68
  network: {
@@ -74,11 +72,8 @@ module Squared
74
72
  }.freeze
75
73
  VAL_DOCKER = {
76
74
  run: {
77
- common: %w[source src destination dst target readonly ro].freeze,
78
- bind: %w[bind-propagation].freeze,
79
- volume: %w[volume-subpath volume-nocopy volume-opt].freeze,
80
- tmpfs: %w[tmpfs-size tmpfs-mode].freeze,
81
- image: %w[image-path].freeze
75
+ bind: %w[type source src destination dst target readonly ro bind-propagation].freeze,
76
+ tmpfs: %w[type destination dst target tmpfs-size tmpfs-mode].freeze
82
77
  }.freeze
83
78
  }.freeze
84
79
  private_constant :COMPOSEFILE, :BAKEFILE, :OPT_DOCKER, :VAL_DOCKER
@@ -96,9 +91,8 @@ module Squared
96
91
  end
97
92
 
98
93
  subtasks({
99
- 'build' => %i[tag context].freeze,
100
- 'compose' => %i[build run exec up down].freeze,
101
- 'bake' => %i[build check].freeze,
94
+ 'build' => %i[tag context bake].freeze,
95
+ 'compose' => %i[build run exec up].freeze,
102
96
  'image' => %i[list rm push tag save].freeze,
103
97
  'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
104
98
  rm].freeze,
@@ -117,6 +111,7 @@ module Squared
117
111
  @mounts = mounts
118
112
  @secrets = secrets
119
113
  @registry = tagjoin registry, kwargs[:username]
114
+ @file = nil
120
115
  initialize_ref Docker.ref
121
116
  initialize_logger(**kwargs)
122
117
  initialize_env(**kwargs)
@@ -129,11 +124,11 @@ module Squared
129
124
 
130
125
  def populate(*, **)
131
126
  super
132
- return unless ref?(Docker.ref) || @only
127
+ return unless ref?(Docker.ref)
133
128
 
134
129
  namespace name do
135
130
  Docker.subtasks do |action, flags|
136
- next if task_pass?(action)
131
+ next if @pass.include?(action)
137
132
 
138
133
  namespace action do
139
134
  flags.each do |flag|
@@ -146,39 +141,26 @@ module Squared
146
141
  param = param_guard(action, flag, args: args, key: flag)
147
142
  buildx(:build, args.extras, "#{flag}": param)
148
143
  end
149
- end
150
- when 'bake'
151
- break unless bake?
152
-
153
- case flag
154
- when :build
155
- format_desc action, flag, 'opts*,target*,context?|:'
144
+ when :bake
145
+ format_desc action, flag, ':?,opts*,target*,context?'
156
146
  task flag do |_, args|
157
147
  args = args.to_a
158
148
  if args.first == ':'
159
149
  choice_command :bake
160
150
  else
161
- buildx :bake, args
151
+ buildx flag, args
162
152
  end
163
153
  end
164
- when :check
165
- format_desc action, flag, 'target'
166
- task flag, [:target] do |_, args|
167
- target = param_guard(action, flag, args: args, key: :target)
168
- buildx :bake, ['allow=fs.read=*', 'call=check', target]
169
- end
170
154
  end
171
155
  when 'compose'
172
- break unless compose?
173
-
174
156
  case flag
175
- when :build, :up, :down
157
+ when :build, :up
176
158
  format_desc action, flag, 'opts*,service*'
177
159
  task flag do |_, args|
178
160
  compose! flag, args.to_a
179
161
  end
180
162
  when :exec, :run
181
- format_desc action, flag, "service,command#{flag == :exec ? '' : '?'}|:,args*,opts*"
163
+ format_desc action, flag, "service,command#{flag == :exec ? '' : '?'},args*,opts*"
182
164
  task flag, [:service] do |_, args|
183
165
  service = param_guard(action, flag, args: args, key: :service)
184
166
  compose!(flag, args.extras, service: service)
@@ -216,15 +198,14 @@ module Squared
216
198
  when :push
217
199
  format_desc action, flag, 'tag,registry/username?,opts*'
218
200
  task flag, [:tag] do |_, args|
219
- tag = param_guard(action, flag, args: args, key: :tag)
220
- image(flag, args.extras, id: tag)
201
+ id = param_guard(action, flag, args: args, key: :tag)
202
+ image(flag, args.extras, id: id)
221
203
  end
222
204
  else
223
205
  format_desc(action, flag, case flag
224
206
  when :rm, :save then 'id*,opts*'
225
207
  when :tag then 'version?'
226
- else 'opts*,args*'
227
- end)
208
+ else 'opts*,args*' end)
228
209
  task flag do |_, args|
229
210
  args = args.to_a
230
211
  if args.empty? && flag != :list
@@ -261,11 +242,12 @@ module Squared
261
242
 
262
243
  ret = docker_session
263
244
  if from == :run
264
- if bake?(n = filetype)
245
+ case (n = filetype)
246
+ when 1, 2
265
247
  ret << 'buildx' << 'bake'
266
248
  append_file n
267
249
  from = :bake
268
- elsif compose?(n)
250
+ when 3, 4
269
251
  ret << 'compose' << 'build'
270
252
  append_file n
271
253
  from = :compose
@@ -283,11 +265,10 @@ module Squared
283
265
  when Enumerable
284
266
  ret.merge(opts.to_a)
285
267
  end
268
+
286
269
  [args, flags].each_with_index do |target, index|
287
- if target.is_a?(String)
288
- ret << target
289
- elsif (target = append_any(target, target: []))
290
- ret.merge(target.map { |arg| index == 0 ? fill_option(arg) : quote_option('build-arg', arg) })
270
+ if (data = append_any(target, target: []))
271
+ ret.merge(data.map { |arg| index == 0 ? fill_option(arg) : quote_option('build-arg', arg) })
291
272
  end
292
273
  end
293
274
  case from
@@ -297,12 +278,12 @@ module Squared
297
278
  ret << quote_option('secret', @secrets, double: true)
298
279
  when Hash
299
280
  append = lambda do |type|
300
- Array(@secrets[type]).each { |arg| ret << quote_option('secret', "type=#{type},#{arg}", double: true) }
281
+ as_a(@secrets[type]).each { |arg| ret << quote_option('secret', "type=#{type},#{arg}", double: true) }
301
282
  end
302
283
  append.call(:file)
303
284
  append.call(:env)
304
285
  else
305
- Array(@secrets).each { |arg| ret << quote_option('secret', arg) }
286
+ as_a(@secrets).each { |arg| ret << quote_option('secret', arg) }
306
287
  end
307
288
  if (val = option('tag', ignore: false))
308
289
  append_tag val
@@ -311,8 +292,8 @@ module Squared
311
292
  end
312
293
  append_context
313
294
  when :bake, :compose
314
- if (val = option(from == :bake ? 'target' : 'service', ignore: false))
315
- ret.merge(split_escape(val).map! { |s| shell_escape(s) })
295
+ option(from == :bake ? 'target' : 'service', ignore: false) do |a|
296
+ ret.merge(split_escape(a).map! { |b| shell_escape(b) })
316
297
  end
317
298
  end
318
299
  ret
@@ -325,20 +306,20 @@ module Squared
325
306
  op.parse(OPT_DOCKER[:buildx][flag == :bake ? :bake : :build] + OPT_DOCKER[:buildx][:shared])
326
307
  case flag
327
308
  when :build, :context
328
- append_tag(tag || option('tag', ignore: false) || self.tag)
309
+ append_tag(tag || option('tag', ignore: false) || @tag)
329
310
  append_context context
330
311
  when :bake
331
312
  unless op.empty?
332
313
  args = op.dup
333
- op.reset
314
+ op.extras.clear
334
315
  if Dir.exist?(args.last)
335
316
  if projectpath?(val = args.pop)
336
317
  context = val
337
318
  else
338
- op.push(val)
319
+ op.extras << val
339
320
  end
340
321
  end
341
- op.append(args, escape: true, strip: /^:/)
322
+ op.append(args, escape: true)
342
323
  contextdir context if context
343
324
  end
344
325
  end
@@ -351,14 +332,15 @@ module Squared
351
332
  op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self)
352
333
  append_file filetype unless op.arg?('f', 'file')
353
334
  op << flag
354
- op.parse(OPT_DOCKER[:compose].fetch(flag, []))
335
+ op.parse(OPT_DOCKER[:compose][flag])
336
+ from = :"compose:#{flag}"
355
337
  case flag
356
- when :build, :up, :down
357
- op.append(escape: true, strip: /^:/)
338
+ when :build, :up
339
+ op.append(escape: true)
358
340
  when :exec, :run
359
- append_command flag, service, op.extras
341
+ append_command(flag, service, op.extras, from: from)
360
342
  end
361
- run(from: :"compose:#{flag}")
343
+ run(from: from)
362
344
  end
363
345
 
364
346
  def container(flag, opts = [], id: nil)
@@ -373,54 +355,44 @@ module Squared
373
355
  when :run, :create, :exec
374
356
  if rc && !op.arg?('mount')
375
357
  run = VAL_DOCKER[:run]
376
- all = collect_hash VAL_DOCKER[:run]
377
- delim = Regexp.new(",\\s*(?=#{all.join('|')})")
378
- Array(@mounts).each do |val|
358
+ both = run[:bind] + run[:tmpfs]
359
+ diff = run[:bind].reject { |val| run[:tmpfs].include?(val) }
360
+ delim = Regexp.new(",\\s*(?=#{both.join('|')})")
361
+ as_a(@mounts).each do |val|
379
362
  args = []
380
- type = nil
363
+ tmpfs = true
381
364
  val.split(delim).each do |opt|
382
365
  k, v, q = split_option opt
383
- if k == 'type'
384
- case v
385
- when 'bind', 'volume', 'image', 'tmpfs'
386
- type = v
387
- else
388
- raise_error("unknown type: #{v}", hint: flag)
389
- end
390
- elsif all.include?(k)
391
- unless type
392
- run.each_pair do |key, val|
393
- next unless val.include?(k)
366
+ next unless both.include?(k)
394
367
 
395
- type = key.to_s unless key == :common
396
- break
397
- end
398
- end
399
- case k
400
- when 'readonly', 'ro'
401
- args << k
402
- next
403
- when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-path'
404
- v = path + v
405
- v = shell_quote(v, option: false, force: false) if q == ''
406
- end
407
- args << "#{k}=#{q + v + q}"
408
- elsif verbose
409
- log_message(Logger::INFO, 'unrecognized option', subject: from, hint: k)
368
+ if k == 'type'
369
+ tmpfs = false if v == 'bind'
370
+ next
371
+ elsif diff.include?(k)
372
+ tmpfs = false
410
373
  end
374
+ case k
375
+ when 'readonly', 'ro'
376
+ args << k
377
+ next
378
+ when 'source', 'src', 'destination', 'dst', 'target'
379
+ v = path + v
380
+ v = shell_quote(v, option: false, force: false) if q == ''
381
+ tmpfs = false if k[0] == 's'
382
+ end
383
+ args << "#{k}=#{q + v + q}"
411
384
  end
412
- raise_error('missing type', hint: flag) unless type
413
- cmd << "--mount type=#{type},#{args.join(',')}"
385
+ cmd << "--mount type=#{tmpfs ? 'tmpfs' : 'bind'},#{args.join(',')}"
414
386
  end
415
387
  end
416
- append_command(flag, id || tagmain, op.extras)
388
+ append_command(flag, id || tagmain, op.extras, from: from)
417
389
  when :update
418
- raise_error('missing container', hint: flag) if op.empty?
419
- op.append(escape: true, strip: /^:/)
390
+ raise_error('missing container', hint: from) if op.empty?
391
+ op.append(escape: true)
420
392
  when :commit
421
393
  latest = op.shift || tagmain
422
394
  cmd << id << latest
423
- raise_error("unknown args: #{op.join(', ')}", hint: flag) unless op.empty?
395
+ raise_error("unknown args: #{op.join(', ')}", hint: from) unless op.empty?
424
396
  return unless confirm_command(cmd.to_s, title: from, target: id, as: latest)
425
397
 
426
398
  registry = option('registry') || @registry
@@ -463,13 +435,13 @@ module Squared
463
435
  when :rm
464
436
  status = %w[created exited dead]
465
437
  end
466
- ps = docker_output('ps -a', *status.map { |s| quote_option('filter', "status=#{s}") })
438
+ ps = docker_output('ps -a', *status.map { |s| "--filter=\"status=#{s}\"" })
467
439
  list_image(flag, ps, no: no, hint: "status: #{status.join(', ')}", from: from) do |img|
468
440
  run(cmd.temp(img), from: from)
469
441
  end
470
442
  return
471
443
  else
472
- op.append(escape: true, strip: /^:/)
444
+ op.append(escape: true)
473
445
  end
474
446
  end
475
447
  run(from: from)
@@ -527,10 +499,10 @@ module Squared
527
499
  when :push
528
500
  id ||= option('tag', ignore: false) || tagmain
529
501
  registry ||= op.shift || option('registry') || @registry
530
- raise_error(id ? "unknown args: #{op.join(', ')}" : 'no id/tag given', hint: flag) unless id && op.empty?
531
- raise_error('username/registry not provided', hint: flag) unless registry
502
+ raise_error(id ? "unknown args: #{op.join(', ')}" : 'no id/tag given', hint: from) unless id && op.empty?
503
+ raise_error('username/registry not provided', hint: from) unless registry
532
504
  registry.chomp!('/')
533
- uri = shell_quote("#{registry}/#{id}")
505
+ uri = shell_quote "#{registry}/#{id}"
534
506
  op << uri
535
507
  img = docker_output 'image', 'tag', id, uri
536
508
  return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
@@ -546,7 +518,7 @@ module Squared
546
518
 
547
519
  def network(flag, opts = [], target: nil)
548
520
  cmd, opts = docker_session('network', flag, opts: opts)
549
- op = OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
521
+ op = OptionPartition.new(opts, OPT_DOCKER[:network][flag], cmd, project: self)
550
522
  op.clear
551
523
  from = :"network:#{flag}"
552
524
  list_image(flag, docker_output('ps -a'), from: from) do |img|
@@ -562,18 +534,6 @@ module Squared
562
534
  super || dockerfile.exist?
563
535
  end
564
536
 
565
- def compose?(file = dockerfile)
566
- return file == 3 || file == 4 if file.is_a?(Numeric)
567
-
568
- COMPOSEFILE.include?(File.basename(file))
569
- end
570
-
571
- def bake?(file = dockerfile)
572
- return file == 1 || file == 2 if file.is_a?(Numeric)
573
-
574
- BAKEFILE.include?(File.basename(file))
575
- end
576
-
577
537
  def dockerfile(val = nil)
578
538
  if val == 'Dockerfile'
579
539
  @file = false
@@ -603,41 +563,35 @@ module Squared
603
563
  session('docker', *cmd, main: false, options: false, **kwargs)
604
564
  end
605
565
 
606
- def append_command(flag, val, list, target: @session)
607
- if list.delete(':')
608
- list << readline('Enter command [args]', force: true)
609
- elsif (args = env('DOCKER_ARGS'))
566
+ def append_command(flag, val, list, target: @session, from: nil)
567
+ if (args = env('DOCKER_ARGS'))
610
568
  list << args
611
569
  end
612
570
  case flag
613
571
  when :run
614
572
  unless session_arg?('name', target: target)
615
- target << basic_option('name', dnsname("#{name}_%s" % rand_s(6)))
573
+ target << basic_option('name', dnsname("#{name}_%s" % if RUBY_VERSION >= '3.1'
574
+ require 'random/formatter'
575
+ Random.new.alphanumeric(6)
576
+ else
577
+ (0...6).map { rand(97..122).chr }.join
578
+ end))
616
579
  end
617
580
  when :exec
618
- raise_error('no command args', hint: flag) if list.empty?
581
+ raise_error('no command args', hint: from) if list.empty?
619
582
  end
620
583
  target << val << list.shift
621
584
  target << list.join(' && ') unless list.empty?
622
585
  end
623
586
 
624
587
  def append_file(type, target: @session)
625
- return if ENV['COMPOSE_FILE'] && compose?(type)
588
+ return unless type == 2 || type == 4 || @file.is_a?(Array)
626
589
 
627
- unless @file.is_a?(Array)
628
- case type
629
- when 2, 4
630
- return
631
- when 3
632
- return unless COMPOSEFILE.map { |val| path + val }.select(&:exist?).size > 1
633
- end
634
- end
635
- files = Array(@file).map { |val| quote_option('file', path + val) }
590
+ files = as_a(@file).map { |val| quote_option('file', path + val) }
636
591
  if target.is_a?(Set)
637
- opts = target.to_a.insert(2, *files)
638
- target.clear.merge(opts)
592
+ target.merge(files)
639
593
  else
640
- target.insert(2, *files)
594
+ target.concat(files)
641
595
  end
642
596
  end
643
597
 
@@ -649,18 +603,20 @@ module Squared
649
603
  end
650
604
 
651
605
  def append_tag(val, target: @session)
652
- ver = option('version', target: target, ignore: false)
653
- list = case val
654
- when String
655
- val.split(',')
656
- when Array
657
- val
658
- else
659
- []
660
- end
661
- list.each do |s|
662
- s = "#{s.sub(/:latest$/, '')}:#{ver}" if ver && (!s.include?(':') || s.end_with?(':latest'))
663
- target << basic_option('tag', tagname(s))
606
+ case val
607
+ when String
608
+ val.split(',')
609
+ when Array
610
+ val
611
+ else
612
+ []
613
+ end.yield_self do |list|
614
+ ver = option('version', target: target, ignore: false)
615
+ list.each do |s|
616
+ s = "#{s}:#{ver}" if ver && (!s.include?(':') || s.delete_suffix!(':latest'))
617
+ target << basic_option('tag', tagname(s))
618
+ end
619
+ target
664
620
  end
665
621
  end
666
622
 
@@ -670,7 +626,7 @@ module Squared
670
626
  index = 0
671
627
  all = option('all', prefix: 'docker')
672
628
  y = from == :'image:rm' && option('y', prefix: 'docker')
673
- pat = /\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})\b/
629
+ pat = /^(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})(?:[_.,:-]|$)/
674
630
  IO.popen(session_done(cmd << '--format=json')).each do |line|
675
631
  data = JSON.parse(line)
676
632
  id = data['ID']
@@ -707,7 +663,7 @@ module Squared
707
663
  cols.each do |key|
708
664
  next if (key == 'Tag' && !dd) || (key == 'Size' && data[key] == '0B')
709
665
 
710
- puts "#{g + f} #{key}: #{Array(data[key]).join(', ')}" unless data[key].to_s.empty?
666
+ puts "#{g + f} #{key}: #{as_a(data[key]).join(', ')}" unless data[key].to_s.empty?
711
667
  end
712
668
  w = 9 + flag.to_s.size + 4 + ee.size
713
669
  puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), styles: theme[:inline])
@@ -719,10 +675,14 @@ module Squared
719
675
  end
720
676
  yield id
721
677
  end
722
- puts log_message(Logger::INFO, 'none detected', subject: name, hint: hint || from) if !found && !y
678
+ puts log_message(Logger::INFO, 'none detected', subject: "#{name}:#{from}", hint: hint) if found || y
723
679
  end
724
680
  rescue StandardError => e
725
- on_error e, from
681
+ log.error e
682
+ ret = on(:error, from, e)
683
+ raise if exception && ret != true
684
+
685
+ warn log_message(Logger::WARN, e, pass: true) if warning?
726
686
  end
727
687
 
728
688
  def confirm_command(*args, title: nil, target: nil, as: nil)
@@ -779,7 +739,7 @@ module Squared
779
739
  cmd = docker_output ctx
780
740
  case flag
781
741
  when :tag
782
- args = tagjoin @registry, tag
742
+ args = tagjoin @registry, @tag
783
743
  when :save
784
744
  opts = "#{opts}.tar" unless opts.end_with?('.tar')
785
745
  cmd << quote_option('output', File.expand_path(opts))
@@ -790,9 +750,13 @@ module Squared
790
750
  else
791
751
  cmd << opts << '--'
792
752
  end
793
- cmd.merge(Array(out).map! { |val| parse.call(val) })
753
+ cmd.merge(if out.is_a?(Array)
754
+ out.map! { |val| parse.call(val) }
755
+ else
756
+ [parse.call(out)]
757
+ end)
794
758
  cmd << args
795
- print_success if success?(run(cmd)) && ctx.match?(/\A(?:network|tag|save)/)
759
+ print_success if success?(run(cmd)) && ctx.start_with?(/(?:network|tag|save)/)
796
760
  end
797
761
  end
798
762
 
@@ -802,7 +766,7 @@ module Squared
802
766
  bake?(val) ? 1 : 2
803
767
  when '.yml', '.yaml'
804
768
  if compose?(val)
805
- @only&.include?('compose') || path.children.none? { |file| bake?(file) } ? 3 : 1
769
+ path.children.any? { |file| bake?(file) } ? 1 : 3
806
770
  else
807
771
  4
808
772
  end
@@ -822,9 +786,10 @@ module Squared
822
786
 
823
787
  def tagname(val)
824
788
  val = val.split(':').map! { |s| charname(s.sub(/^\W+/, '')) }
825
- ret = val.join(':')
826
- ret = val.first if val.size > 1 && ret.size > 128
827
- ret[0..127]
789
+ val.join(':').yield_self do |s|
790
+ s = val.first if val.size > 1 && s.size > 128
791
+ s[0..127]
792
+ end
828
793
  end
829
794
 
830
795
  def dnsname(val)
@@ -838,6 +803,14 @@ module Squared
838
803
  def tagmain
839
804
  tag.is_a?(Array) ? tag.first : tag
840
805
  end
806
+
807
+ def compose?(file = dockerfile)
808
+ COMPOSEFILE.include?(File.basename(file))
809
+ end
810
+
811
+ def bake?(file = dockerfile)
812
+ BAKEFILE.include?(File.basename(file))
813
+ end
841
814
  end
842
815
 
843
816
  Application.implement Docker