squared 0.4.12 → 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.
@@ -32,22 +32,24 @@ module Squared
32
32
  no-attach=b pull=b scale=i t|timeout=i wait-timeout=i].freeze
33
33
  }.freeze,
34
34
  container: {
35
- run: %w[d|detach init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet
36
- read-only rm runtime t|tty add-host=q annotation=q a|attach=b blkio-weight-device=i cap-add=b
37
- cap-drop=b cgroup-parent=b cgroupns=b cidfile=p detach-keys=q device=q device-cgroup-rule=q
38
- device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q disable-content-trust=b?
39
- dns=e dns-option=e dns-search=e domainname=b entrypoint=q e|env=qq env-file=p expose=e gpus=q
40
- group-add=b health-cmd=q health-interval=b health-retries=i health-start-interval=b
41
- health-start-period=b health-timeout=b h|hostname=e io-maxbandwidth=b io-maxiops=b ip=b ip6=e ipc=b
42
- isolation=b kernel-memory=b l|label=q label-file=p link=b link-local-ip=b log-driver=b log-opt=q
43
- mac-address=e memory-swappiness=b mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b
44
- platform=b p|publish=e pull=b restart=b runtime=b security-opt=q shm-size=b sig-proxy=b?
45
- stop-signal=b stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q user=e userns=b uts=b
46
- v|volume=q volume-driver=b volumes-from=b w|workdir=q].freeze,
35
+ create: %w[init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet read-only
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
39
+ device-write-iops=q disable-content-trust=b? dns=e dns-option=e dns-search=e domainname=b
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,
48
+ run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
47
49
  exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p user=e
48
50
  w|workdir=q].freeze,
49
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
50
- cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=b pids-limit=b
52
+ cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=b pids-limit=n
51
53
  restart=q].freeze,
52
54
  commit: %w[a|author=q c|change=q m|message=q pause=b?].freeze,
53
55
  inspect: %w[s|size f|format=q].freeze,
@@ -60,7 +62,8 @@ module Squared
60
62
  image: {
61
63
  list: %w[a|all digests no-trunc f|filter=q format=q].freeze,
62
64
  push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
63
- rm: %w[f|force no-prune].freeze
65
+ rm: %w[f|force no-prune].freeze,
66
+ save: %w[o|output=p platform=b].freeze
64
67
  }.freeze,
65
68
  network: {
66
69
  connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=b link=b link-local-ip=b].freeze,
@@ -90,8 +93,8 @@ module Squared
90
93
  subtasks({
91
94
  'build' => %i[tag context bake].freeze,
92
95
  'compose' => %i[build run exec up].freeze,
93
- 'image' => %i[list rm push].freeze,
94
- 'container' => %i[run exec update commit inspect diff start stop restart pause unpause top stats kill
96
+ 'image' => %i[list rm push tag save].freeze,
97
+ 'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
95
98
  rm].freeze,
96
99
  'network' => %i[connect disconnect].freeze
97
100
  })
@@ -107,7 +110,8 @@ module Squared
107
110
  @tag = tag || tagname("#{@project}:#{@version || 'latest'}")
108
111
  @mounts = mounts
109
112
  @secrets = secrets
110
- @registry = [registry, kwargs[:username]].compact.join('/')
113
+ @registry = tagjoin registry, kwargs[:username]
114
+ @file = nil
111
115
  initialize_ref Docker.ref
112
116
  initialize_logger(**kwargs)
113
117
  initialize_env(**kwargs)
@@ -174,7 +178,7 @@ module Squared
174
178
  container(flag, args.extras, id: id)
175
179
  end
176
180
  end
177
- when :run
181
+ when :run, :create
178
182
  format_desc action, flag, 'image,opts*,args*'
179
183
  task flag, [:image] do |_, args|
180
184
  if args.image
@@ -194,15 +198,18 @@ module Squared
194
198
  when :push
195
199
  format_desc action, flag, 'tag,registry/username?,opts*'
196
200
  task flag, [:tag] do |_, args|
197
- tag = param_guard(action, flag, args: args, key: :tag)
198
- image(flag, args.extras, id: tag)
201
+ id = param_guard(action, flag, args: args, key: :tag)
202
+ image(flag, args.extras, id: id)
199
203
  end
200
- when :list, :rm
201
- format_desc(action, flag, flag == :rm ? 'id*,opts*' : 'opts*,args*')
204
+ else
205
+ format_desc(action, flag, case flag
206
+ when :rm, :save then 'id*,opts*'
207
+ when :tag then 'version?'
208
+ else 'opts*,args*' end)
202
209
  task flag do |_, args|
203
210
  args = args.to_a
204
- if flag == :rm && args.empty?
205
- choice_command :rm
211
+ if args.empty? && flag != :list
212
+ choice_command flag
206
213
  else
207
214
  image flag, args
208
215
  end
@@ -260,10 +267,8 @@ module Squared
260
267
  end
261
268
 
262
269
  [args, flags].each_with_index do |target, index|
263
- if target.is_a?(String)
264
- ret << target
265
- elsif (target = append_any(target, target: []))
266
- 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) })
267
272
  end
268
273
  end
269
274
  case from
@@ -287,8 +292,8 @@ module Squared
287
292
  end
288
293
  append_context
289
294
  when :bake, :compose
290
- if (val = option(from == :bake ? 'target' : 'service', ignore: false))
291
- 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) })
292
297
  end
293
298
  end
294
299
  ret
@@ -340,13 +345,15 @@ module Squared
340
345
 
341
346
  def container(flag, opts = [], id: nil)
342
347
  cmd, opts = docker_session('container', flag, opts: opts)
348
+ rc = flag == :run || flag == :create
343
349
  list = OPT_DOCKER[:container].fetch(flag, [])
344
- list += OPT_DOCKER[:container][:update] if flag == :run
345
- op = OptionPartition.new(opts, list, cmd, project: self, args: flag == :run || flag == :exec)
350
+ list += OPT_DOCKER[:container][:create] if flag == :run
351
+ list += OPT_DOCKER[:container][:update] if rc
352
+ op = OptionPartition.new(opts, list, cmd, project: self, args: rc || flag == :exec)
346
353
  from = :"container:#{flag}"
347
354
  case flag
348
- when :run, :exec
349
- if flag == :run && !op.arg?('mount')
355
+ when :run, :create, :exec
356
+ if rc && !op.arg?('mount')
350
357
  run = VAL_DOCKER[:run]
351
358
  both = run[:bind] + run[:tmpfs]
352
359
  diff = run[:bind].reject { |val| run[:tmpfs].include?(val) }
@@ -355,7 +362,7 @@ module Squared
355
362
  args = []
356
363
  tmpfs = true
357
364
  val.split(delim).each do |opt|
358
- k, v, q = split_option(opt)
365
+ k, v, q = split_option opt
359
366
  next unless both.include?(k)
360
367
 
361
368
  if k == 'type'
@@ -378,7 +385,7 @@ module Squared
378
385
  cmd << "--mount type=#{tmpfs ? 'tmpfs' : 'bind'},#{args.join(',')}"
379
386
  end
380
387
  end
381
- append_command(flag, id.to_s.empty? ? tagmain : id, op.extras, from: from)
388
+ append_command(flag, id || tagmain, op.extras, from: from)
382
389
  when :update
383
390
  raise_error('missing container', hint: from) if op.empty?
384
391
  op.append(escape: true)
@@ -442,7 +449,7 @@ module Squared
442
449
 
443
450
  def image(flag, opts = [], sync: true, id: nil, registry: nil)
444
451
  cmd, opts = docker_session('image', flag, opts: opts)
445
- op = OptionPartition.new(opts, OPT_DOCKER[:image][flag], cmd, project: self)
452
+ op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
446
453
  exception = @exception
447
454
  banner = true
448
455
  from = :"image:#{flag}"
@@ -473,7 +480,7 @@ module Squared
473
480
  end
474
481
  else
475
482
  if op.empty?
476
- list_image(flag, docker_output('image ls -a'), from: from) do |val|
483
+ list_image(:rm, docker_output('image ls -a'), from: from) do |val|
477
484
  image(:rm, opts, sync: sync, id: val)
478
485
  end
479
486
  else
@@ -481,13 +488,21 @@ module Squared
481
488
  end
482
489
  return
483
490
  end
491
+ when :tag, :save
492
+ list_image(flag, docker_output('image ls -a'), from: from) do |val|
493
+ op << val
494
+ if flag == :tag
495
+ op << tagname("#{@project}:#{op.extras.first}")
496
+ break
497
+ end
498
+ end
484
499
  when :push
485
500
  id ||= option('tag', ignore: false) || tagmain
486
501
  registry ||= op.shift || option('registry') || @registry
487
502
  raise_error(id ? "unknown args: #{op.join(', ')}" : 'no id/tag given', hint: from) unless id && op.empty?
488
503
  raise_error('username/registry not provided', hint: from) unless registry
489
504
  registry.chomp!('/')
490
- uri = shell_quote("#{registry}/#{id}")
505
+ uri = shell_quote "#{registry}/#{id}"
491
506
  op << uri
492
507
  img = docker_output 'image', 'tag', id, uri
493
508
  return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
@@ -497,7 +512,8 @@ module Squared
497
512
  exception = true
498
513
  banner = false
499
514
  end
500
- run(cmd, sync: sync, exception: exception, banner: banner, from: from)
515
+ ret = run(cmd, sync: sync, exception: exception, banner: banner, from: from)
516
+ print_success if success?(ret) && (flag == :tag || flag == :save)
501
517
  end
502
518
 
503
519
  def network(flag, opts = [], target: nil)
@@ -587,18 +603,20 @@ module Squared
587
603
  end
588
604
 
589
605
  def append_tag(val, target: @session)
590
- ver = option('version', target: target, ignore: false)
591
- list = case val
592
- when String
593
- val.split(',')
594
- when Array
595
- val
596
- else
597
- []
598
- end
599
- list.each do |s|
600
- s = "#{s.sub(/:latest$/, '')}:#{ver}" if ver && (!s.include?(':') || s.end_with?(':latest'))
601
- 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
602
620
  end
603
621
  end
604
622
 
@@ -685,14 +703,14 @@ module Squared
685
703
 
686
704
  def choice_command(flag)
687
705
  msg, cmd, index = case flag
688
- when :run, :rm
689
- ['Choose an image', 'images -a', 2]
690
706
  when :exec
691
707
  ['Choose a container', 'ps -a', 0]
692
708
  when :bake
693
709
  ['Choose a target', 'buildx bake --list=type=targets', 0]
694
- else
710
+ when :connect, :disconnect
695
711
  ['Choose a network', 'network ls', 0]
712
+ else
713
+ ['Choose an image', 'images -a', 2]
696
714
  end
697
715
  lines = `#{docker_output(cmd)}`.lines
698
716
  header = lines.shift
@@ -702,25 +720,43 @@ module Squared
702
720
  puts " # #{header}"
703
721
  multiple = false
704
722
  parse = ->(val) { val.split(/\s+/)[index] }
723
+ ctx = flag.to_s
705
724
  case flag
706
725
  when :run, :exec
707
726
  values = [['Options', flag == :run], ['Arguments', flag == :exec]]
708
- cmd = flag.to_s
709
727
  when :rm, :bake
710
728
  values = ['Options']
711
729
  multiple = true
712
- cmd = flag == :rm ? 'image rm' : "buildx bake -f #{shell_quote(dockerfile)}"
713
- else
730
+ ctx = flag == :rm ? 'image rm' : "buildx bake -f #{shell_quote(dockerfile)}"
731
+ when :save
732
+ values = [['Output', true], 'Platform']
733
+ multiple = true
734
+ when :connect, :disconnect
714
735
  values = ['Options', ['Container', true]]
715
- cmd = "network #{flag}"
736
+ ctx = "network #{flag}"
716
737
  end
717
738
  out, opts, args = choice_index(msg, lines, multiple: multiple, values: values)
718
- ret = run docker_output(cmd, opts, '--', *(if out.is_a?(Array)
719
- out.map! { |val| parse.call(val) }
720
- else
721
- [parse.call(out)]
722
- end), args)
723
- print_success if success?(ret && cmd.start_with?('network'))
739
+ cmd = docker_output ctx
740
+ case flag
741
+ when :tag
742
+ args = tagjoin @registry, @tag
743
+ when :save
744
+ opts = "#{opts}.tar" unless opts.end_with?('.tar')
745
+ cmd << quote_option('output', File.expand_path(opts))
746
+ if args
747
+ cmd << basic_option('platform', args)
748
+ args = nil
749
+ end
750
+ else
751
+ cmd << opts << '--'
752
+ end
753
+ cmd.merge(if out.is_a?(Array)
754
+ out.map! { |val| parse.call(val) }
755
+ else
756
+ [parse.call(out)]
757
+ end)
758
+ cmd << args
759
+ print_success if success?(run(cmd)) && ctx.start_with?(/(?:network|tag|save)/)
724
760
  end
725
761
  end
726
762
 
@@ -743,11 +779,17 @@ module Squared
743
779
  val && projectpath?(val) ? shell_quote(path + val) : '.'
744
780
  end
745
781
 
782
+ def tagjoin(*args, char: '/')
783
+ args.compact!
784
+ args.join(char) unless args.empty?
785
+ end
786
+
746
787
  def tagname(val)
747
788
  val = val.split(':').map! { |s| charname(s.sub(/^\W+/, '')) }
748
- ret = val.join(':')
749
- ret = val.first if val.size > 1 && ret.size > 128
750
- 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
751
793
  end
752
794
 
753
795
  def dnsname(val)