squared 0.4.18 → 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.
@@ -21,8 +21,7 @@ module Squared
21
21
  compose: {
22
22
  common: %w[all-resources compatibility dry-run ansi|b env-file=p f|file=p parallel=b profile=b progress=b
23
23
  project-directory=p p|project-name=e].freeze,
24
- build: %w[check no-cache pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b
25
- ssh=qq].freeze,
24
+ build: %w[no-cache pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b ssh=qq].freeze,
26
25
  exec: %w[dry-run privileged d|detach e|env=qq index=i T|no-TTY=b? user=e w|workdir=q].freeze,
27
26
  run: %w[build dry-run no-deps quiet-pull remove-orphans rm P|service-ports use-aliases cap-add=b cap-drop=b
28
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
@@ -55,15 +54,15 @@ module Squared
55
54
  commit: %w[a|author=q c|change=q m|message=q pause=b?].freeze,
56
55
  inspect: %w[s|size f|format=q].freeze,
57
56
  start: %w[a|attach i|interactive detach-keys=q].freeze,
58
- stop: %w[s|signal=b t|time=i t|timeout=i].freeze,
59
- 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,
60
59
  kill: %w[s|signal=b].freeze,
61
60
  stats: %w[no-trunc format|q].freeze
62
61
  }.freeze,
63
62
  image: {
64
63
  list: %w[a|all digests no-trunc f|filter=q format=q].freeze,
65
64
  push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
66
- rm: %w[f|force no-prune platform=b].freeze,
65
+ rm: %w[f|force no-prune].freeze,
67
66
  save: %w[o|output=p platform=b].freeze
68
67
  }.freeze,
69
68
  network: {
@@ -73,11 +72,8 @@ module Squared
73
72
  }.freeze
74
73
  VAL_DOCKER = {
75
74
  run: {
76
- common: %w[source src destination dst target readonly ro].freeze,
77
- bind: %w[bind-propagation].freeze,
78
- volume: %w[volume-subpath volume-nocopy volume-opt].freeze,
79
- tmpfs: %w[tmpfs-size tmpfs-mode].freeze,
80
- 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
81
77
  }.freeze
82
78
  }.freeze
83
79
  private_constant :COMPOSEFILE, :BAKEFILE, :OPT_DOCKER, :VAL_DOCKER
@@ -95,9 +91,8 @@ module Squared
95
91
  end
96
92
 
97
93
  subtasks({
98
- 'build' => %i[tag context].freeze,
94
+ 'build' => %i[tag context bake].freeze,
99
95
  'compose' => %i[build run exec up].freeze,
100
- 'bake' => %i[build check].freeze,
101
96
  'image' => %i[list rm push tag save].freeze,
102
97
  'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
103
98
  rm].freeze,
@@ -116,6 +111,7 @@ module Squared
116
111
  @mounts = mounts
117
112
  @secrets = secrets
118
113
  @registry = tagjoin registry, kwargs[:username]
114
+ @file = nil
119
115
  initialize_ref Docker.ref
120
116
  initialize_logger(**kwargs)
121
117
  initialize_env(**kwargs)
@@ -128,11 +124,11 @@ module Squared
128
124
 
129
125
  def populate(*, **)
130
126
  super
131
- return unless ref?(Docker.ref) || @only
127
+ return unless ref?(Docker.ref)
132
128
 
133
129
  namespace name do
134
130
  Docker.subtasks do |action, flags|
135
- next if task_pass?(action)
131
+ next if @pass.include?(action)
136
132
 
137
133
  namespace action do
138
134
  flags.each do |flag|
@@ -145,31 +141,18 @@ module Squared
145
141
  param = param_guard(action, flag, args: args, key: flag)
146
142
  buildx(:build, args.extras, "#{flag}": param)
147
143
  end
148
- end
149
- when 'bake'
150
- break unless bake?
151
-
152
- case flag
153
- when :build
144
+ when :bake
154
145
  format_desc action, flag, ':?,opts*,target*,context?'
155
146
  task flag do |_, args|
156
147
  args = args.to_a
157
148
  if args.first == ':'
158
149
  choice_command :bake
159
150
  else
160
- buildx :bake, args
151
+ buildx flag, args
161
152
  end
162
153
  end
163
- when :check
164
- format_desc action, flag, 'target'
165
- task flag, [:target] do |_, args|
166
- target = param_guard(action, flag, args: args, key: :target)
167
- buildx :bake, ['allow=fs.read=*', 'call=check', target]
168
- end
169
154
  end
170
155
  when 'compose'
171
- break unless compose?
172
-
173
156
  case flag
174
157
  when :build, :up
175
158
  format_desc action, flag, 'opts*,service*'
@@ -177,7 +160,7 @@ module Squared
177
160
  compose! flag, args.to_a
178
161
  end
179
162
  when :exec, :run
180
- format_desc action, flag, "service,command#{flag == :exec ? '' : '?'}|:,args*,opts*"
163
+ format_desc action, flag, "service,command#{flag == :exec ? '' : '?'},args*,opts*"
181
164
  task flag, [:service] do |_, args|
182
165
  service = param_guard(action, flag, args: args, key: :service)
183
166
  compose!(flag, args.extras, service: service)
@@ -215,8 +198,8 @@ module Squared
215
198
  when :push
216
199
  format_desc action, flag, 'tag,registry/username?,opts*'
217
200
  task flag, [:tag] do |_, args|
218
- tag = param_guard(action, flag, args: args, key: :tag)
219
- image(flag, args.extras, id: tag)
201
+ id = param_guard(action, flag, args: args, key: :tag)
202
+ image(flag, args.extras, id: id)
220
203
  end
221
204
  else
222
205
  format_desc(action, flag, case flag
@@ -282,11 +265,10 @@ module Squared
282
265
  when Enumerable
283
266
  ret.merge(opts.to_a)
284
267
  end
268
+
285
269
  [args, flags].each_with_index do |target, index|
286
- if target.is_a?(String)
287
- ret << target
288
- elsif (target = append_any(target, target: []))
289
- 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) })
290
272
  end
291
273
  end
292
274
  case from
@@ -296,12 +278,12 @@ module Squared
296
278
  ret << quote_option('secret', @secrets, double: true)
297
279
  when Hash
298
280
  append = lambda do |type|
299
- 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) }
300
282
  end
301
283
  append.call(:file)
302
284
  append.call(:env)
303
285
  else
304
- Array(@secrets).each { |arg| ret << quote_option('secret', arg) }
286
+ as_a(@secrets).each { |arg| ret << quote_option('secret', arg) }
305
287
  end
306
288
  if (val = option('tag', ignore: false))
307
289
  append_tag val
@@ -310,8 +292,8 @@ module Squared
310
292
  end
311
293
  append_context
312
294
  when :bake, :compose
313
- if (val = option(from == :bake ? 'target' : 'service', ignore: false))
314
- 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) })
315
297
  end
316
298
  end
317
299
  ret
@@ -324,17 +306,17 @@ module Squared
324
306
  op.parse(OPT_DOCKER[:buildx][flag == :bake ? :bake : :build] + OPT_DOCKER[:buildx][:shared])
325
307
  case flag
326
308
  when :build, :context
327
- append_tag(tag || option('tag', ignore: false) || self.tag)
309
+ append_tag(tag || option('tag', ignore: false) || @tag)
328
310
  append_context context
329
311
  when :bake
330
312
  unless op.empty?
331
313
  args = op.dup
332
- op.reset
314
+ op.extras.clear
333
315
  if Dir.exist?(args.last)
334
316
  if projectpath?(val = args.pop)
335
317
  context = val
336
318
  else
337
- op.push(val)
319
+ op.extras << val
338
320
  end
339
321
  end
340
322
  op.append(args, escape: true)
@@ -356,7 +338,7 @@ module Squared
356
338
  when :build, :up
357
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
343
  run(from: from)
362
344
  end
@@ -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
366
+ next unless both.include?(k)
367
+
383
368
  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
- if val.include?(k)
394
- type = key.to_s unless key == :common
395
- break
396
- end
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)
369
+ tmpfs = false if v == 'bind'
370
+ next
371
+ elsif diff.include?(k)
372
+ tmpfs = false
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'
410
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?
390
+ raise_error('missing container', hint: from) if op.empty?
419
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,7 +435,7 @@ 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
@@ -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)
@@ -562,14 +534,6 @@ module Squared
562
534
  super || dockerfile.exist?
563
535
  end
564
536
 
565
- def compose?(file = dockerfile)
566
- COMPOSEFILE.include?(File.basename(file))
567
- end
568
-
569
- def bake?(file = dockerfile)
570
- BAKEFILE.include?(File.basename(file))
571
- end
572
-
573
537
  def dockerfile(val = nil)
574
538
  if val == 'Dockerfile'
575
539
  @file = false
@@ -599,19 +563,22 @@ module Squared
599
563
  session('docker', *cmd, main: false, options: false, **kwargs)
600
564
  end
601
565
 
602
- def append_command(flag, val, list, target: @session)
603
- if list.delete(':')
604
- list << readline('Enter command [args]', force: true)
605
- elsif (args = env('DOCKER_ARGS'))
566
+ def append_command(flag, val, list, target: @session, from: nil)
567
+ if (args = env('DOCKER_ARGS'))
606
568
  list << args
607
569
  end
608
570
  case flag
609
571
  when :run
610
572
  unless session_arg?('name', target: target)
611
- 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))
612
579
  end
613
580
  when :exec
614
- raise_error('no command args', hint: flag) if list.empty?
581
+ raise_error('no command args', hint: from) if list.empty?
615
582
  end
616
583
  target << val << list.shift
617
584
  target << list.join(' && ') unless list.empty?
@@ -620,7 +587,7 @@ module Squared
620
587
  def append_file(type, target: @session)
621
588
  return unless type == 2 || type == 4 || @file.is_a?(Array)
622
589
 
623
- files = Array(@file).map { |val| quote_option('file', path + val) }
590
+ files = as_a(@file).map { |val| quote_option('file', path + val) }
624
591
  if target.is_a?(Set)
625
592
  target.merge(files)
626
593
  else
@@ -636,18 +603,20 @@ module Squared
636
603
  end
637
604
 
638
605
  def append_tag(val, target: @session)
639
- ver = option('version', target: target, ignore: false)
640
- list = case val
641
- when String
642
- val.split(',')
643
- when Array
644
- val
645
- else
646
- []
647
- end
648
- list.each do |s|
649
- s = "#{s.sub(/:latest$/, '')}:#{ver}" if ver && (!s.include?(':') || s.end_with?(':latest'))
650
- 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
651
620
  end
652
621
  end
653
622
 
@@ -657,7 +626,7 @@ module Squared
657
626
  index = 0
658
627
  all = option('all', prefix: 'docker')
659
628
  y = from == :'image:rm' && option('y', prefix: 'docker')
660
- pat = /\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})\b/
629
+ pat = /^(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})(?:[_.,:-]|$)/
661
630
  IO.popen(session_done(cmd << '--format=json')).each do |line|
662
631
  data = JSON.parse(line)
663
632
  id = data['ID']
@@ -694,7 +663,7 @@ module Squared
694
663
  cols.each do |key|
695
664
  next if (key == 'Tag' && !dd) || (key == 'Size' && data[key] == '0B')
696
665
 
697
- 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?
698
667
  end
699
668
  w = 9 + flag.to_s.size + 4 + ee.size
700
669
  puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), styles: theme[:inline])
@@ -706,10 +675,14 @@ module Squared
706
675
  end
707
676
  yield id
708
677
  end
709
- 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
710
679
  end
711
680
  rescue StandardError => e
712
- 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?
713
686
  end
714
687
 
715
688
  def confirm_command(*args, title: nil, target: nil, as: nil)
@@ -766,7 +739,7 @@ module Squared
766
739
  cmd = docker_output ctx
767
740
  case flag
768
741
  when :tag
769
- args = tagjoin @registry, tag
742
+ args = tagjoin @registry, @tag
770
743
  when :save
771
744
  opts = "#{opts}.tar" unless opts.end_with?('.tar')
772
745
  cmd << quote_option('output', File.expand_path(opts))
@@ -777,9 +750,13 @@ module Squared
777
750
  else
778
751
  cmd << opts << '--'
779
752
  end
780
- 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)
781
758
  cmd << args
782
- 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)/)
783
760
  end
784
761
  end
785
762
 
@@ -809,9 +786,10 @@ module Squared
809
786
 
810
787
  def tagname(val)
811
788
  val = val.split(':').map! { |s| charname(s.sub(/^\W+/, '')) }
812
- ret = val.join(':')
813
- ret = val.first if val.size > 1 && ret.size > 128
814
- 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
815
793
  end
816
794
 
817
795
  def dnsname(val)
@@ -825,6 +803,14 @@ module Squared
825
803
  def tagmain
826
804
  tag.is_a?(Array) ? tag.first : tag
827
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
828
814
  end
829
815
 
830
816
  Application.implement Docker