squared 0.4.11 → 0.4.13

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,7 @@ 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]
111
114
  initialize_ref Docker.ref
112
115
  initialize_logger(**kwargs)
113
116
  initialize_env(**kwargs)
@@ -174,7 +177,7 @@ module Squared
174
177
  container(flag, args.extras, id: id)
175
178
  end
176
179
  end
177
- when :run
180
+ when :run, :create
178
181
  format_desc action, flag, 'image,opts*,args*'
179
182
  task flag, [:image] do |_, args|
180
183
  if args.image
@@ -197,12 +200,15 @@ module Squared
197
200
  tag = param_guard(action, flag, args: args, key: :tag)
198
201
  image(flag, args.extras, id: tag)
199
202
  end
200
- when :list, :rm
201
- format_desc(action, flag, flag == :rm ? 'id*,opts*' : 'opts*,args*')
203
+ else
204
+ format_desc(action, flag, case flag
205
+ when :rm, :save then 'id*,opts*'
206
+ when :tag then 'version?'
207
+ else 'opts*,args*' end)
202
208
  task flag do |_, args|
203
209
  args = args.to_a
204
- if flag == :rm && args.empty?
205
- choice_command :rm
210
+ if args.empty? && flag != :list
211
+ choice_command flag
206
212
  else
207
213
  image flag, args
208
214
  end
@@ -340,13 +346,15 @@ module Squared
340
346
 
341
347
  def container(flag, opts = [], id: nil)
342
348
  cmd, opts = docker_session('container', flag, opts: opts)
349
+ rc = flag == :run || flag == :create
343
350
  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)
351
+ list += OPT_DOCKER[:container][:create] if flag == :run
352
+ list += OPT_DOCKER[:container][:update] if rc
353
+ op = OptionPartition.new(opts, list, cmd, project: self, args: rc || flag == :exec)
346
354
  from = :"container:#{flag}"
347
355
  case flag
348
- when :run, :exec
349
- if flag == :run && !op.arg?('mount')
356
+ when :run, :create, :exec
357
+ if rc && !op.arg?('mount')
350
358
  run = VAL_DOCKER[:run]
351
359
  both = run[:bind] + run[:tmpfs]
352
360
  diff = run[:bind].reject { |val| run[:tmpfs].include?(val) }
@@ -378,7 +386,7 @@ module Squared
378
386
  cmd << "--mount type=#{tmpfs ? 'tmpfs' : 'bind'},#{args.join(',')}"
379
387
  end
380
388
  end
381
- append_command(flag, id.to_s.empty? ? tagmain : id, op.extras, from: from)
389
+ append_command(flag, id || tagmain, op.extras, from: from)
382
390
  when :update
383
391
  raise_error('missing container', hint: from) if op.empty?
384
392
  op.append(escape: true)
@@ -442,7 +450,7 @@ module Squared
442
450
 
443
451
  def image(flag, opts = [], sync: true, id: nil, registry: nil)
444
452
  cmd, opts = docker_session('image', flag, opts: opts)
445
- op = OptionPartition.new(opts, OPT_DOCKER[:image][flag], cmd, project: self)
453
+ op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
446
454
  exception = @exception
447
455
  banner = true
448
456
  from = :"image:#{flag}"
@@ -473,7 +481,7 @@ module Squared
473
481
  end
474
482
  else
475
483
  if op.empty?
476
- list_image(flag, docker_output('image ls -a'), from: from) do |val|
484
+ list_image(:rm, docker_output('image ls -a'), from: from) do |val|
477
485
  image(:rm, opts, sync: sync, id: val)
478
486
  end
479
487
  else
@@ -481,6 +489,14 @@ module Squared
481
489
  end
482
490
  return
483
491
  end
492
+ when :tag, :save
493
+ list_image(flag, docker_output('image ls -a'), from: from) do |val|
494
+ op << val
495
+ if flag == :tag
496
+ op << tagname("#{@project}:#{op.extras.first}")
497
+ break
498
+ end
499
+ end
484
500
  when :push
485
501
  id ||= option('tag', ignore: false) || tagmain
486
502
  registry ||= op.shift || option('registry') || @registry
@@ -497,7 +513,8 @@ module Squared
497
513
  exception = true
498
514
  banner = false
499
515
  end
500
- run(cmd, sync: sync, exception: exception, banner: banner, from: from)
516
+ ret = run(cmd, sync: sync, exception: exception, banner: banner, from: from)
517
+ print_success if success?(ret) && (flag == :tag || flag == :save)
501
518
  end
502
519
 
503
520
  def network(flag, opts = [], target: nil)
@@ -653,11 +670,11 @@ module Squared
653
670
  index += 1
654
671
  next unless confirm("#{h + b}? [#{c}] ", d, timeout: 60)
655
672
 
656
- puts if @@print_order == 0
673
+ puts if printfirst?
657
674
  end
658
675
  yield id
659
676
  end
660
- puts log_message(Logger::INFO, 'none detected', subject: "#{name}:#{from}", hint: hint) unless found || y
677
+ puts log_message(Logger::INFO, 'none detected', subject: "#{name}:#{from}", hint: hint) if found || y
661
678
  end
662
679
  rescue StandardError => e
663
680
  log.error e
@@ -670,13 +687,13 @@ module Squared
670
687
  def confirm_command(*args, title: nil, target: nil, as: nil)
671
688
  return false unless title && target
672
689
 
673
- puts unless @@print_order == 0
690
+ puts unless printfirst?
674
691
  t = title.to_s.split(':')
675
692
  emphasize(args, title: message(t.first.upcase, *t.drop(1)), border: borderstyle, sub: [
676
693
  { pat: /\A(\w+(?: => \w+)+)(.*)\z/, styles: theme[:header] },
677
694
  { pat: /\A(.+)\z/, styles: theme[:caution] }
678
695
  ])
679
- @@print_order += 1
696
+ printsucc
680
697
  a = t.last.capitalize
681
698
  b = sub_style(target, styles: theme[:subject])
682
699
  c = as && sub_style(as, styles: theme[:inline])
@@ -685,14 +702,14 @@ module Squared
685
702
 
686
703
  def choice_command(flag)
687
704
  msg, cmd, index = case flag
688
- when :run, :rm
689
- ['Choose an image', 'images -a', 2]
690
705
  when :exec
691
706
  ['Choose a container', 'ps -a', 0]
692
707
  when :bake
693
708
  ['Choose a target', 'buildx bake --list=type=targets', 0]
694
- else
709
+ when :connect, :disconnect
695
710
  ['Choose a network', 'network ls', 0]
711
+ else
712
+ ['Choose an image', 'images -a', 2]
696
713
  end
697
714
  lines = `#{docker_output(cmd)}`.lines
698
715
  header = lines.shift
@@ -702,25 +719,43 @@ module Squared
702
719
  puts " # #{header}"
703
720
  multiple = false
704
721
  parse = ->(val) { val.split(/\s+/)[index] }
722
+ ctx = flag.to_s
705
723
  case flag
706
724
  when :run, :exec
707
725
  values = [['Options', flag == :run], ['Arguments', flag == :exec]]
708
- cmd = flag.to_s
709
726
  when :rm, :bake
710
727
  values = ['Options']
711
728
  multiple = true
712
- cmd = flag == :rm ? 'image rm' : "buildx bake -f #{shell_quote(dockerfile)}"
713
- else
729
+ ctx = flag == :rm ? 'image rm' : "buildx bake -f #{shell_quote(dockerfile)}"
730
+ when :save
731
+ values = [['Output', true], 'Platform']
732
+ multiple = true
733
+ when :connect, :disconnect
714
734
  values = ['Options', ['Container', true]]
715
- cmd = "network #{flag}"
735
+ ctx = "network #{flag}"
716
736
  end
717
737
  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'))
738
+ cmd = docker_output ctx
739
+ case flag
740
+ when :tag
741
+ args = tagjoin @registry, @tag
742
+ when :save
743
+ opts = "#{opts}.tar" unless opts.end_with?('.tar')
744
+ cmd << quote_option('output', File.expand_path(opts))
745
+ if args
746
+ cmd << basic_option('platform', args)
747
+ args = nil
748
+ end
749
+ else
750
+ cmd << opts << '--'
751
+ end
752
+ cmd.merge(if out.is_a?(Array)
753
+ out.map! { |val| parse.call(val) }
754
+ else
755
+ [parse.call(out)]
756
+ end)
757
+ cmd << args
758
+ print_success if success?(run(cmd)) && ctx.match?(/\A(?:network|tag|save)/)
724
759
  end
725
760
  end
726
761
 
@@ -743,6 +778,11 @@ module Squared
743
778
  val && projectpath?(val) ? shell_quote(path + val) : '.'
744
779
  end
745
780
 
781
+ def tagjoin(*args, char: '/')
782
+ args.compact!
783
+ args.join(char) unless args.empty?
784
+ end
785
+
746
786
  def tagname(val)
747
787
  val = val.split(':').map! { |s| charname(s.sub(/^\W+/, '')) }
748
788
  ret = val.join(':')
@@ -14,7 +14,11 @@ module Squared
14
14
  check = ->(proj) { proj.is_a?(Project::Git) && !proj.exclude?(Project::Git.ref) && git_clone?(proj.path) }
15
15
  if uri.is_a?(Array)
16
16
  base = name
17
- uri.each { |val| repo << proj if (proj = @project[val.to_s]) && check.call(proj) }
17
+ uri.each do |val|
18
+ if (proj = @project[val.to_s]) && check.call(proj)
19
+ repo << proj
20
+ end
21
+ end
18
22
  elsif uri
19
23
  data[name.to_s] = uri
20
24
  elsif name.is_a?(Enumerable)
@@ -121,20 +125,18 @@ module Squared
121
125
  def rev_write(name = nil, data = nil, sync: true, utc: nil)
122
126
  return unless @revfile
123
127
 
128
+ sleep 0 while !sync && @revlock
129
+ @revlock = true
124
130
  if name
125
131
  data&.each { |key, val| rev_entry(name, key, val: val) }
126
132
  rev_timeutc(name, utc) if utc
127
133
  end
128
- sleep 0 while !sync && @revlock
129
- begin
130
- @revlock = true
131
- File.write(@revfile, JSON.pretty_generate(@revdoc))
132
- rescue StandardError => e
133
- log&.debug e
134
- warn log_message(Logger::WARN, e, pass: true) if warning?
135
- ensure
136
- @revlock = false
137
- end
134
+ File.write(@revfile, JSON.pretty_generate(@revdoc))
135
+ rescue StandardError => e
136
+ log&.debug e
137
+ warn log_message(Logger::WARN, e, pass: true) if warning?
138
+ ensure
139
+ @revlock = false
138
140
  end
139
141
 
140
142
  def git_clone?(path, name = nil)
@@ -457,8 +459,8 @@ module Squared
457
459
  when 'stash'
458
460
  format_desc(action, flag, 'opts*', after: case flag
459
461
  when :push then 'pathspec*'
460
- when :list then nil
461
- else 'commit?' end)
462
+ when :clear, :list then nil
463
+ else 'stash?|:' end)
462
464
  task flag do |_, args|
463
465
  stash flag, args.to_a
464
466
  end
@@ -516,15 +518,7 @@ module Squared
516
518
  index = choice_commit(multiple: true)
517
519
  else
518
520
  index = []
519
- args.each do |val|
520
- if matchhead(val)
521
- index << commithead(val)
522
- elsif (sha = commithash(val))
523
- index << sha
524
- else
525
- break
526
- end
527
- end
521
+ args.each { |val| index << (commithead(val) || commithash(val) || break) }
528
522
  args = args.drop(index.size)
529
523
  end
530
524
  diff(flag, args, index: index)
@@ -990,7 +984,14 @@ module Squared
990
984
  end
991
985
  end
992
986
  opts[:origin] = val if (val = option('origin', ignore: false))
993
- opts[:branch] = val if (val = option('branch', strict: true))
987
+ if (val = option('branch', strict: true))
988
+ opts[:branch] = val
989
+ opts.delete(:revision)
990
+ elsif (val = option('revision', strict: true))
991
+ opts[:revision] = val
992
+ opts.delete(:branch)
993
+ opts.delete(:mirror)
994
+ end
994
995
  opts[:local] = val != '0' if (val = option('local', strict: true))
995
996
  opts.delete(:'recurse-submodules') || opts.delete(:'no-recurse-submodules') if append_submodules(from: :clone)
996
997
  append_hash opts
@@ -1012,10 +1013,13 @@ module Squared
1012
1013
  when :push
1013
1014
  append_pathspec op.extras
1014
1015
  when :pop, :apply, :drop
1015
- unless op.empty?
1016
+ if op.extras.delete(':')
1017
+ op << choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1018
+ column: /^[^@]+@\{(\d+)\}/, force: true)
1019
+ elsif !op.empty?
1016
1020
  op << shell_escape(op.pop)
1017
- op.clear
1018
1021
  end
1022
+ op.clear
1019
1023
  when :clear
1020
1024
  if confirm("Remove #{sub_style('all', styles: theme[:active])} stash entries? [y/N] ", 'N')
1021
1025
  source(stdout: true)
@@ -1064,7 +1068,7 @@ module Squared
1064
1068
  { pat: /^(## )(.+?)(\.{3})(.+)$/, styles: [nil, g, nil, r], index: -1 }
1065
1069
  ]
1066
1070
  else
1067
- [{ pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2 }]
1071
+ [pat: /^(\t+)([a-z]+: +.+)$/, styles: r, index: 2]
1068
1072
  end
1069
1073
  end
1070
1074
  out, banner, from = source(io: true)
@@ -1072,7 +1076,7 @@ module Squared
1072
1076
  list_result(ret, 'files', from: from, action: 'modified')
1073
1077
  end
1074
1078
 
1075
- def revbuild(flag = nil, opts = [], sync: invoked_sync?('revbuild', flag), **kwargs)
1079
+ def revbuild(flag = nil, opts = [], sync: nil, **kwargs)
1076
1080
  statusargs = lambda do
1077
1081
  {
1078
1082
  include: relativepath(as_a(kwargs[:include]), all: true),
@@ -1090,6 +1094,7 @@ module Squared
1090
1094
  sha = git_spawn('rev-parse --verify HEAD').chomp
1091
1095
  return if sha.empty?
1092
1096
 
1097
+ sync = invoked_sync?('revbuild', flag) if sync.nil?
1093
1098
  kwargs = kwargs.key?(:include) || kwargs.key?(:exclude) ? statusargs.call : @revbuild || {}
1094
1099
  case flag
1095
1100
  when :build
@@ -1457,7 +1462,7 @@ module Squared
1457
1462
  else
1458
1463
  git_spawn 'fetch --all --prune --quiet' if option('sync')
1459
1464
  out, banner, from = source(cmd << '-vv --no-abbrev --list', io: true)
1460
- ret = write_lines(out, grep: /^\*\s+#{Regexp.escape(head)}\s/, banner: banner, first: true) do |line|
1465
+ ret = write_lines(out, grep: [/^\*\s+#{Regexp.escape(head)}\s/], banner: banner, first: true) do |line|
1461
1466
  next line if stdin?
1462
1467
 
1463
1468
  data = line.sub(/^\*\s+/, '').split(/\s+/)
@@ -1585,7 +1590,7 @@ module Squared
1585
1590
  op << shell_quote(remote) if remote
1586
1591
  out, banner, from = source(io: true)
1587
1592
  print_item banner
1588
- ret = write_lines(out, grep: op.extras)
1593
+ ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
1589
1594
  list_result(ret, flag.to_s, from: from, grep: op.extras)
1590
1595
  end
1591
1596
 
@@ -1662,7 +1667,7 @@ module Squared
1662
1667
  def source(cmd = @session, exception: true, io: false, sync: true, stdout: false, stderr: false, banner: true,
1663
1668
  multiple: false, **kwargs)
1664
1669
  cmd = cmd.target if cmd.is_a?(OptionPartition)
1665
- banner = nil if multiple && banner
1670
+ banner = nil if banner && (multiple || !banner?)
1666
1671
  if cmd.respond_to?(:done)
1667
1672
  if io && banner == false
1668
1673
  from = nil
@@ -1723,15 +1728,27 @@ module Squared
1723
1728
  end
1724
1729
  end
1725
1730
 
1726
- def write_lines(data, banner: nil, loglevel: nil, grep: nil, sub: nil, pass: false, first: false)
1727
- grep &&= as_a(grep).yield_self { |a| a.empty? || a.include?('*') ? nil : a.map { |val| Regexp.new(val) } }
1728
- sub &&= stdin? ? nil : as_a(sub)
1731
+ def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1732
+ grep = unless grep.empty?
1733
+ grep.map do |val|
1734
+ if val.is_a?(Regexp)
1735
+ val
1736
+ else
1737
+ val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1738
+ Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1739
+ end
1740
+ end
1741
+ end
1742
+ sub = nil if stdin?
1729
1743
  ret = 0
1730
1744
  out = []
1731
1745
  data.each do |line|
1732
1746
  next if grep&.none? { |pat| pat.match?(line) }
1733
1747
 
1734
- line = yield line if block_given?
1748
+ if block_given?
1749
+ line = yield line
1750
+ next unless line
1751
+ end
1735
1752
  if loglevel
1736
1753
  log&.add loglevel, line
1737
1754
  else
@@ -1749,21 +1766,14 @@ module Squared
1749
1766
  ret
1750
1767
  end
1751
1768
 
1752
- def list_result(size, type, from: nil, action: 'found', grep: nil)
1753
- if verbose
1754
- if size > 0
1755
- styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1756
- styles << :bold if styles.size <= 1
1757
- puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1758
- sub: { pat: /^(\d+)(.+)$/, styles: styles })
1759
- else
1760
- puts empty_status("No #{type} were #{action}", 'grep', grep.is_a?(Array) ? case grep.size
1761
- when 0
1762
- nil
1763
- else
1764
- grep.join(', ')
1765
- end : grep.to_s)
1766
- end
1769
+ def list_result(size, type, grep: [], action: 'found', from: nil)
1770
+ if size == 0
1771
+ puts empty_status("No #{type} were #{action}", 'grep', grep.join(', '))
1772
+ elsif verbose
1773
+ styles = theme.fetch(:banner, []).reject { |s| s.to_s.end_with?('!') }
1774
+ styles << :bold if styles.size <= 1
1775
+ puts print_footer("#{size} #{size == 1 ? type.sub(/(?:(?<!l)e)?s\z/, '') : type}",
1776
+ sub: [pat: /^(\d+)(.+)$/, styles: styles])
1767
1777
  end
1768
1778
  on :last, from
1769
1779
  end
@@ -1843,7 +1853,7 @@ module Squared
1843
1853
  when 'refspec'
1844
1854
  refspec << shell_escape($2, quote: true)
1845
1855
  end
1846
- elsif op.arg?('--multiple')
1856
+ elsif op.arg?('multiple')
1847
1857
  op.found << opt
1848
1858
  else
1849
1859
  op.errors << opt
@@ -1858,7 +1868,7 @@ module Squared
1858
1868
  op.merge(refspec)
1859
1869
  end
1860
1870
  op.delete('--all')
1861
- elsif op.arg?('--multiple')
1871
+ elsif op.arg?('multiple')
1862
1872
  op.swap.merge(op.map! { |opt| shell_escape(opt, quote: true) })
1863
1873
  return
1864
1874
  elsif option('all')
@@ -1975,7 +1985,7 @@ module Squared
1975
1985
  end
1976
1986
 
1977
1987
  def commithash(val)
1978
- val[/:(\h{5,40})\z/, 1] || val[/\A#\{(\h{5,40})\}\z/, 1]
1988
+ val[/\A:(\h{5,40})\z/, 1] || val[/\A#\{(\h{5,40})\}\z/, 1]
1979
1989
  end
1980
1990
 
1981
1991
  def commithead(val)