squared 0.7.9 → 0.7.11

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6c6ff7f1f5a649c84d9fb799e3da482e39b75823629b78fcb5fa476473cbb43
4
- data.tar.gz: 29559d83bf196111dd046905b88f1680287524589deabdc4a80b8fb23ca0de0c
3
+ metadata.gz: b2815cc0583f2f21107460840d1e73cbb74b7013d77c9f1ae65e6b4cd72bfe07
4
+ data.tar.gz: c321afc376c0b219967d6318650143588099f0d734461c470970919bc03cf00b
5
5
  SHA512:
6
- metadata.gz: 1987e693d59692f782d6c6b08c75b385efd384d96be5d73757d6068964408888d61e274dcc9444342d8d12a28cfc8628e186fbc71759e76a9cee9eef197c86ac
7
- data.tar.gz: 3cca9732ab404591931ccad9b48946d15e22cf1fc6d9fd06529a3b8b9216c0e3c9b8fafca79dab97c10e6d8917c656f7c9529182140e48cb47f7cd712e7157c7
6
+ metadata.gz: 76adacb4694911e5b587c0f9871d8adc8cab9770cf9173d82452d58ad142e171b2a284f93f35769869b42a43f374e005daa595a6698537c07b74847a46e99df2
7
+ data.tar.gz: 92f875447ab1969fb3374a686ec5766e0980f6536fa3859737d16207e763f6e68db07ad350419b85d53846e65e3f87aed29546eb182066ab27be9bd09f4f34cc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.11] - 2026-09-04
4
+
5
+ ### Added
6
+
7
+ - Node command package action [approve|deny] is interactive.
8
+
9
+ ### Fixed
10
+
11
+ - Docker container commands [run|exec] did not sanitize input prompt.
12
+
13
+ ## [0.6.18]
14
+
15
+ ### Added
16
+
17
+ - Docker command bake action buildx with first arg "-" inherits base args.
18
+
19
+ ### Fixed
20
+
21
+ - Git action grep did not recognize format and max-count.
22
+ - Node command nvm did not quote executable path.
23
+
24
+ ## [0.5.26]
25
+
26
+ ### Fixed
27
+
28
+ - Node command nvm did not quote executable path.
29
+
30
+ ## [0.4.41]
31
+
32
+ ### Added
33
+
34
+ - Common base argument SUCCESS for command confirmation was created.
35
+
36
+ ### Changed
37
+
38
+ - Program options prefer using quotes instead of escape.
39
+ - Docker container run arguments with a leading dash are concatenated.
40
+
41
+ ### Fixed
42
+
43
+ - Common prompt method choice did not grep list items.
44
+ - OptionPartition static method strip did not parse flags with dashes.
45
+ - Project base method choice_index did not support multiple selections.
46
+
47
+ ## [0.7.10] - 2026-08-06
48
+
49
+ ### Fixed
50
+
51
+ - See `0.6.17`.
52
+
53
+ ## [0.6.17]
54
+
55
+ ### Added
56
+
57
+ - NPM command install can bypass engines with ENV and engine-strict=false.
58
+
59
+ ### Fixed
60
+
61
+ - Ruby command gem action push interactive file selector did nothing.
62
+ - Git choice prompt did not permit optional empty values.
63
+
64
+ ## [0.4.40]
65
+
66
+ ### Changed
67
+
68
+ - Series method exclude? was made compatible with v0.8 signature.
69
+
3
70
  ## [0.7.9] - 2026-07-04
4
71
 
5
72
  ### Added
@@ -1924,6 +1991,8 @@
1924
1991
 
1925
1992
  - Changelog was created.
1926
1993
 
1994
+ [0.7.11]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.11
1995
+ [0.7.10]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.10
1927
1996
  [0.7.9]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.9
1928
1997
  [0.7.8]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.8
1929
1998
  [0.7.7]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.7
@@ -1934,6 +2003,8 @@
1934
2003
  [0.7.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.2
1935
2004
  [0.7.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.1
1936
2005
  [0.7.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.0
2006
+ [0.6.18]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.18
2007
+ [0.6.17]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.17
1937
2008
  [0.6.16]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.16
1938
2009
  [0.6.15]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.15
1939
2010
  [0.6.14]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.14
@@ -1951,6 +2022,7 @@
1951
2022
  [0.6.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.2
1952
2023
  [0.6.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.1
1953
2024
  [0.6.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.0
2025
+ [0.5.26]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.26
1954
2026
  [0.5.25]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.25
1955
2027
  [0.5.24]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.24
1956
2028
  [0.5.23]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.23
@@ -1977,6 +2049,8 @@
1977
2049
  [0.5.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.2-ruby
1978
2050
  [0.5.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.1-ruby
1979
2051
  [0.5.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.0-ruby
2052
+ [0.4.41]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.41
2053
+ [0.4.40]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.40
1980
2054
  [0.4.39]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.39
1981
2055
  [0.4.38]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.38
1982
2056
  [0.4.37]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.37
@@ -15,6 +15,7 @@ module Squared
15
15
  CHOICE: 25,
16
16
  QUOTE: "'",
17
17
  SPACE: ' => ',
18
+ SUCCESS: 'Success',
18
19
  GRAPH: %w[| - | \\ -].freeze,
19
20
  BORDER: %w[| - - - - - | | - -].freeze,
20
21
  VIEW: 'view',
@@ -92,6 +93,13 @@ module Squared
92
93
  VAR[key.is_a?(::String) ? key.to_sym : key] = val
93
94
  end
94
95
 
96
+ def __arg__(key, name = nil, &blk)
97
+ ret = ARG[key.to_sym]
98
+ return ret unless name && ret.is_a?(::Hash)
99
+
100
+ ret[name = name.downcase] || (block_given? ? ret.fetch(name.to_sym, &blk) : ret[name.to_sym])
101
+ end
102
+
95
103
  def __freeze__
96
104
  PATH.freeze
97
105
  ARG.freeze
@@ -56,10 +56,11 @@ module Squared
56
56
  def choice(msg, list = nil, min: 1, max: 1, multiple: false, index: false, grep: nil, border: nil, auto: true,
57
57
  force: true, attempts: 3, timeout: 0)
58
58
  require 'timeout'
59
+ multiple = Array(multiple) if multiple.is_a?(::Numeric)
59
60
  if list
60
61
  grep &&= Array(grep).map { |val| Regexp.new(val) }
61
62
  items = list.each_with_object([]) do |val, out|
62
- next if grep&.none? { |pat| pat.match?(line) }
63
+ next if grep&.none? { |pat| pat.match?(val) }
63
64
 
64
65
  out << val.to_s.chomp
65
66
  puts '%2d. %s' % [out.size, val]
@@ -76,11 +77,11 @@ module Squared
76
77
  puts print_footer(border: border)
77
78
  end
78
79
  msg = "#{msg + (force ? ':' : '?')} [#{min}-#{max}#{if (n = multiple)
79
- "|,#{n.is_a?(::Numeric) ? "{#{n}}" : '*'}"
80
+ "|,#{n.is_a?(::Array) ? "{#{n.join(',')}}" : '*'}"
80
81
  end}] "
81
82
  end
82
83
  end
83
- between = ->(s) { s.match?(/^\d+$/) && s.to_i.between?(min, max) }
84
+ between = ->(s) { s.match?(/^\d+$/) && s.to_i.between?(min, max <= min ? Float::INFINITY : max) }
84
85
  Timeout.timeout(timeout) do
85
86
  while (ch = Readline.readline(msg))
86
87
  ch.strip!
@@ -105,7 +106,7 @@ module Squared
105
106
  unless k.include?(nil)
106
107
  k.uniq!
107
108
  k.sort!
108
- unless multiple.is_a?(::Numeric) && multiple != k.size
109
+ unless multiple.is_a?(::Array) && !multiple.include?(k.size)
109
110
  return index || !items ? k : k.map { |i| items[i.pred] }
110
111
  end
111
112
  end
@@ -140,7 +140,7 @@ module Squared
140
140
  end
141
141
 
142
142
  def env_key(*val)
143
- val.join('_').gsub(/\W+/, '_').upcase
143
+ val.join('_').sub(/\A\d+\W*/, '__').gsub(/\W+/, '_').upcase
144
144
  end
145
145
 
146
146
  def env_bool(key, default = false, index: false, **kwargs)
@@ -332,8 +332,7 @@ module Squared
332
332
  'yaml'
333
333
  when 'js'
334
334
  'json'
335
- when ''
336
- nil
335
+ when '' then nil
337
336
  else
338
337
  ret
339
338
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.7.9'
4
+ VERSION = '0.7.11'
5
5
  end
@@ -1545,7 +1545,7 @@ module Squared
1545
1545
  def env(key, default = nil, suffix: nil, strict: false, ignore: nil, **kwargs, &blk)
1546
1546
  name = "#{key}_#{envname_get}"
1547
1547
  ret = if suffix
1548
- ENV.fetch("#{name}_#{suffix}", '')
1548
+ ENV["#{name}_#{suffix}"] || (ignore == false && ENV["#{key}_#{suffix}"]) || ''
1549
1549
  elsif strict
1550
1550
  ENV[name].to_s
1551
1551
  else
@@ -1703,7 +1703,7 @@ module Squared
1703
1703
  end
1704
1704
 
1705
1705
  def print_success
1706
- puts 'Success'
1706
+ puts ARG[:SUCCESS]
1707
1707
  end
1708
1708
 
1709
1709
  def print_error(*args, loglevel: Logger::WARN, **kwargs)
@@ -1897,7 +1897,7 @@ module Squared
1897
1897
  end
1898
1898
 
1899
1899
  def append_hash(data, target: @session || [], build: false)
1900
- if build && (type = env('BUILD', suffix: 'TYPE') || ENV['BUILD_TYPE'])
1900
+ if build && (type = env('BUILD', suffix: 'TYPE', ignore: false))
1901
1901
  if (extra = data[type = "__#{type}__"] || data[type.to_sym]).is_a?(Hash)
1902
1902
  data = data.merge(extra)
1903
1903
  else
@@ -2086,8 +2086,8 @@ module Squared
2086
2086
  args
2087
2087
  end
2088
2088
 
2089
- def confirm_basic(msg, hint, default = 'Y', style: :inline, target: @session, prefix: nil, **kwargs)
2090
- return true if prefix ? option('y', prefix: prefix) : target && option('y', target: target)
2089
+ def confirm_basic(msg, hint, default = 'Y', style: :inline, target: @session, prefix: target&.first, **kwargs)
2090
+ return true if option('y', prefix: prefix)
2091
2091
 
2092
2092
  confirm("#{msg} [#{sub_style(hint.to_s, style.is_a?(Symbol) ? theme[style] : style)}]", default, **kwargs)
2093
2093
  end
@@ -2121,7 +2121,10 @@ module Squared
2121
2121
  if accept
2122
2122
  hint = Array(ret).map { |val| sub_style(val.to_s, theme[:inline]) }.join(', ')
2123
2123
  accept = Array(accept).map { |val| Array(val) }
2124
- ret = Array(ret) if accept.any? { |val| val[1] == true }
2124
+ if accept.any? { |val| val[1] == true }
2125
+ ret = [ret]
2126
+ multiple = -1
2127
+ end
2125
2128
  begin
2126
2129
  item = accept.shift
2127
2130
  c = confirm("#{item[0]}#{" [#{hint}]" if hint}", item[2] ? 'Y' : 'N')
@@ -2134,7 +2137,7 @@ module Squared
2134
2137
  end until accept.empty?
2135
2138
  end
2136
2139
  if values
2137
- ret = Array(ret)
2140
+ ret = [ret] unless accept && multiple == -1
2138
2141
  Array(values).each do |val|
2139
2142
  if val.is_a?(Array)
2140
2143
  val, force = val
@@ -2700,6 +2703,10 @@ module Squared
2700
2703
  end
2701
2704
  end
2702
2705
 
2706
+ def dryrun?(*, target: @session, prefix: target&.first)
2707
+ Array(target).include?('--dry-run') || !option('dry-run', target: target, prefix: prefix).nil?
2708
+ end
2709
+
2703
2710
  def pwd?
2704
2711
  path == Pathname.pwd
2705
2712
  end
@@ -21,14 +21,14 @@ module Squared
21
21
  }.freeze,
22
22
  compose: {
23
23
  common: %w[all-resources ansi=b compatibility dry-run env-file=p f|file=p parallel=n profile=b progress=b
24
- project-directory=p p|project-name=e].freeze,
24
+ project-directory=p p|project-name=b].freeze,
25
25
  build: %w[check no-cache print pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b
26
26
  provenance=q sbom=q ssh=qq].freeze,
27
27
  create: %w[build force-recreate no-build no-recreate quiet-pull remove-orphans y|yes pull=b scale=i].freeze,
28
- exec: %w[d|detach privileged e|env=qq index=i T|no-TTY=b? user=e w|workdir=q].freeze,
28
+ exec: %w[d|detach privileged e|env=qq index=i T|no-tty=!? user=b w|workdir=q].freeze,
29
29
  run: %w[build d|detach no-deps q|quiet quiet-build quiet-pull remove-orphans rm P|service-ports use-aliases
30
30
  cap-add=b cap-drop=b entrypoint=q e|env=qq env-from-file=p i|interactive=b? l|label=q name=b
31
- T|no-TTY=b? p|publish=q pull=b u|user=e v|volume=q w|workdir=q].freeze,
31
+ T|no-tty=!? p|publish=q pull=b u|user=b v|volume=qq w|workdir=q].freeze,
32
32
  up: %w[abort-on-container-exit abort-on-container-failure always-recreate-deps attach-dependencies build
33
33
  d|detach force-recreate menu no-build no-color no-deps no-log-prefix no-recreate no-start quiet-build
34
34
  quiet-pull remove-orphans V|renew-anon-volumes timestamps wait w|watch y|yes attach=b
@@ -38,9 +38,9 @@ module Squared
38
38
  }.freeze,
39
39
  container: {
40
40
  create: %w[init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet read-only
41
- rm t|tty use-api-socket add-host=q annotation=q a|attach=b blkio-weight=i blkio-weight-device=i
42
- cap-add=b cap-drop=b cgroup-parent=b cgroupns=b cidfile=p device=q device-cgroup-rule=q
43
- device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
41
+ rm t|tty=!? use-api-socket add-host=q annotation=q a|attach=b blkio-weight=i
42
+ blkio-weight-device=i cap-add=b cap-drop=b cgroup-parent=b cgroupns=b cidfile=p device=q
43
+ device-cgroup-rule=q device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
44
44
  dns=q dns-option=q dns-search=q domainname=b entrypoint=q e|env=qq env-file=p expose=q gpus=q
45
45
  group-add=b health-cmd=q health-interval=b health-retries=i health-start-interval=q
46
46
  health-start-period=q health-timeout=q hostname=q io-maxbandwidth=b io-maxiops=b ip=b ip6=q ipc=b
@@ -48,13 +48,14 @@ module Squared
48
48
  m|memory=b memory-reservation=b memory-swap=n memory-swappiness=n mount=qq name=b network=b
49
49
  network-alias=b oom-score-adj=b pid=b pids-limit=n platform=q p|publish=q pull=b restart=b
50
50
  runtime=b security-opt=q shm-size=b stop-signal=b stop-timeout=i storage-opt=q sysctl=q tmpfs=q
51
- ulimit=q u|user=b userns=b uts=b v|volume=q volume-driver=b volumes-from=b w|workdir=q].freeze,
51
+ ulimit=q u|user=b userns=b uts=b v|volume=qq volume-driver=b volumes-from=b w|workdir=q].freeze,
52
52
  run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
53
53
  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
54
54
  cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=n pids-limit=n
55
55
  restart=q].freeze,
56
- exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p u|user=e
56
+ exec: %w[d|detach i|interactive privileged t|tty=!? detach-keys=q e|env=qq env-file=p u|user=b
57
57
  w|workdir=q].freeze,
58
+ rm: %w[f|force l|link v|volumes].freeze,
58
59
  commit: %w[no-pause a|author=q c|change=q m|message=q pause=b?].freeze,
59
60
  inspect: %w[s|size f|format=q].freeze,
60
61
  start: %w[a|attach i|interactive detach-keys=q].freeze,
@@ -405,15 +406,15 @@ module Squared
405
406
  end
406
407
 
407
408
  def buildx(flag, opts = [], tag: nil, context: nil, from: nil)
409
+ bx = OPT_DOCKER[:buildx]
408
410
  if flag == :bake && context&.match?(%r{^["']?(https?|git)://}i)
409
- shared = data[:shared].dup.push('f|file=q')
411
+ shared = bx[:shared].dup.push('f|file=q')
410
412
  shared.delete('f|file=p')
411
413
  end
412
414
  cmd, opts = docker_session('buildx', opts: opts)
413
- data = OPT_DOCKER[:buildx]
414
- op = OptionPartition.new(opts, data[:common], cmd, project: self, strict: strict?)
415
+ op = OptionPartition.new(opts, bx[:common], cmd, project: self, strict: strict?)
415
416
  op.append(flag, quote: false)
416
- .parse(data[flag == :bake ? :bake : :build] + (shared || data[:shared]))
417
+ .parse(bx[flag == :bake ? :bake : :build] + (shared || bx[:shared]))
417
418
  case flag
418
419
  when :build, :context
419
420
  append_tag(tag || option('tag', ignore: false) || self.tag)
@@ -424,6 +425,10 @@ module Squared
424
425
  end
425
426
  when :bake
426
427
  append_file(0, index: 3) unless from || op.arg?('f', 'file') || !anypath?(*COMPOSEFILE)
428
+ if op.first == '-'
429
+ op.shift
430
+ op << @output[4] if @output[4]
431
+ end
427
432
  unless op.empty?
428
433
  if !context && op.size > 1 && exist?(op.last, type: 'd')
429
434
  pat = /\btarget\s+"#{Regexp.escape(op.last)}"/
@@ -454,10 +459,11 @@ module Squared
454
459
  docker_session('compose', command, '--', *service)
455
460
  else
456
461
  cmd, opts = docker_session('compose', opts: opts)
457
- op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self, strict: strict?)
462
+ cp = OPT_DOCKER[:compose]
463
+ op = OptionPartition.new(opts, cp[:common], cmd, project: self, strict: strict?)
458
464
  append_file(filetype, force: flag == :publish) unless op.arg?('f', 'file')
459
465
  op << flag
460
- op.parse(OPT_DOCKER[:compose].fetch(flag, []))
466
+ op.parse(cp.fetch(flag, []))
461
467
  if flag == :publish
462
468
  id ||= option('tag', ignore: false) || op.shift || tagmain
463
469
  registry ||= option('registry') || op.shift || @oci
@@ -488,10 +494,11 @@ module Squared
488
494
 
489
495
  def container(flag, opts = [], id: nil)
490
496
  cmd, opts = docker_session('container', flag, opts: opts)
491
- data = OPT_DOCKER[:container]
492
- list = data.fetch(flag, [])
493
- list += data[:create] if (rc = flag == :run)
494
- list += data[:update] if rc ||= flag == :create
497
+ rc = flag == :run
498
+ ct = OPT_DOCKER[:container]
499
+ list = ct.fetch(flag, [])
500
+ list += ct[:create] if rc
501
+ list += ct[:update] if rc ||= flag == :create
495
502
  op = OptionPartition.new(opts, list, cmd, project: self, strict: strict?, args: rc || flag == :exec)
496
503
  from = symjoin 'container', flag
497
504
  case flag
@@ -645,7 +652,7 @@ module Squared
645
652
  raise_error ArgumentError, 'username/registry not specified', hint: flag unless registry
646
653
  uri = shell_quote tagjoin(registry, id)
647
654
  op << uri
648
- img = docker_output 'image', 'tag', id, uri
655
+ img = docker_output 'image tag', id, uri
649
656
  return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
650
657
 
651
658
  @session = img
@@ -736,12 +743,12 @@ module Squared
736
743
  session('docker', *cmd, main: false, options: false, **kwargs)
737
744
  end
738
745
 
739
- def append_command(flag, val, list, target: @session, prompt: ':')
740
- list << if list.delete(prompt)
741
- readline('Enter command [args]', force: flag == :exec)
742
- else
743
- env('DOCKER_ARGS')
744
- end
746
+ def append_command(flag, id, list, target: @session, prompt: ':')
747
+ if list.delete(prompt)
748
+ list << readline('Enter command [args]', force: flag == :exec)
749
+ elsif (val = env('DOCKER_ARGS'))
750
+ list << val
751
+ end
745
752
  case flag
746
753
  when :run
747
754
  unless session_arg?('name', target: target)
@@ -750,8 +757,22 @@ module Squared
750
757
  when :exec
751
758
  raise_error ArgumentError, 'nothing to execute', hint: flag if list.empty?
752
759
  end
753
- target << val << list.shift
754
- target << list.join(' && ') unless list.empty?
760
+ target << id
761
+ case list.first
762
+ when '--'
763
+ target.merge(list.drop(1))
764
+ when /\A(--|&&)(\s|\z)/
765
+ target.merge(list)
766
+ else
767
+ target << list.shift
768
+ unless list.empty?
769
+ if list.first.start_with?('-')
770
+ target.merge(list)
771
+ else
772
+ target << list.join(' && ')
773
+ end
774
+ end
775
+ end
755
776
  end
756
777
 
757
778
  def append_file(type, target: @session, index: 2, force: false)
@@ -822,7 +843,7 @@ module Squared
822
843
  pwd_set(from: from) do
823
844
  index = 1
824
845
  all = option('all', prefix: 'docker')
825
- y = from == :'image:rm' && option('y', prefix: 'docker')
846
+ y = from.to_s.end_with?(':rm') && option('y', prefix: 'docker')
826
847
  filter = env('DOCKER_FILTER', filter).to_s
827
848
  pat = if OptionPartition.pattern?(filter)
828
849
  Regexp.new(filter)
@@ -963,7 +984,7 @@ module Squared
963
984
  end
964
985
  cmd.merge(Array(out).map { |val| val.split(/\s+/, 2).first })
965
986
  cmd << args
966
- success?(run(cmd), ctx.start_with?('network', 'tag', 'save'))
987
+ success?(run(cmd, { 'NO_COLOR' => 'true' }), ctx.start_with?('network', 'tag', 'save'))
967
988
  end
968
989
 
969
990
  def filetype(val = dockerfile)
@@ -141,7 +141,6 @@ module Squared
141
141
  end
142
142
  File.write(@revfile, JSON.pretty_generate(@revdoc))
143
143
  rescue => e
144
- log&.debug e
145
144
  warn log_warn(e, pass: true) if warning
146
145
  ensure
147
146
  @revlock = false
@@ -170,9 +169,10 @@ module Squared
170
169
  fetch: {
171
170
  base: %w[multiple porcelain progress P|prune-tags refetch stdin u|update-head-ok
172
171
  recurse-submodules-default=b?].freeze,
173
- pull: %w[4 6 n t a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet set-upstream
174
- unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q recurse-submodules=v
175
- refmap=q o|server-option=q shallow-exclude=b shallow-since=v upload-pack=q].freeze
172
+ pull: %w[n t 4|ipv4 6|ipv6 a|append atomic dry-run f|force k|keep negotiate-only prefetch p|prune q|quiet
173
+ set-upstream unshallow update-shallow v|verbose deepen=i depth=i j|jobs=i negotiation-tip=q
174
+ recurse-submodules=v refmap=q o|server-option=q shallow-exclude=b shallow-since=v
175
+ upload-pack=q].freeze
176
176
  }.freeze,
177
177
  git: {
178
178
  add: %w[N|intent-to-add].freeze,
@@ -212,7 +212,7 @@ module Squared
212
212
  graph ita-invisible-in-index minimal name-only name-status no-color-moved-ws no-prefix no-renames
213
213
  numstat patch-with-raw patch-with-stat patience pickaxe-all pickaxe-regex raw shortstat summary
214
214
  a|text abbrev=i? anchored=q break-rewrites=b? color=b color-moved=b color-moved-ws=b color-words=q?
215
- diff-algorithm=b diff-filter=e? X|dirstat=b? dirstat-by-file=b? dst-prefix=q find-copies=i?
215
+ diff-algorithm=b diff-filter=q? X|dirstat=b? dirstat-by-file=b? dst-prefix=q find-copies=i?
216
216
  find-object=b find-renames=b? ignore-matching-lines=q ignore-submodules=b? line-prefix=q
217
217
  max-depth=n output=p output-indicator-context=q output-indicator-new=q output-indicator-old=q
218
218
  relative=p rotate-to=p skip-to=p src-prefix=q stat=b? stat-count=i stat-width=i stat-name-width=i
@@ -277,7 +277,7 @@ module Squared
277
277
  checkout: %w[overwrite-ignore guess overlay progress recurse-submodules track].freeze,
278
278
  fetch: {
279
279
  base: %w[auto-gc auto-maintenance write-commit-graph write-fetch-head].freeze,
280
- pull: %w[all ipv4 ipv6 recurse-submodules show-forced-updates tags].freeze
280
+ pull: %w[all recurse-submodules show-forced-updates tags].freeze
281
281
  },
282
282
  grep: %w[color exclude-standard recursive textconv].freeze,
283
283
  log: {
@@ -310,7 +310,7 @@ module Squared
310
310
  }.freeze,
311
311
  rebase: {
312
312
  send: %w[continue skip abort quit].freeze,
313
- value: %w[true false merges interactive].freeze
313
+ value: %w[false true merges interactive].freeze
314
314
  }.freeze,
315
315
  reset: %w[soft mixed hard merge keep recurse-submodules no-recurse-submodules].freeze,
316
316
  revbuild: %w[untracked-files ignore-submodules ignored].freeze
@@ -601,10 +601,10 @@ module Squared
601
601
  when 'i', 'E', 'F', 'P'
602
602
  opts << last
603
603
  else
604
- if last =~ /^(f(-ormat)?)=(.+)$/
604
+ if last =~ /^f(?:-ormat)?=(.+)$/
605
605
  opts.shift
606
606
  opts << "format=#{$1}"
607
- elsif last =~ /^(max(-count)?)=(\d+)$/
607
+ elsif last =~ /^max(?:-count)?=(\d+)$/
608
608
  opts << "max-count=#{$1}"
609
609
  else
610
610
  grep << last
@@ -1201,7 +1201,11 @@ module Squared
1201
1201
  first: (matchpathspec if flag == :push))
1202
1202
  case flag
1203
1203
  when :push
1204
- op.readline('Enter message', option: 'message', quote: true) if op.remove(':')
1204
+ if op.remove(':')
1205
+ op.readline('Enter message', option: 'message', quote: true)
1206
+ else
1207
+ append_message
1208
+ end
1205
1209
  append_pathspec op.extras
1206
1210
  when :pop, :apply, :drop, :branch
1207
1211
  if op.remove(':')
@@ -1886,7 +1890,7 @@ module Squared
1886
1890
  op.add_quote(remote) if remote
1887
1891
  out, banner, from = source(io: true)
1888
1892
  print_item banner
1889
- ret = write_lines(out, grep: op.extras, prefix: "refs/#{flag}/")
1893
+ ret = write_lines(out, grep: op.extras)
1890
1894
  list_result(ret, flag.to_s, grep: op.extras, banner: banner, from: from)
1891
1895
  end
1892
1896
 
@@ -2101,7 +2105,7 @@ module Squared
2101
2105
  ret = choice_index('Choose a commit', git_spawn(cmd, stdout: false), column: /^(\S+)/, force: force, **kwargs)
2102
2106
  case ret
2103
2107
  when Array
2104
- ret.map(&:stripstyle)
2108
+ ret.map { |val| val&.stripstyle }
2105
2109
  when String
2106
2110
  ret.stripstyle
2107
2111
  else
@@ -2186,9 +2190,9 @@ module Squared
2186
2190
  end
2187
2191
  end
2188
2192
 
2189
- def append_pathspec(files = [], target: @session, expect: false, **kwargs)
2193
+ def append_pathspec(files = [], target: @session, expect: false, strict: strict?, **kwargs)
2190
2194
  if session_arg?('pathspec-from-file', target: target)
2191
- OptionPartition.clear(target, files, styles: theme[:inline])
2195
+ OptionPartition.clear(target, files, strict: strict, styles: theme[:inline])
2192
2196
  true
2193
2197
  else
2194
2198
  option('pathspec', target: target) { |val| files = split_escape(val) } if files.empty?
@@ -2279,10 +2283,6 @@ module Squared
2279
2283
  stdout: stdout, banner: banner, **kwargs)
2280
2284
  end
2281
2285
 
2282
- def dryrun?(*, target: @session, prefix: target&.first)
2283
- Array(target).include?('--dry-run') || !option('dry-run', target: target, prefix: prefix).nil?
2284
- end
2285
-
2286
2286
  def quiet?(*, target: @session, **)
2287
2287
  return silent? unless target
2288
2288
 
@@ -8,8 +8,8 @@ module Squared
8
8
  common: %w[dry-run=!? force=!? loglevel=b include-workspace-root=!? workspaces=!? w|workspace=v].freeze,
9
9
  common_scripts: %w[dangerously-allow-all-scripts=!? strict-allow-scripts=!? allow-scripts=q].freeze,
10
10
  install: %w[package-lock-only=!? prefer-dedupe=!? E|save-exact=!? before=q cpu=b libc=b os=b].freeze,
11
- install_a: %w[audit=! bin-links=! foreground-scripts=!? fund=! ignore-scripts=!? install-links=!?
12
- package-lock=! strict-peer-deps=!? include=b install-strategy=b omit=b].freeze,
11
+ install_a: %w[audit=! bin-links=! engine-strict=!? foreground-scripts=!? fund=! ignore-scripts=!?
12
+ install-links=!? package-lock=! strict-peer-deps=!? include=b install-strategy=b omit=b].freeze,
13
13
  install_b: %w[no-save B|save-bundle D|save-dev O|save-optional save-peer P|save-prod g|global=!?
14
14
  S|save=!?].freeze,
15
15
  install_c: %w[allow-directory=b? allow-file=b? allow-git=b? allow-remote=b?].freeze,
@@ -18,6 +18,7 @@ module Squared
18
18
  pack: %w[ignore-scripts=!? json=!? pack-destination=p].freeze,
19
19
  rebuild: %w[bin-links=! foreground-scripts=!? global=!? ignore-scripts=!? install-links=!?].freeze,
20
20
  'approve-scripts': %w[a|all allow-scripts-pending allow-scripts-pin=!? no-allow-scripts-pin json].freeze,
21
+ 'deny-scripts': %w[a|all json].freeze,
21
22
  no: {
22
23
  install: %w[audit bin-links fund package-lock].freeze
23
24
  }.freeze
@@ -285,7 +286,7 @@ module Squared
285
286
 
286
287
  format_desc action, nil, 'version?,args*'
287
288
  task action, [:version] do |_, args|
288
- path = ->(s) { File.join(ENV['NVM_DIR'], s) }
289
+ path = ->(s) { shell_quote(File.join(ENV['NVM_DIR'], s)) }
289
290
  unless (version = args.version)
290
291
  cmd = ". #{path.call('nvm.sh')} && nvm list --no-colors --no-alias"
291
292
  version = pwd_set(from: :nvm) do
@@ -313,13 +314,41 @@ module Squared
313
314
  outdated flag, args.to_a
314
315
  end
315
316
  when 'package'
316
- format_desc(action, flag, 'opts*', before: case flag
317
- when :dedupe, :rebuild then nil
318
- when :reinstall then 'f/orce?'
319
- else 'name*'
320
- end)
321
- task flag do |_, args|
322
- package flag, args.to_a
317
+ case flag
318
+ when :approve, :deny
319
+ format_desc action, flag, 'name*|:,opts*'
320
+ task flag do |_, args|
321
+ args = args.to_a
322
+ if args.first == ':'
323
+ args.shift
324
+ pwd_set do
325
+ pad = 0
326
+ list = IO.popen(npm_output('approve-scripts --allow-scripts-pending').to_s)
327
+ .each_with_object([]) do |line, out|
328
+ next unless line =~ /^\s+([a-z\d@][^@]*)@(\S+)/
329
+
330
+ pad = [pad, $1.size].max
331
+ out << [$1, $2]
332
+ end
333
+ .map { |name, ver| name.ljust(pad + 4) + ver }
334
+ if list.empty?
335
+ puts log_message('no packages to review', subject: name, hint: "#{flag}-scripts")
336
+ exit 0
337
+ end
338
+ args.concat(choice_index('Select packages', list, multiple: true, column: /^(\S+)/))
339
+ end
340
+ end
341
+ package flag, args
342
+ end
343
+ else
344
+ format_desc(action, flag, 'opts*', before: case flag
345
+ when :dedupe, :rebuild then nil
346
+ when :reinstall then 'f/orce?'
347
+ else 'name*'
348
+ end)
349
+ task flag do |_, args|
350
+ package flag, args.to_a
351
+ end
323
352
  end
324
353
  when 'bump'
325
354
  break unless version
@@ -627,8 +656,7 @@ module Squared
627
656
  cmd << '--ignore-scripts' if option('ignore-scripts')
628
657
  cmd << '--dangerously-allow-all-builds' if option('approve-builds')
629
658
  else
630
- cmd = session 'npm'
631
- cmd << (ci = option('ci') ? 'ci' : 'install')
659
+ cmd = session('npm', (ci = option('ci', prefix: 'npm')) ? 'ci' : 'install')
632
660
  option('approve-scripts') do |val|
633
661
  cmd = npm_output 'approve-scripts'
634
662
  cmd << case val
@@ -654,6 +682,14 @@ module Squared
654
682
  quote_option 'allow-scripts', val
655
683
  end
656
684
  end
685
+ option('engine-strict', ignore: false) do |val|
686
+ cmd << case val
687
+ when '0', 'false'
688
+ '--engine-strict=false'
689
+ else
690
+ '--engine-strict'
691
+ end
692
+ end
657
693
  cmd << '--workspaces=false' if ws
658
694
  cmd << '--force' if option('force')
659
695
  append_nocolor
@@ -1463,8 +1499,7 @@ module Squared
1463
1499
  next if yarntype(exist: true) == 0
1464
1500
  when /^pnpm/
1465
1501
  next if pnpmtype(exist: true) == 0
1466
- when /^npm/
1467
- nil
1502
+ when /^npm/ then nil
1468
1503
  else
1469
1504
  next
1470
1505
  end
@@ -1518,8 +1553,8 @@ module Squared
1518
1553
  end
1519
1554
 
1520
1555
  def remove_modules(prefix = dependbin)
1521
- modules = basepath 'node_modules'
1522
- return false unless modules.directory? && confirm_basic('Remove?', modules, prefix: prefix)
1556
+ modules = basepath!('node_modules', type: 'd')
1557
+ return false unless modules && confirm_basic('Remove?', modules, prefix: prefix)
1523
1558
 
1524
1559
  modules.rmtree
1525
1560
  rescue Timeout::Error => e
@@ -36,9 +36,9 @@ module Squared
36
36
  root-user-action=b t|target=p upgrade-strategy=b].freeze,
37
37
  install_a: %w[no-index pre prefer-binary all-releases=b extra-index-url=q f|find-links=q i|index-url=q
38
38
  no-binary=q only-binary=q only-final=b].freeze,
39
- install_b: %w[build-constraint check-build-dependencies no-build-isolation no-clean no-deps require-hashes
40
- use-pep517 c|constraint=p group=q progress-bar=b r|requirement=p requirements-from-script=p
41
- src=p].freeze,
39
+ install_b: %w[check-build-dependencies no-build-isolation no-clean no-deps require-hashes use-pep517
40
+ build-constraint=p c|constraint=p group=q progress-bar=b r|requirement=p
41
+ requirements-from-script=p src=p].freeze,
42
42
  install_c: %w[C|config-settings=q e|editable=v].freeze,
43
43
  install_d: %w[ignore-requires-python uploaded-prior-to=q].freeze,
44
44
  hash: %w[a|algorithm].freeze,
@@ -181,8 +181,8 @@ module Squared
181
181
  format_desc action, nil, "script+|#{indexchar}index+|#,pattern*"
182
182
  task action, [:command] do |_, args|
183
183
  found = 0
184
- %w[tool.poetry.scripts tool.pdm.scripts project.scripts].each_with_index do |table, i|
185
- next if (list = read_pyproject(table)).empty?
184
+ %w[tool.poetry tool.pdm project].each_with_index do |table, i|
185
+ next if (list = read_pyproject("#{table}.scripts")).empty?
186
186
 
187
187
  if args.command == '#'
188
188
  format_list(list, "run[#{indexchar}N]", 'scripts', grep: args.extras, from: pyprojectfile)
@@ -208,7 +208,7 @@ module Squared
208
208
  log.warn "run script #{n} of #{list.size}".subhint('out of range')
209
209
  end
210
210
  else
211
- case i
211
+ case (i > 1 && poetry? ? 0 : i)
212
212
  when 0
213
213
  found |= 1
214
214
  run(session_output('poetry', 'run', val), from: :run)
@@ -297,7 +297,7 @@ module Squared
297
297
  install flag, ['upgrade', *args.to_a, 'pip']
298
298
  end
299
299
  when :freeze
300
- format_desc action, flag, "file?=#{DEP_PYTHON[4]},u/uninstall,opts*"
300
+ format_desc action, flag, "file?=#{DEP_PYTHON[4]},u/ninstall?,opts*"
301
301
  task flag do |_, args|
302
302
  opts = args.to_a
303
303
  if has_value!(opts, 'u', 'uninstall')
@@ -312,7 +312,7 @@ module Squared
312
312
  end
313
313
  file = pip(flag, opts: opts, banner: requirement.nil?, requirement: requirement)
314
314
  puts File.read(file) unless silent?
315
- if requirement && confirm_basic('Uninstall?', file)
315
+ if requirement && confirm_basic('Uninstall?', file, prefix: 'pip')
316
316
  pip(:uninstall, opts: ['y', "r=#{shell_quote(file)}"])
317
317
  end
318
318
  end
@@ -482,7 +482,7 @@ module Squared
482
482
  :patch
483
483
  end
484
484
  end
485
- '--not-required' if option('not-required', notequals: '0') && !env('PYTHON_DEFAULT')
485
+ '--not-required' if option('not-required', notequals: '0')
486
486
  end
487
487
  cmd << '--local' if option('l', 'local')
488
488
  append_global
@@ -607,17 +607,17 @@ module Squared
607
607
  project: self, strict: strict?, single: singleopt(flag))
608
608
  else
609
609
  op = append_pip(flag, opts, pipopts(:install), target: pip_session('install'))
610
- end
611
- case flag
612
- when :editable
613
- op << quote_option('e', op.pop || editable || '.')
614
- op.clear
615
- when :user, :upgrade
616
- op.concat(packages)
617
- raise_error ArgumentError, 'no packages listed', hint: flag if op.empty?
618
- op << "--#{flag}"
619
- op.append
620
- python_session('-m pip', *op.to_a.drop(1)) if workspace.windows?
610
+ case flag
611
+ when :editable
612
+ op << quote_option('e', op.pop || editable || '.')
613
+ op.clear
614
+ when :user, :upgrade
615
+ op.concat(packages)
616
+ raise_error ArgumentError, 'no packages listed', hint: flag if op.empty?
617
+ op << "--#{flag}"
618
+ op.append
619
+ python_session('-m pip', *op.to_a.drop(1)) if workspace.windows?
620
+ end
621
621
  end
622
622
  run(banner: banner, from: :install)
623
623
  end
@@ -726,7 +726,7 @@ module Squared
726
726
  op.clear(pass: false)
727
727
  else
728
728
  if op.empty?
729
- op << "#{dist.call}/*"
729
+ op.add_path(dist.call, '*')
730
730
  else
731
731
  op.add_path
732
732
  end
@@ -798,8 +798,7 @@ module Squared
798
798
  raise_error 'no subcommand', hint: flag if op.empty?
799
799
  op << (action = op.shift)
800
800
  case action
801
- when 'dir', 'info', 'purge'
802
- nil
801
+ when 'dir', 'info', 'purge' then nil
803
802
  when 'list', 'remove'
804
803
  op.add_first(quote: true)
805
804
  else
@@ -811,8 +810,7 @@ module Squared
811
810
  raise_error 'no subcommand', hint: flag if op.empty?
812
811
  op << (action = op.shift)
813
812
  case action
814
- when 'list', 'edit', 'debug'
815
- nil
813
+ when 'list', 'edit', 'debug' then nil
816
814
  when 'get', 'unset', 'set'
817
815
  op.add_first
818
816
  op.add_first(quote: true, expect: true) if action == 'set'
@@ -1091,8 +1089,7 @@ module Squared
1091
1089
  /\A(?:v+|q+|b+|V+|O+)\z/
1092
1090
  when :pdm, :poetry
1093
1091
  /\Av+\z/
1094
- when :twine, :meson
1095
- nil
1092
+ when :twine, :meson then nil
1096
1093
  else
1097
1094
  /\A(?:v+|q+)\z/
1098
1095
  end
@@ -510,8 +510,7 @@ module Squared
510
510
  opts << 'redownload' if has_value!(opts, 'f', 'force')
511
511
  if (lock = basepath!('Gemfile.lock'))
512
512
  case (val = ENV['BUNDLE_FROZEN'])
513
- when '0', 'false'
514
- nil
513
+ when '0', 'false' then nil
515
514
  else
516
515
  if val
517
516
  print_error("BUNDLE_FROZEN: #{val}", loglevel: Logger::INFO, subject: name, hint: flag)
@@ -782,8 +781,7 @@ module Squared
782
781
  next if val == '0' || val == 'false'
783
782
 
784
783
  run(bundle_output('binstubs --all', case val
785
- when '1', 'true'
786
- nil
784
+ when '1', 'true' then nil
787
785
  else
788
786
  if val.start_with?('~')
789
787
  val = File.join(Dir.home, val == '~' ? '.bundle' : val[1..-1])
@@ -1074,6 +1072,7 @@ module Squared
1074
1072
  buffer = []
1075
1073
  filter = kwargs.fetch(:filter, {})
1076
1074
  semver = filter[:semver]
1075
+ dryrun = filter[:dryrun] || dryrun?(prefix: 'gem')
1077
1076
  update = if sync && filter[:select]
1078
1077
  semver ||= 'major'
1079
1078
  items = []
@@ -1226,7 +1225,7 @@ module Squared
1226
1225
  'user-install'
1227
1226
  end
1228
1227
  end
1229
- if filter[:dryrun]
1228
+ if dryrun
1230
1229
  print_run gem_output('update -f', *update.quote!(escape: true)), false
1231
1230
  else
1232
1231
  gem(:update, *update, opts: opts)
@@ -1268,8 +1267,8 @@ module Squared
1268
1267
  .clear(pass: false)
1269
1268
  end
1270
1269
  when :push
1271
- if op.empty? || (n = op.index(':'))
1272
- file = basepath(if !n && (spec = gemspec)
1270
+ if op.empty? || ia
1271
+ file = basepath(if !ia && (spec = gemspec)
1273
1272
  "#{spec.name}-#{spec.version}.gem"
1274
1273
  else
1275
1274
  choice_index 'Select a file', Dir.glob('*.gem', base: path)
@@ -1922,8 +1921,7 @@ module Squared
1922
1921
  val = case (val = $2)
1923
1922
  when 'true'
1924
1923
  true
1925
- when '', '[]'
1926
- nil
1924
+ when '', '[]' then nil
1927
1925
  else
1928
1926
  if val =~ /\A\[:(.+)\]\z/
1929
1927
  $1.split(', :').map { |s| (s[/\A"(.+)"\z/, 1] || s).to_sym }
@@ -87,8 +87,25 @@ module Squared
87
87
 
88
88
  def strip(val)
89
89
  val = shell_split val if val.is_a?(String)
90
- Array(val).map { |s| s.sub(OPT_SINGLE, '\1=\2').sub(OPT_VALUE, '\1=\2').sub(OPT_NAME, '\2') }
91
- .reject(&:empty?)
90
+ ret = []
91
+ pass = -1
92
+ (args = Array(val)).each_with_index do |s, i|
93
+ next if s.empty? || pass == i
94
+
95
+ if s == '--'
96
+ ret << s << args[i.succ..-1].join(' ')
97
+ break
98
+ end
99
+ next ret << s unless s.start_with?('-')
100
+
101
+ if !s.include?('=') && (j = args[i.succ]) && !j.start_with?('-')
102
+ ret << "#{s.sub(/^-{1,2}/, '')}=#{j}"
103
+ pass = i.succ
104
+ else
105
+ ret << s.sub(OPT_SINGLE, '\1=\2').sub(OPT_VALUE, '\1=\2').sub(OPT_NAME, '\2')
106
+ end
107
+ end
108
+ ret
92
109
  end
93
110
 
94
111
  def select(list, bare: true, no: true, single: false, double: false)
@@ -152,8 +169,11 @@ module Squared
152
169
  end
153
170
 
154
171
  def pattern?(val)
172
+ Regexp.new(val)
155
173
  val.start_with?('\A', '^') || val.end_with?('\z', '$') ||
156
174
  val.match?(/[.)][*+?]|\(\?[:=!><]|\\[dsw\d]|\[.+\]|\{\d+,?\d*\}/i)
175
+ rescue RegexpError
176
+ false
157
177
  end
158
178
 
159
179
  private
@@ -251,13 +271,13 @@ module Squared
251
271
  else
252
272
  [flag]
253
273
  end
274
+ bare.concat(mod) if val.chomp!('?')
275
+ m.concat(mod) if val.chomp!('m')
254
276
  case val[n.succ]
255
277
  when 'e'
256
278
  e.concat(mod)
257
279
  when 'b'
258
280
  b.concat(mod)
259
- when 'm'
260
- m.concat(mod)
261
281
  when 'q'
262
282
  qq.concat(mod) if val[n + 2] == 'q'
263
283
  q.concat(mod)
@@ -276,11 +296,7 @@ module Squared
276
296
  when '+'
277
297
  ml.concat(mod)
278
298
  bare.concat(mod)
279
- else
280
- next
281
299
  end
282
- m.concat(mod) if val[n + 2] == 'm'
283
- bare.concat(mod) if val.end_with?('?')
284
300
  else
285
301
  bare << val
286
302
  mod = [val]
@@ -315,7 +331,7 @@ module Squared
315
331
  end
316
332
  numtype = [
317
333
  [i, /\A\d+\z/],
318
- [f, /\A\d*(?:\.\d+)?\z/],
334
+ [f, /\A(\d+(\.\d*)?|\d*\.\d+)\z/],
319
335
  [si, /\A-?\d+\z/]
320
336
  ].freeze
321
337
  numcheck = ->(k, v) { numtype.any? { |flag, pat| flag.include?(k) && v.match?(pat) } }
@@ -360,8 +376,8 @@ module Squared
360
376
  elsif has.call(q)
361
377
  add quote_option(key, val, double: has.call(qq), **kwargs)
362
378
  elsif has.call(p)
363
- if val.match?(/\A(["']).+\1\z/)
364
- add shell_option(key, val, escape: false, **kwargs)
379
+ if val =~ /\A(["']).+\1\z/
380
+ add shell_option(key, val, double: $1 == '"', escape: false, **kwargs)
365
381
  elsif path
366
382
  add quote_option(key, path + val, **kwargs)
367
383
  else
@@ -184,8 +184,7 @@ module Squared
184
184
  end
185
185
  env('REPO_GROUPS', **envargs) do |val|
186
186
  g = case val
187
- when '0', 'false'
188
- nil
187
+ when '0', 'false' then nil
189
188
  else
190
189
  val
191
190
  end
@@ -209,8 +209,9 @@ module Squared
209
209
  already_invoked? parallel, val
210
210
  end
211
211
 
212
- def exclude?(key, empty = false)
213
- @exclude.include?(key) || (empty && (!key?(key) || self[key].empty?))
212
+ def exclude?(key, fallback = false, empty: nil)
213
+ fallback = empty unless empty.nil?
214
+ @exclude.include?(key) || (fallback && (!key?(key) || self[key].empty?))
214
215
  end
215
216
 
216
217
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.9
4
+ version: 0.7.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
@@ -125,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
127
  requirements: []
128
- rubygems_version: 4.0.10
128
+ rubygems_version: 4.0.19
129
129
  specification_version: 4
130
130
  summary: Rake task generator for managing multi-language workspaces.
131
131
  test_files: []