squared 0.8.5 → 0.8.7

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: e486fab218bc18e8b6e3921184c1d4a9aeaf155e2d19f70b62062b279606714d
4
- data.tar.gz: c5d1cf6cabf110cce2befee1fc6d34dcb9f437d5c84b6dc552bd304b3f7af304
3
+ metadata.gz: 671df38fde0cf8e19a5edf52d76aedba0d3eb294e7eaeed1bc32e63ca50b5e37
4
+ data.tar.gz: 1f87dfbe1fb9881d0d5a2ae023a4e4f14e353bd30368afcc0b45e6cd7e5c3beb
5
5
  SHA512:
6
- metadata.gz: ba4f0d602d818ad424fc9fc940954f1c7a8d4fc138fd98e8ebb6f4b7d98baa45188a3f0476f4a7de0512c60e06d74f965da7f4a54a34cdcda76d7baec525e2d1
7
- data.tar.gz: 52f518b0aaeb168633399c34f62cf19cb14fc1ed463047cf795b527e82d9439e5c2266c3d0f0709777f6bdafdf40cd88f76bbb118e1236f4b8e8ca9a81741a16
6
+ metadata.gz: ea1eda6ca2addd9593a414c453eaa7450e31309f91ce56077732e0bc24297d23901a177ec8fd3740ccbdf92e19ec0c4473720e344508ba365fb44a745deda65f
7
+ data.tar.gz: bf9ac431a644fa2fd532bf33aff3bba1a1b9a52a0e8e2ec7b724df2bcfec41760aa00378034a099e0da60a47b21778581bae5a31700c9a5c9d7e0a816d715a6d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.7] - 2026-10-22
4
+
5
+ ### Added
6
+
7
+ - OptionPartition can be initialized with enum and pattern options.
8
+
9
+ ### Fixed
10
+
11
+ - Python venv creation did not run post-install for package managers.
12
+
13
+ ## [0.7.14]
14
+
15
+ ### Changed
16
+
17
+ - Python program meson prefix options use local path resolution.
18
+ - Bundle exec wrapper can be configured per application.
19
+
20
+ ### Fixed
21
+
22
+ - Bundle exec wrapper did not accept boolean values.
23
+
24
+ ## [0.6.21]
25
+
26
+ ### Fixed
27
+
28
+ - RuboCop did not resolve config to project directory.
29
+ - Python program did not use correct separator for double dash options.
30
+
31
+ ## [0.8.6] - 2026-10-01
32
+
33
+ ### Added
34
+
35
+ - Pip command options were updated to 26.2.
36
+ - Application method assign for appending project instance methods was created.
37
+
38
+ ### Fixed
39
+
40
+ - Bundle exec wrapper did not accept boolean values.
41
+ - RuboCop did not resolve config to project directory.
42
+
43
+ ## [0.7.13] - 2026-09-25
44
+
45
+ ### Fixed
46
+
47
+ - OptionPartition did not parse quoted option values.
48
+
49
+ ## [0.6.20]
50
+
51
+ ### Added
52
+
53
+ - Prompt choice optional selection can be bypassed with a single dash.
54
+ - OptionPartition can parse supplementary option flags for plain output.
55
+
56
+ ### Fixed
57
+
58
+ - Git interactive commit selection did not accept additional values.
59
+
3
60
  ## [0.8.5] - 2026-09-23
4
61
 
5
62
  ### Added
@@ -2124,12 +2181,16 @@
2124
2181
 
2125
2182
  - Changelog was created.
2126
2183
 
2184
+ [0.8.7]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.7
2185
+ [0.8.6]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.6
2127
2186
  [0.8.5]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.5
2128
2187
  [0.8.4]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.4
2129
2188
  [0.8.3]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.3
2130
2189
  [0.8.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.2
2131
2190
  [0.8.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.1
2132
2191
  [0.8.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.0
2192
+ [0.7.14]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.14
2193
+ [0.7.13]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.13
2133
2194
  [0.7.12]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.12
2134
2195
  [0.7.11]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.11
2135
2196
  [0.7.10]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.10
@@ -2143,6 +2204,8 @@
2143
2204
  [0.7.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.2
2144
2205
  [0.7.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.1
2145
2206
  [0.7.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.0
2207
+ [0.6.21]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.21
2208
+ [0.6.20]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.20
2146
2209
  [0.6.19]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.19
2147
2210
  [0.6.18]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.18
2148
2211
  [0.6.17]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.17
data/README.md CHANGED
@@ -96,19 +96,28 @@ Workspace::Application
96
96
  apply :clean, ["build/sqd/"] # variable_set (alias)
97
97
  end
98
98
  .with(:docker, only: ["build", "compose"], hide: [:windows?]) do # When true hide entire group (formerly pass 0.6.0)
99
- .add("squared", "docker", file: "Dockerfile", context: ".", tag: "latest", registry: "localhost:5000", username: "squared", # compose publish => { oci: "oci://docker.io" }
100
- args: "--ssh=default",
101
- client: { config: '/home/user/.docker', tls: true }, # global options
102
- secrets: ["id=github,env=GITHUB_TOKEN"],
103
- mounts: ["src=.,dst=/project,ro,bind-propagation=rshared"]) do # Docker
104
- series(:clean) do # run | depend | doc | lint | test | copy | clean
99
+ add("squared", "docker", file: "Dockerfile", context: ".", tag: "latest", registry: "localhost:5000", username: "squared", # compose publish => { oci: "oci://docker.io" }
100
+ args: "--ssh=default",
101
+ client: { config: '/home/user/.docker', tls: true }, # global options
102
+ secrets: ["id=github,env=GITHUB_TOKEN"],
103
+ mounts: ["src=.,dst=/project,ro,bind-propagation=rshared"]) do # Docker
104
+ series(:clean) do # run | depend | doc | lint | test | copy | clean
105
105
  File.read(basepath("docker-bake.hcl"))
106
106
  .scan(/\btags\s+=\s+\["([^"]+)"\]/)
107
107
  .each { |val| image(:rm, tag: val.first) }
108
108
  end
109
109
  end
110
110
  end
111
- .pass("pull", group: "default") { test? || doc? } # rake pathname:pull | rake optparse:pull
111
+ .assign(:ruby) do # Add or override instance methods
112
+ def vmname(bin: false)
113
+ bin ? ['rbenv', '/usr/bin/rbenv'] : 'rbenv'
114
+ end
115
+
116
+ def copy?
117
+ false
118
+ end
119
+ end
120
+ .pass("pull", group: "default") { test? || doc? } # rake pathname:pull optparse:pull
112
121
  .style("banner", 255.255) # 256 colors (fg | fg.bg | -0.bg)
113
122
  .build(default: "build", parallel: ["pull", "fetch", "rebase", "archive", "clean", /^outdated:/], pass: ["publish"]) do |workspace|
114
123
  workspace
@@ -91,7 +91,9 @@ module Squared
91
91
  while (ch = Prompt.reline.readline(msg))
92
92
  ch.strip!
93
93
  unless ch.empty?
94
- if multiple
94
+ if ch == '-' && !force
95
+ break
96
+ elsif multiple
95
97
  k = if ch == '*'
96
98
  (min..max).to_a
97
99
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.8.5'
4
+ VERSION = '0.8.7'
5
5
  end
@@ -403,7 +403,7 @@ module Squared
403
403
  end
404
404
  unless ref.is_a?(Symbol) && (proj = @base.find { |item| item.ref == ref })
405
405
  proj = if !ref.is_a?(Class)
406
- require_project(ref) || Application.find(ref: ref, path: path)
406
+ find_project(ref: ref, path: path)
407
407
  elsif Application.extends(ref)
408
408
  ref
409
409
  end || @kind[name]&.last
@@ -447,6 +447,16 @@ module Squared
447
447
  self
448
448
  end
449
449
 
450
+ def assign(ref = @ref, &blk)
451
+ proj = ref && find_project(ref: ref)
452
+ if proj
453
+ proj.class_eval(&blk)
454
+ else
455
+ warn log_message(Logger::ERROR, 'class not found', subject: main, hint: ref)
456
+ end
457
+ self
458
+ end
459
+
450
460
  def stage(val, first: false, &blk)
451
461
  data = @stage[val]
452
462
  if first == true
@@ -474,7 +484,7 @@ module Squared
474
484
  obj = begin
475
485
  JSON.parse(homepath(obj).read, { symbolize_names: true })
476
486
  rescue => e
477
- warn log_message(Logger::ERROR, e)
487
+ warn log_message(Logger::ERROR, e, subject: main)
478
488
  end
479
489
  end
480
490
  apply_style(data || theme, obj, args, empty: empty) if obj && (!target || data)
@@ -635,16 +645,24 @@ module Squared
635
645
  val = val.split(':') if val.is_a?(String)
636
646
  if before || after || opts
637
647
  pos = []
638
- pos << (before.is_a?(Array) ? before.join(',') : before) if before
639
- if opts
640
- pos << if opts.is_a?(Array)
641
- arg ? "#{arg}=#{opts.join(',')}" : opts.join('|')
648
+ append = lambda do |data|
649
+ pos << case data
650
+ when Array
651
+ data.join(',')
652
+ when Hash
653
+ data.keys.join('|')
642
654
  else
643
- opts
655
+ data
644
656
  end
645
657
  end
646
- pos << (after.is_a?(Array) ? after.join(',') : after) if after
647
- val << "#{val.pop}[#{pos.join(',')}]"
658
+ append.call(before)
659
+ pos << if opts.is_a?(Array)
660
+ arg ? "#{arg}=#{opts.join(',')}" : opts.join('|')
661
+ else
662
+ opts
663
+ end
664
+ append.call(after)
665
+ val << "#{val.pop}[#{pos.compact.join(',')}]"
648
666
  end
649
667
  out ? message(*val) : task_desc(*val)
650
668
  end
@@ -1082,6 +1100,10 @@ module Squared
1082
1100
  nil
1083
1101
  end
1084
1102
 
1103
+ def find_project(ref:, path: nil)
1104
+ require_project(ref) || Application.find(ref: ref, path: path)
1105
+ end
1106
+
1085
1107
  def root?(path, pass: [])
1086
1108
  return false unless path.directory?
1087
1109
 
@@ -1174,10 +1174,20 @@ module Squared
1174
1174
  ret = basepath(*args)
1175
1175
  return ret unless ascend && !ret.exist?
1176
1176
 
1177
+ case ascend
1178
+ when String, Pathname
1179
+ root = Pathname.new(ascend).cleanpath
1180
+ root = nil unless root.absolute? && root.directory?
1181
+ end
1177
1182
  path.parent.ascend.each do |dir|
1178
1183
  target = dir.join(*args)
1179
1184
  return target if target.exist?
1180
- break if (ascend.is_a?(String) && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
1185
+
1186
+ if root
1187
+ break if dir == root
1188
+ elsif workspace.root == dir || parent&.path == dir || (ascend.is_a?(String) && dir.join(ascend).exist?)
1189
+ break
1190
+ end
1181
1191
  end
1182
1192
  ret
1183
1193
  end
@@ -1365,7 +1375,7 @@ module Squared
1365
1375
  run(val, var, sync: sync, banner: banner, **kwargs)
1366
1376
  end
1367
1377
  rescue => e
1368
- on_error(e, exception: kwargs.fetch(:exception, exception?))
1378
+ on_error(e, exception: exception?(**kwargs))
1369
1379
  end
1370
1380
  on :last, from
1371
1381
  end
@@ -1901,7 +1911,7 @@ module Squared
1901
1911
  out << ''
1902
1912
  end
1903
1913
  if from
1904
- out << from
1914
+ out << (from = from.to_s)
1905
1915
  /\A(#{Regexp.escape(from)})(.*)\z/
1906
1916
  end
1907
1917
  else
@@ -2765,7 +2775,9 @@ module Squared
2765
2775
  workspace.warning
2766
2776
  end
2767
2777
 
2768
- def exception?(*level)
2778
+ def exception?(*level, **kwargs)
2779
+ return kwargs[:exception] if kwargs.key?(:exception)
2780
+
2769
2781
  ret = exception
2770
2782
  return ret unless ret.is_a?(Numeric)
2771
2783
 
@@ -181,7 +181,7 @@ module Squared
181
181
  if flags.nil?
182
182
  case action
183
183
  when 'ls'
184
- format_desc(action, nil, VAL_DOCKER[:ls].keys, after: 'a/ll?,s/tandard?,range*', arg: nil)
184
+ format_desc(action, nil, 'a/ll?,s/tandard?,range*', before: VAL_DOCKER[:ls])
185
185
  task action, [:command] do |_, args|
186
186
  command = param_guard(action, 'command', args: args, key: :command)
187
187
  args = args.extras
@@ -968,10 +968,13 @@ module Squared
968
968
  when :commit, :'no-commit'
969
969
  format_desc action, flag, 'branch|:,ours|theirs?,opts*'
970
970
  task flag, [:branch] do |_, args|
971
- branch = args.branch
972
- current = git_spawn('branch --show-current').chomp
971
+ branch = if args.branch == ':'
972
+ choice_refs('Choose a branch', accept: "#{action.capitalize}?")
973
+ else
974
+ args.branch
975
+ end
976
+ current = git_value 'branch --show-current'
973
977
  if branch
974
- branch = choice_refs('Choose a branch', accept: "#{action.capitalize}?") if branch == ':'
975
978
  opts = args.extras
976
979
  if has_value!(opts, 'ours')
977
980
  opts << 's=ort' << 'X=ours'
@@ -1103,7 +1106,7 @@ module Squared
1103
1106
  git(:remote, args.extras, extras: [name, url])
1104
1107
  end
1105
1108
  else
1106
- format_desc action, flag, "#{OPT_GIT[:remote].keys.join('|')},opts*" if TASK_METADATA
1109
+ format_desc(action, flag, 'opts*', before: OPT_GIT[:remote])
1107
1110
  task flag, [:command] do |_, args|
1108
1111
  command = param_guard(action, flag, args: args, key: :command)
1109
1112
  cmd, opts = git_session('remote', opts: args.extras)
@@ -1223,7 +1226,7 @@ module Squared
1223
1226
  (heads.dup << cur).each_with_index do |branch, i|
1224
1227
  next unless (i < heads.size && cur != branch) || i == heads.size
1225
1228
 
1226
- git_spawn 'switch --quiet', force && '--force', shell_quote(branch)
1229
+ git_spawn('switch --quiet', force && '--force', shell_quote(branch))
1227
1230
  pull(nil, opts, sync: false, hint: branch) if heads.include?(branch)
1228
1231
  end
1229
1232
  return
@@ -1299,13 +1302,13 @@ module Squared
1299
1302
  if flag
1300
1303
  return unless dir && ref
1301
1304
 
1302
- url = git_spawn("config --get remote.#{remotetrack}.url")
1305
+ url = git_value "config --get remote.#{remotetrack}.url"
1303
1306
  cmd, opts = git_session('clone', worktree: false, opts: opts)
1304
1307
  op = OptionPartition.new(opts, OPT_GIT[:clone], cmd, project: self, strict: strict?,
1305
1308
  no: OPT_GIT[:no][:clone])
1306
1309
  op.add_option('b', ref)
1307
1310
  .delim
1308
- .add_quote(url.chomp)
1311
+ .add_quote(url)
1309
1312
  .add_path(dir)
1310
1313
  .clear
1311
1314
  source
@@ -1498,7 +1501,7 @@ module Squared
1498
1501
  end
1499
1502
  return
1500
1503
  end
1501
- sha = git_spawn('rev-parse --verify HEAD').chomp
1504
+ sha = git_value 'rev-parse --verify HEAD'
1502
1505
  return if sha.empty?
1503
1506
 
1504
1507
  sync = kwargs.fetch(:sync) { invoked_sync?('revbuild', flag) }
@@ -1561,7 +1564,7 @@ module Squared
1561
1564
  .clear(pass: false)
1562
1565
  when :commit
1563
1566
  exit 1 unless ref
1564
- branch ||= git_spawn('name-rev --name-only', ref).chomp
1567
+ branch ||= git_value('name-rev --name-only', ref)
1565
1568
  raise ArgumentError, message('could not resolve upstream branch', hint: ref) if branch.empty?
1566
1569
 
1567
1570
  op.append(remotetrack, "#{ref}:#{branch}")
@@ -1756,7 +1759,7 @@ module Squared
1756
1759
  end
1757
1760
  if (n = op.index('>'))
1758
1761
  patch = op.slice!(n, 2)[1]
1759
- elsif !op.exist?(op.last)
1762
+ elsif !op.last.include?('*') && !op.exist?(op.last)
1760
1763
  patch ||= op.pop
1761
1764
  end
1762
1765
  append_pathspec op.extras
@@ -1968,7 +1971,8 @@ module Squared
1968
1971
  list_result(ret, 'branches', banner: banner, from: from)
1969
1972
  return
1970
1973
  else
1971
- if (head = git_spawn('rev-parse --abbrev-ref HEAD').chomp).empty?
1974
+ head = git_value 'rev-parse --abbrev-ref HEAD'
1975
+ if head.empty?
1972
1976
  ret = 0
1973
1977
  else
1974
1978
  git_spawn 'fetch --all --prune --quiet' if option('sync')
@@ -2095,7 +2099,7 @@ module Squared
2095
2099
  else
2096
2100
  ref
2097
2101
  end)
2098
- return git_spawn(cmd, exception: false).chomp
2102
+ return git_value(cmd, exception: false)
2099
2103
  when :output
2100
2104
  if opts.delete('sq-quote')
2101
2105
  cmd << '--sq-quote'
@@ -2349,7 +2353,7 @@ module Squared
2349
2353
  ret = choice_index(msg, git_spawn(cmd, stdout: false), column: /^(\S+)/, force: force, **kwargs)
2350
2354
  case ret
2351
2355
  when Array
2352
- ret.map { |val| val&.stripstyle }
2356
+ ret.map { |val| val.is_a?(Array) ? val.map { |s| s&.stripstyle } : val&.stripstyle }
2353
2357
  when String
2354
2358
  ret.stripstyle
2355
2359
  else
@@ -2527,6 +2531,10 @@ module Squared
2527
2531
  stdout: stdout, banner: banner, **kwargs)
2528
2532
  end
2529
2533
 
2534
+ def git_value(*cmd, exception: true)
2535
+ git_spawn(*cmd, exception: exception).chomp
2536
+ end
2537
+
2530
2538
  def git_grep(cmd, *grep, equals: true, **kwargs)
2531
2539
  ret = []
2532
2540
  git_spawn(cmd, **kwargs).lines(chomp: true).each do |line|
@@ -69,7 +69,7 @@ module Squared
69
69
  install: %w[check-cache check-resolutions immutable immutable-cache inline-builds json refresh-lockfile
70
70
  mode=b].freeze,
71
71
  add: %w[cached D|dev json O|optional P|peer prefer-dev].freeze,
72
- add_a: %w[C|caret E|exact F|fixed interactive T|tilde mode=b].freeze,
72
+ add_a: %w[C|caret E|exact F|fixed i|interactive no-time-gate T|tilde mode=b].freeze,
73
73
  update: %w[R|recursive].freeze,
74
74
  dedupe: %w[check json mode=b strategy=b].freeze,
75
75
  run: %w[B|binaries-only inspect inspect-brk T|top-level require=q].freeze,
@@ -305,13 +305,11 @@ module Squared
305
305
  format_desc action, nil, 'version|pre?{major,minor,patch}|prerelease'
306
306
  task action, [:version] do |_, args|
307
307
  version = param_guard(action, 'to', args: args, key: :version)
308
- if version.match?(/^((pre)?(major|minor|patch))|prerelease$/)
309
- flag = version.to_sym
310
- version = nil
308
+ if version.match?(/^(pre)?(major|minor|patch)|prerelease$/)
309
+ bump version.to_sym
311
310
  else
312
- flag = :version
311
+ bump :version, version
313
312
  end
314
- bump flag, version
315
313
  end
316
314
  when 'pack'
317
315
  format_desc action, nil, 'opts*'
@@ -662,7 +660,7 @@ module Squared
662
660
  end
663
661
  if nolockfile?(prefix: 'yarn')
664
662
  cmd << '--no-lockfile'
665
- elsif option('ci') || (ci? && exist?('yarn.lock'))
663
+ elsif option('ci') || (ci? && exist?(yarnlock))
666
664
  if yarn == 1
667
665
  cmd << '--frozen-lockfile'
668
666
  elsif !flag
@@ -698,13 +696,13 @@ module Squared
698
696
  '--force'
699
697
  elsif nolockfile?(prefix: 'pnpm')
700
698
  '--no-lockfile'
701
- elsif option('ci') || (ci? && exist?('pnpm-lock.yaml'))
699
+ elsif option('ci') || (ci? && exist?(pnpmlock))
702
700
  '--frozen-lockfile'
703
701
  end
704
702
  cmd << '--ignore-scripts' if option('ignore-scripts')
705
703
  cmd << '--dangerously-allow-all-builds' if option('approve-builds')
706
704
  else
707
- ci = option('ci', prefix: 'npm') || (ci? && exist?('package-lock.json'))
705
+ ci = option('ci', prefix: 'npm') || (ci? && exist?(npmlock))
708
706
  cmd = session('npm', ci ? 'ci' : 'install')
709
707
  option('approve-scripts') do |val|
710
708
  cmd = npm_output 'approve-scripts'
@@ -758,7 +756,7 @@ module Squared
758
756
  append_platform
759
757
  end
760
758
  end
761
- cmd << '--package-lock=false' << 'save=false' if nolockfile?(prefix: 'npm')
759
+ cmd << '--package-lock=false' << '--save=false' if nolockfile?(prefix: 'npm')
762
760
  cmd << '--ignore-scripts' if option('ignore-scripts')
763
761
  end
764
762
  if add
@@ -771,7 +769,7 @@ module Squared
771
769
  end
772
770
 
773
771
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
774
- cmd = session(pnpm? && exist?('pnpm-lock.yaml') ? 'pnpm' : 'npm', 'outdated')
772
+ cmd = session(pnpm? && exist?(pnpmlock) ? 'pnpm' : 'npm', 'outdated')
775
773
  dryrun = has_value?(opts, 'd', 'dry-run') || dryrun?
776
774
  unless dryrun
777
775
  log.info cmd.to_s
@@ -1381,7 +1379,7 @@ module Squared
1381
1379
  print_run(cmd, banner, **kwargs)
1382
1380
  session 'npx', cmd
1383
1381
  start = time_epoch if kwargs.fetch(:verbose, verbose? && !stdin?)
1384
- ret = run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: from)
1382
+ ret = run(sync: sync, banner: banner, exception: exception?(**kwargs), from: from)
1385
1383
  print_status(name, subject: 'tsc', start: start) if success?(ret, banner, start.nil?) && start
1386
1384
  ret
1387
1385
  end
@@ -1452,7 +1450,7 @@ module Squared
1452
1450
  end
1453
1451
 
1454
1452
  def yarn?
1455
- (@pm[:yarn] ||= if rootpath('yarn.lock', ascend: dependroot).exist?
1453
+ (@pm[:yarn] ||= if rootpath(yarnlock, ascend: dependroot).exist?
1456
1454
  yarntype
1457
1455
  elsif (ver = read_package || read_install)
1458
1456
  if ver =~ /^yarn(?:@(\d)|$)/
@@ -1473,7 +1471,7 @@ module Squared
1473
1471
  end
1474
1472
 
1475
1473
  def pnpm?
1476
- (@pm[:pnpm] ||= if rootpath('pnpm-lock.yaml', ascend: dependroot).exist?
1474
+ (@pm[:pnpm] ||= if rootpath(pnpmlock, ascend: dependroot).exist?
1477
1475
  pnpmtype
1478
1476
  elsif (ver = read_package || read_install)
1479
1477
  ver.start_with?('pnpm') ? pnpmtype : 0
@@ -1582,7 +1580,7 @@ module Squared
1582
1580
  require 'yaml'
1583
1581
  doc = YAML.load_file(rc)
1584
1582
  doc.nodeLinker == 'node-modules' ? 2 : 3
1585
- elsif exist && !exist?('yarn.lock')
1583
+ elsif exist && !exist?(yarnlock)
1586
1584
  0
1587
1585
  else
1588
1586
  1
@@ -1594,6 +1592,10 @@ module Squared
1594
1592
  3
1595
1593
  end
1596
1594
 
1595
+ def yarnlock
1596
+ 'yarn.lock'
1597
+ end
1598
+
1597
1599
  def pnpmtype(exist: false)
1598
1600
  require 'yaml'
1599
1601
  doc = YAML.load_file(basepath('node_modules/.modules.yaml', ascend: dependroot))
@@ -1608,13 +1610,21 @@ module Squared
1608
1610
  end
1609
1611
  rescue => e
1610
1612
  if exist
1611
- %w[pnpm-lock.yaml pnpm-workspace.yaml].any? { |val| exist?(val) } ? 4 : 0
1613
+ %W[#{pnpmlock} pnpm-workspace.yaml].any? { |val| exist?(val) } ? 4 : 0
1612
1614
  else
1613
1615
  log.debug e
1614
1616
  4
1615
1617
  end
1616
1618
  end
1617
1619
 
1620
+ def pnpmlock
1621
+ 'pnpm-lock.yaml'
1622
+ end
1623
+
1624
+ def pnpmopts(*args)
1625
+ OPT_PNPM[:common] + args.flat_map { |name| OPT_PNPM.fetch(name, []) }
1626
+ end
1627
+
1618
1628
  def remove_modules(prefix: dependbin)
1619
1629
  modules = basepath!('node_modules', type: 'd')
1620
1630
  return false unless modules && confirm_basic('Remove?', modules, prefix: prefix)
@@ -1696,16 +1706,16 @@ module Squared
1696
1706
  ].concat(args)
1697
1707
  end
1698
1708
 
1699
- def npmopts(*args)
1700
- OPT_NPM[:common] + args.flat_map { |name| OPT_NPM.fetch(name, []) }
1709
+ def npmlock
1710
+ 'package-lock.json'
1701
1711
  end
1702
1712
 
1703
- def pnpmopts(*args)
1704
- OPT_PNPM[:common] + args.flat_map { |name| OPT_PNPM.fetch(name, []) }
1713
+ def npmopts(*args)
1714
+ OPT_NPM[:common] + args.flat_map { |name| OPT_NPM.fetch(name, []) }
1705
1715
  end
1706
1716
 
1707
1717
  def lockfile(delete = false)
1708
- ret = basepath(yarn? ? 'yarn.lock' : pnpm? ? 'pnpm-lock.yaml' : 'package-lock.json')
1718
+ ret = basepath(yarn? ? yarnlock : pnpm? ? pnpmlock : npmlock)
1709
1719
  if ret.exist?
1710
1720
  if delete
1711
1721
  ret.delete
@@ -7,22 +7,24 @@ module Squared
7
7
  DEP_PYTHON = %w[poetry.lock setup.cfg pyproject.toml setup.py requirements.txt].freeze
8
8
  DIR_PYTHON = (DEP_PYTHON + %w[meson.build README.rst]).freeze
9
9
  OPT_PYTHON = {
10
- common: %w[b=+ B d E h i I O P q=+ s S u v=+ V=+ x c=q m=b W=b X=q check-hash-based-pycs=b].freeze,
10
+ common: %w[b=+ B d E h i I O P q=+ s S u v=+ V=+ x c=q m=b W=b X=q
11
+ check-hash-based-pycs={always,default,never}].freeze,
11
12
  build: %w[C=bm n|no-isolation s|sdist x|skip-dependency-check v|verbose w|wheel config-json=q config-setting=q
12
13
  installer=b o|outdir=p].freeze,
13
14
  venv: %w[clear copies symlinks system-site-packages upgrade upgrade-deps without-scm-ignore-files without-pip
14
15
  prompt=q].freeze,
15
16
  virtualenv: %w[always-copy clear copies download never-download no-download no-periodic-update no-pip no-seed
16
- no-setuptools no-vcs-ignore q|quiet read-only-app-data reset-app-data symlink-app-data symlinks
17
- system-site-packages with-traceback without-pip upgrade-embed-wheels v|verbose=+ activators=q
18
- app-data=p creator=b discovery=b extra-search-dir=p pip=b prompt=q p|python=q seeder=b
19
- setuptools=b try-first-with=q].freeze
17
+ no-setuptools no-vcs-ignore no-venv-redirect q|quiet read-only-app-data reset-app-data
18
+ symlink-app-data symlinks system-site-packages with-traceback without-pip upgrade-embed-wheels
19
+ venv-redirect v|verbose=+ activators={bash,batch,cshell,fish,nushell,powershell,python,xonsh}
20
+ app-data=p creator=b discovery={builtin} extra-search-dir=p pip=b prompt=q p|python=q
21
+ seeder={app-data,pip} setuptools=b try-first-with=q].freeze
20
22
  }.freeze
21
23
  OPT_PIP = {
22
- common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input require-virtualenv
23
- q|quiet=+ v|verbose=+ cache-dir=p cert=p client-cert=p exists-action=b keyring-provider=b log=p
24
- proxy=q python=q resume-retries=i retries=i timeout=i trusted-host=b use-deprecated=b
25
- use-feature=b].freeze,
24
+ common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input no-proxy-env
25
+ require-virtualenv q|quiet=+ v|verbose=+ cache-dir=p cert=p client-cert=p exists-action=b
26
+ keyring-provider=b log=p proxy=q python=q resume-retries=i retries=i timeout=i trusted-host=b
27
+ use-deprecated=b use-feature=b].freeze,
26
28
  cache: %w[format=b].freeze,
27
29
  completion: %w[b|bash f|fish p|powershell z|zsh].freeze,
28
30
  config: %w[global user site editor=p].freeze,
@@ -35,12 +37,12 @@ module Squared
35
37
  no-warn-conflicts no-warn-script-location U|upgrade user prefix=p report=p root=p
36
38
  root-user-action=b t|target=p upgrade-strategy=b].freeze,
37
39
  install_a: %w[no-index pre prefer-binary all-releases=b extra-index-url=q f|find-links=q i|index-url=q
38
- no-binary=q only-binary=q only-final=b].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,
40
+ no-binary=q only-binary=q only-final=b refresh-package=q uploaded-prior-to=q].freeze,
41
+ install_b: %w[check-build-dependencies no-build-isolation no-clean no-deps no-require-hashes only-deps
42
+ require-hashes use-pep517 build-constraint=p c|constraint=p group=q progress-bar=b
43
+ r|requirement=p requirements-from-script=p src=p].freeze,
42
44
  install_c: %w[C|config-settings=q e|editable=v].freeze,
43
- install_d: %w[ignore-requires-python uploaded-prior-to=q].freeze,
45
+ install_d: %w[ignore-requires-python].freeze,
44
46
  hash: %w[a|algorithm].freeze,
45
47
  list: %w[e|editable exclude-editable include-editable l|local not-required o|outdated u|uptodate user
46
48
  exclude=b format=b path=p].freeze,
@@ -77,15 +79,20 @@ module Squared
77
79
  sign-with=b u|username=qq].freeze
78
80
  }.freeze
79
81
  OPT_MESON = {
80
- build: %w[D=q clearcache debug errorlogs fatal-meson-warnings pkgconfig.relocatable prefer-static
81
- python.allow-limited-api reconfigure stdsplit strip vsenv werror wipe auto-features=b backend=b
82
- bindir=p build.cmake-prefix-path=p build.pkg-config-path=p buildtype=b cmake-prefix-path=p
83
- cross-file=p datadir=p default-library=b default-both-libraries=b force-fallback-for=q genvslite=b
84
- includedir=p infodir=p install-umask=b layout=b libdir=p libexecdir=p licensedir=p localedir=p
85
- localstatedir=p mandir=p native-file=p optimization=b pkg-config-path=p prefix=p
86
- python.bytecompile=i python.platlibdir=p python.purelibdir=p python.install-env=b sbindir=p
87
- sharedstatedir=p sysconfdir=p unity=b unity-size=i warnlevel=b wrap-mode=b].freeze,
88
- compile: %w[clean v|verbose j|jobs=i l|load-average=b ninja-args=q vs-args=q xcode-args=q].freeze
82
+ build: %w[D=q clearcache debug errorlogs fatal-meson-warnings os2-emxomf pkgconfig.relocatable prefer-static
83
+ python.allow-limited-api reconfigure stdsplit strip vsenv werror wipe auto-features=b
84
+ backend={ninja,vs,vs2010,vs2012,vs2013,vs2015,vs2017,vs2019,vs2022,vs2026,xcode,none}
85
+ bindir=q build.cmake-prefix-path=p build.pkg-config-path=p
86
+ buildtype={plain,debug,debugoptimized,release,minsize,custom} cmake-prefix-path=p cross-file=p
87
+ datadir=q default-library={shared,static,both} default-both-libraries={shared,static,auto}
88
+ force-fallback-for=q genvslite={vs2022,vs2026} includedir=q infodir=q install-umask=b
89
+ layout={mirror,flat} libdir=q libexecdir=q licensedir=q localedir=q localstatedir=q mandir=q
90
+ namingscheme={platform,classic} native-file=p optimization={plain,0,g,1,2,3,s} pkg-config-path=p
91
+ prefix=p python.build-config=p python.bytecompile=b python.platlibdir=p python.purelibdir=p
92
+ python.install-env={auto,prefix,system,venv} sbindir=q sharedstatedir=q sysconfdir=q
93
+ unity={on,off,subprojects} unity-size=i warnlevel={0,1,2,3,everything}
94
+ wrap-mode={default,nofallback,nodownload,forcefallback,nopromote}].freeze,
95
+ compile: %w[C=p clean v|verbose j|jobs=i l|load-average=b ninja-args=q vs-args=q xcode-args=q].freeze
89
96
  }.freeze
90
97
  PASS_PYTHON = {
91
98
  python: %w[c v V].freeze,
@@ -276,7 +283,7 @@ module Squared
276
283
  args = args.to_a
277
284
  if args.empty?
278
285
  args = readline('Enter command', force: true).split(' ', 2)
279
- elsif args.size == 1 && option('interactive', notequals: '0', prefix: ref)
286
+ elsif args.size == 1 && option('interactive', notequals: '0', prefix: 'python')
280
287
  args << readline('Enter arguments', force: false) unless args.first.include?(' ')
281
288
  end
282
289
  venv_init
@@ -346,18 +353,18 @@ module Squared
346
353
  case flag
347
354
  when :upgrade
348
355
  task flag, [:strategy] do |_, args|
349
- install flag, (case args.strategy
350
- when 'eager'
351
- 'eager'
352
- when /^only-if|needed$/
353
- 'only-if-needed'
354
- end.yield_self do |val|
355
- if val
356
- args.extras << "upgrade-strategy=#{val}"
357
- else
358
- args.to_a
359
- end
360
- end)
356
+ val = case args.strategy
357
+ when 'eager'
358
+ 'eager'
359
+ when /^only-if/, /needed$/
360
+ 'only-if-needed'
361
+ end
362
+ args = if val
363
+ args.extras << "upgrade-strategy=#{val}"
364
+ else
365
+ args.to_a
366
+ end
367
+ install flag, args
361
368
  end
362
369
  when :requirement, :target
363
370
  task flag, [:path] do |_, args|
@@ -735,7 +742,7 @@ module Squared
735
742
  op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), OPT_PYTHON[:common],
736
743
  session('python', path: !venv?),
737
744
  project: self, strict: strict?, multiple: [/^-c/], single: singleopt(:python),
738
- args: true, stdin: true)
745
+ sep: ' ', args: true, stdin: true)
739
746
  op.concat(args)
740
747
  if op.include?('-')
741
748
  op.exist?(add: true)
@@ -749,7 +756,7 @@ module Squared
749
756
  end
750
757
  end
751
758
  print_run(op, banner, **kwargs)
752
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: :python)
759
+ run(sync: sync, banner: banner, exception: exception?(**kwargs), from: :python)
753
760
  end
754
761
 
755
762
  def pip(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -831,7 +838,7 @@ module Squared
831
838
  op.clear
832
839
  end
833
840
  print_run(op, banner, **kwargs)
834
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: symjoin('pip', flag))
841
+ run(sync: sync, banner: banner, exception: exception?(**kwargs), from: symjoin('pip', flag))
835
842
  .yield_self { |val| ret || val }
836
843
  end
837
844
 
@@ -936,7 +943,8 @@ module Squared
936
943
  pre = preopts(quiet: quiet)
937
944
  return session(name, *pre, *cmd, path: !venv?) unless opts
938
945
 
939
- op = OptionPartition.new(opts, common, project: self, strict: strict?, single: singleopt(name.to_sym))
946
+ op = OptionPartition.new(opts, common, project: self, strict: strict?, sep: name == 'python' ? ' ' : '=',
947
+ single: singleopt(name.to_sym))
940
948
  [session(name, *pre, *op.to_a, *cmd, path: !venv?), op.extras]
941
949
  end
942
950
 
@@ -1089,12 +1097,12 @@ module Squared
1089
1097
  def singleopt(flag = nil)
1090
1098
  case flag
1091
1099
  when :python
1092
- /\A(?:v+|q+|b+|V+|O+)\z/
1100
+ /\A(v+|q+|b+|V+|O+)\z/
1093
1101
  when :pdm, :poetry
1094
1102
  /\Av+\z/
1095
1103
  when :twine, :meson then nil
1096
1104
  else
1097
- /\A(?:v+|q+)\z/
1105
+ /\A(v+|q+)\z/
1098
1106
  end
1099
1107
  end
1100
1108
 
@@ -1112,19 +1120,18 @@ module Squared
1112
1120
  def pipopts(flag)
1113
1121
  ret = OPT_PIP.fetch(flag, []) + OPT_PIP[:common]
1114
1122
  if pip_install?(flag)
1115
- ret.concat(OPT_PIP[:install_a])
1123
+ ret.concat(OPT_PIP[:install_a], OPT_PIP[:install_d])
1116
1124
  ret.concat(OPT_PIP[:install_b]) unless flag == :index
1117
- ret.concat(OPT_PIP[:install_d])
1118
1125
  case flag
1119
1126
  when :install
1120
- ret.concat(OPT_PIP[:install_c] + OPT_PIP[:debug])
1127
+ ret.concat(OPT_PIP[:install_c], OPT_PIP[:debug])
1121
1128
  when :lock, :wheel
1122
1129
  ret.concat(OPT_PIP[:install_c])
1123
1130
  when :download, :index
1124
1131
  ret.concat(OPT_PIP[:debug])
1125
1132
  end
1126
1133
  elsif flag == :list
1127
- ret.concat(OPT_PIP[:install_a])
1134
+ ret.concat(OPT_PIP[:install_a], OPT_PIP[:install_d])
1128
1135
  end
1129
1136
  ret
1130
1137
  end
@@ -1198,6 +1205,7 @@ module Squared
1198
1205
  status = true
1199
1206
  :virtualenv
1200
1207
  else
1208
+ status = !banner
1201
1209
  :venv
1202
1210
  end
1203
1211
  cmd, opts = python_session('-m', flag, opts: opts)
@@ -1205,32 +1213,31 @@ module Squared
1205
1213
  op.append(dir, delim: true)
1206
1214
  .clear(pass: false)
1207
1215
  status ||= op.arg?(/\A-v+\z/)
1208
- ret = run(op, env, exception: true, banner: banner)
1209
- args = []
1210
- requires = read_pyproject 'build-system', 'requires'
1211
- args.concat(requires) if requires.is_a?(Array)
1212
- if poetry?
1213
- requires = read_pyproject 'tool.poetry', 'requires-poetry'
1214
- args << "poetry#{requires}"
1215
- elsif setuptools? && !virtualenv?
1216
- args << 'setuptools' << 'wheel'
1217
- end
1218
- pip(:install, *args, banner: false) unless args.empty?
1219
- success?(ret, banner, !status) do |out|
1220
- if out && dir.directory?
1221
- if poetry?
1222
- dir += (windows? ? 'Scripts/python.exe' : 'bin/python')
1223
- cmd = poetry_session 'env use'
1224
- else
1225
- puts "Success: #{dir}"
1226
- return
1227
- end
1228
- cmd << shell_quote(dir)
1229
- print_run cmd, silent?
1230
- run(banner: false)
1216
+ if run(op, env, exception: true, banner: banner) && dir.directory?
1217
+ args = []
1218
+ requires = read_pyproject 'build-system', 'requires'
1219
+ args.concat(requires) if requires.is_a?(Array)
1220
+ if poetry?
1221
+ requires = read_pyproject 'tool.poetry', 'requires-poetry'
1222
+ args << "poetry#{requires}"
1223
+ elsif setuptools? && !virtualenv?
1224
+ args << 'setuptools' << 'wheel'
1225
+ end
1226
+ pip(:install, *args, banner: false) unless args.empty?
1227
+ if poetry?
1228
+ dir += (windows? ? 'Scripts/python.exe' : 'bin/python')
1229
+ poetry_session 'env use'
1230
+ elsif pdm?
1231
+ pdm_session 'use -f'
1231
1232
  else
1232
- puts 'Failed'
1233
+ puts "Success: #{dir}" unless status
1234
+ return
1233
1235
  end
1236
+ cmd << shell_quote(dir)
1237
+ print_run cmd, silent?
1238
+ run(banner: false)
1239
+ elsif !status
1240
+ puts 'Failed'
1234
1241
  end
1235
1242
  end
1236
1243
 
@@ -21,24 +21,25 @@ module Squared
21
21
  nomultiline noprompt noscript nosingleline noverbose regexp-completor sample-book-mode script
22
22
  simple-prompt single-irb singleline tracer truncate-echo-on-assignment type-completor verbose
23
23
  back-trace-limit=i context-mode=i prompt=b prompt-mode=b].freeze,
24
- rdbg: %w[no-color no-rc no-sigint-hook c|command n|nonstop stop-at-load cookie=q e=q host=q x|init-script=p
24
+ rdbg: %w[e=q no-color no-rc no-sigint-hook c|command n|nonstop stop-at-load cookie=q host=q x|init-script=p
25
25
  O|open=q? port=i port-range=b session-name=q sock-path=q util=q].freeze,
26
26
  rbs: %w[I=pm r=bm no-collection no-stdlib collection=p log-level=b log-output=p repo=p].freeze,
27
- rubocop: %w[D P r=bm auto-gen-config a|autocorrect A|autocorrect-all d|debug disable-pending-cops
28
- display-only-correctable display-only-fail-level-offenses display-only-failed
29
- display-only-safe-correctable S|display-style-guide display-time disable-uncorrectable editor-mode
30
- enable-pending-cops E|extra-details F|fail-fast force-default-config force-exclusion x|fix-layout
31
- ignore-disable-comments ignore-parent-exclusion ignore-unrecognized-cops init l|lint
32
- list-enabled-cops-for=p L|list-target-files lsp memory no-detach only-guide-cops
33
- only-recognized-file-types no-exclude-limit profile raise-cop-error regenerate-todo
34
- restart-server safe server-status start-server stderr stop-server C|cache=b cache-root=p config=p
35
- exclude-limit=i fail-level=b f|format=b except=q only=q o|out=p plugin=p require=p show-cops=q
36
- show-docs-url=q s|stdin=p].freeze,
27
+ rubocop: %w[D P r=bm auto-gen-config a|autocorrect A|autocorrect-all d|debug diff disable-all-cops
28
+ disable-pending-cops display-only-correctable display-only-fail-level-offenses display-only-failed
29
+ display-only-safe-correctable S|display-style-guide display-suppressed display-time
30
+ disable-uncorrectable editor-mode enable-pending-cops E|extra-details F|fail-fast
31
+ force-default-config force-exclusion x|fix-layout ignore-disable-comments ignore-parent-exclusion
32
+ ignore-unrecognized-cops init l|lint L|list-target-files lsp mcp memory no-detach no-exclude-limit
33
+ only-guide-cops only-recognized-file-types profile raise-cop-error regenerate-todo
34
+ report-unused-todo-entries restart-server safe server-status start-server stderr stop-server
35
+ verbose-version C|cache=b cache-root=p changed=b config=p exclude-limit=i fail-level=b f|format=b
36
+ except=q list-enabled-cops-for=p only=q o|out=p plugin=p require=p show-cops=q show-docs-url=q
37
+ s|stdin=p].freeze,
37
38
  pry: %w[f e=q I=pm no-color no-history no-multiline no-pager no-plugins simple-prompt c|context=q
38
39
  d|disable-plugin=q gem=b r|require=bm s|select-plugin=q].freeze,
39
40
  no: {
40
41
  rubocop: %w[auto-gen-enforced-style auto-gen-only-exclude auto-gen-timestamp color display-cop-names
41
- offense-counts parallel server].freeze
42
+ offense-counts parallel preview server].freeze
42
43
  }
43
44
  }.freeze
44
45
  OPT_BUNDLE = {
@@ -226,9 +227,10 @@ module Squared
226
227
  serve_set kwargs[:serve]
227
228
  gemfile_set kwargs[:gemspec]
228
229
  self.autodetect = autodetect
229
- @bundle = { exec: exec.is_a?(String) ? basepath!(exec) : false }
230
+ @bundle = { exec: exec }
230
231
  @steepfile = basepath! steep if steep
231
- @rubocopfile = Pathname.new(rubocop).realpath rescue basepath!(Dir.home, '.rubocop.yml') if rubocop
232
+ rubocop = rootpath('.rubocop.yml', ascend: Dir.home) if rubocop == true
233
+ @rubocopfile = basepath! rubocop if rubocop
232
234
  @rubygems = kwargs.fetch(:rubygems, 0)
233
235
  return unless rakefile && @output[0].nil? && @copy.nil? && !version && !self.autodetect
234
236
 
@@ -1039,7 +1041,7 @@ module Squared
1039
1041
  end
1040
1042
  end
1041
1043
  print_run(op, banner, **kwargs) unless flag
1042
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: from || :ruby)
1044
+ run(sync: sync, banner: banner, exception: exception?(**kwargs), from: from || :ruby)
1043
1045
  end
1044
1046
 
1045
1047
  def gem(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -1416,7 +1418,7 @@ module Squared
1416
1418
  op.clear(errors: true) if gems
1417
1419
  print_run(op, banner, **kwargs)
1418
1420
  run(banner: banner && %w[q silent quiet no-verbose].none? { |s| op.include?(shell_option(s)) },
1419
- exception: kwargs.fetch(:exception, exception?), from: from)
1421
+ exception: exception?(**kwargs), from: from)
1420
1422
  end
1421
1423
 
1422
1424
  def bundle(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -1553,7 +1555,7 @@ module Squared
1553
1555
  op.clear
1554
1556
  end
1555
1557
  print_run(op, banner, **kwargs)
1556
- ret = run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?),
1558
+ ret = run(sync: sync, banner: banner, exception: exception?(**kwargs),
1557
1559
  from: symjoin('bundle', flag))
1558
1560
  success?(ret, banner, output)
1559
1561
  end
@@ -1566,7 +1568,7 @@ module Squared
1566
1568
  op.append(escape: true)
1567
1569
  print_run(op, banner, **kwargs)
1568
1570
  var = { 'BANNER' => '0' } unless banner
1569
- run(op, var, sync: sync, banner: false, exception: kwargs.fetch(:exception, exception?), from: :rake)
1571
+ run(op, var, sync: sync, banner: false, exception: exception?(**kwargs), from: :rake)
1570
1572
  end
1571
1573
 
1572
1574
  def irb(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:irb], **kwargs)
@@ -1581,7 +1583,7 @@ module Squared
1581
1583
 
1582
1584
  def rdbg(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:rdbg], **kwargs)
1583
1585
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1584
- cmd = session 'rdbg'
1586
+ cmd = session(*bundle_args('rdbg'))
1585
1587
  args.each_with_index do |val, i|
1586
1588
  break if val == '--'
1587
1589
  next unless exist?(val, type: 'f')
@@ -1594,7 +1596,7 @@ module Squared
1594
1596
  op.concat(args)
1595
1597
  op.append(quote: false, delim: true)
1596
1598
  print_run(op, banner, **kwargs)
1597
- run(banner: false, exception: kwargs.fetch(:exception, exception?), from: :rdbg)
1599
+ run(banner: false, exception: exception?(**kwargs), from: :rdbg)
1598
1600
  end
1599
1601
 
1600
1602
  def rbs(flag, *args, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -1643,7 +1645,7 @@ module Squared
1643
1645
  op.clear
1644
1646
  .append(*args)
1645
1647
  print_run(op, banner, **kwargs)
1646
- run(banner: false, exception: kwargs.fetch(:exception, exception?), from: symjoin('rbs', flag))
1648
+ run(banner: false, exception: exception?(**kwargs), from: symjoin('rbs', flag))
1647
1649
  end
1648
1650
  end
1649
1651
 
@@ -1666,7 +1668,7 @@ module Squared
1666
1668
  op.append(delim: true)
1667
1669
  .clear(errors: true)
1668
1670
  print_run(op, banner, **kwargs)
1669
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: :rubocop)
1671
+ run(sync: sync, banner: banner, exception: exception?(**kwargs), from: :rubocop)
1670
1672
  end
1671
1673
 
1672
1674
  def serve(root, *, bind: nil, port: 3000, **kwargs)
@@ -1860,11 +1862,11 @@ module Squared
1860
1862
  op.concat(args)
1861
1863
  op.append(delim: delim)
1862
1864
  print_run(op, banner, **kwargs)
1863
- run(banner: false, exception: kwargs.fetch(:exception, exception?), from: from)
1865
+ run(banner: false, exception: exception?(**kwargs), from: from)
1864
1866
  end
1865
1867
 
1866
1868
  def ruby_session(*cmd, **kwargs)
1867
- session('ruby', *preopts, *cmd, **kwargs)
1869
+ session(*bundle_args('ruby'), *preopts, *cmd, **kwargs)
1868
1870
  end
1869
1871
 
1870
1872
  def gem_session(*cmd, **kwargs)
@@ -1882,11 +1884,10 @@ module Squared
1882
1884
  end
1883
1885
 
1884
1886
  def rbs_session(*cmd, opts: nil)
1885
- args = bundle_args 'rbs'
1886
- return session(*args, *cmd) unless opts
1887
+ return session(*bundle_args('rbs'), *cmd) unless opts
1887
1888
 
1888
1889
  op = OptionPartition.new(opts, OPT_RUBY[:rbs], project: self, strict: strict?)
1889
- [session(*args, *op.to_a, *cmd), op.extras]
1890
+ [session(*bundle_args('rbs'), *op.to_a, *cmd), op.extras]
1890
1891
  end
1891
1892
 
1892
1893
  def gem_output(*cmd, **kwargs)
@@ -1894,7 +1895,7 @@ module Squared
1894
1895
  end
1895
1896
 
1896
1897
  def ruby_output(*cmd, **kwargs)
1897
- session_output('ruby', *cmd, **kwargs)
1898
+ session_output(*bundle_args('ruby'), *cmd, **kwargs)
1898
1899
  end
1899
1900
 
1900
1901
  def bundle_output(*cmd, **kwargs)
@@ -1907,7 +1908,20 @@ module Squared
1907
1908
 
1908
1909
  def bundle_args(*args)
1909
1910
  if (val = @bundle[:exec])
1910
- args.unshift(quote_option('gemfile', val)) unless val == true
1911
+ app = args.first
1912
+ if val.is_a?(Hash)
1913
+ env = val[app.to_s] || val[app.to_sym]
1914
+ return args unless env
1915
+
1916
+ val = env.is_a?(String) ? basepath!(env) : true
1917
+ elsif app == 'ruby'
1918
+ return args
1919
+ end
1920
+ if val.is_a?(String)
1921
+ return args unless (val = basepath!(val))
1922
+
1923
+ args.unshift("--gemfile=#{shell_escape(val.relative_path_from(path), quote: true)}")
1924
+ end
1911
1925
  args.unshift('bundle', 'exec')
1912
1926
  end
1913
1927
  args
@@ -219,6 +219,7 @@ module Squared
219
219
  end
220
220
 
221
221
  attr_reader :target, :extras, :found, :errors, :values, :project, :path, :sep
222
+ attr_accessor :stdout
222
223
 
223
224
  def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
224
225
  :merge, :compact, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
@@ -241,14 +242,16 @@ module Squared
241
242
  @sep = sep
242
243
  @errors = []
243
244
  @found = []
245
+ @stdout = false
244
246
  parse(list, opts, **kwargs, &blk)
245
247
  end
246
248
 
247
249
  def parse(list, opts = extras, no: nil, single: nil, args: false, multiple: nil, first: nil, underscore: nil,
248
- explicit: false, bool: %w[true false], stdin: false, &blk)
250
+ explicit: false, bool: %w[true false], auto: [], regexp: {}, enum: {}, stdin: false, &blk)
249
251
  @extras = []
250
252
  @values = []
251
253
  bare = []
254
+ auto = auto.flat_map { |val| val.split('|', 2) }
252
255
  e = []
253
256
  b = []
254
257
  m = []
@@ -261,8 +264,6 @@ module Squared
261
264
  bl = []
262
265
  ex = []
263
266
  ml = []
264
- re = {}
265
- ar = {}
266
267
  sw = {}
267
268
  se = {}
268
269
  list.flat_map do |val|
@@ -284,10 +285,10 @@ module Squared
284
285
  end
285
286
  if (n = val.index('='))
286
287
  flag = val[0, n]
287
- enum = flag.split(':')
288
- mod = if enum.size > 1
289
- pre = enum.shift
290
- enum.map { |s| "#{pre}:#{s}" }
288
+ opt = flag.split(':')
289
+ mod = if opt.size > 1
290
+ pre = opt.shift
291
+ opt.map { |s| "#{pre}:#{s}" }
291
292
  else
292
293
  [flag]
293
294
  end
@@ -322,14 +323,14 @@ module Squared
322
323
  pat = eval(val)
323
324
  raise SyntaxError unless pat.is_a?(Regexp)
324
325
 
325
- mod.each { |key| re[key] = pat }
326
+ mod.each { |key| regexp[key] = pat }
326
327
  rescue SyntaxError
327
328
  warn "#{self.class}: invalid pattern (#{val})"
328
329
  end
329
330
  when '{'
330
331
  group = val[(n + 2)..-1].chomp!('}')&.split(',')
331
332
  if group
332
- mod.each { |key| ar[key] = group }
333
+ mod.each { |key| enum[key] = group }
333
334
  else
334
335
  warn "#{self.class}: invalid group (#{val})"
335
336
  end
@@ -350,6 +351,7 @@ module Squared
350
351
  tr = ->(a) { a.map { |val| val.tr('-', '_') } }
351
352
  @values.concat(tr.call(@values))
352
353
  bare.concat(tr.call(bare))
354
+ auto.concat(tr.call(auto))
353
355
  e.concat(tr.call(e))
354
356
  b.concat(tr.call(b))
355
357
  m.concat(tr.call(m))
@@ -363,6 +365,7 @@ module Squared
363
365
  ml.concat(tr.call(ml))
364
366
  no.concat(tr.call(no))
365
367
  end
368
+ bare.concat(auto)
366
369
  if target.is_a?(JoinSet)
367
370
  target.multiple = multiple if multiple
368
371
  target.multiple = ml.map { |val| val.size == 1 ? "-#{val}" : "--#{val}" }
@@ -395,6 +398,10 @@ module Squared
395
398
  add "#{sw[opt]}#{opt}"
396
399
  else
397
400
  add(opt.size == 1 ? "-#{opt}" : "--#{opt}")
401
+ if auto.include?(opt)
402
+ @stdout = true
403
+ break
404
+ end
398
405
  end
399
406
  elsif opt.start_with?(/no[-_]/) && no.include?(s = opt[3..-1])
400
407
  add "--no-#{s}"
@@ -430,8 +437,8 @@ module Squared
430
437
  else
431
438
  push opt
432
439
  end
433
- elsif !ar.empty? && has.call(ar.keys, key)
434
- group = get.call(ar)
440
+ elsif !enum.empty? && has.call(enum.keys, key)
441
+ group = get.call(enum)
435
442
  if group.include?(val)
436
443
  add shell_option(key, val, force: false, **kwargs)
437
444
  elsif @strict
@@ -439,10 +446,10 @@ module Squared
439
446
  else
440
447
  push opt
441
448
  end
442
- elsif !re.empty? && has.call(re.keys, key)
443
- pat = get.call(re)
449
+ elsif !regexp.empty? && has.call(regexp.keys, key)
450
+ pat = get.call(regexp)
444
451
  if val.match?(pat)
445
- add quote_option(key, val, **kwargs)
452
+ add __send__(kwargs[:merge] ? :basic_option : :quote_option, key, val, **kwargs)
446
453
  elsif @strict
447
454
  clear([opt], hint: "match != /#{pat.source}/")
448
455
  else
@@ -821,6 +828,10 @@ module Squared
821
828
  end
822
829
  end
823
830
 
831
+ def style?(*args)
832
+ !stdout && args.none?(&:!)
833
+ end
834
+
824
835
  def uniq!(list)
825
836
  list = uniq list
826
837
  return if list.empty?
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.8.5
4
+ version: 0.8.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
@@ -126,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
128
  requirements: []
129
- rubygems_version: 4.0.21
129
+ rubygems_version: 4.0.23
130
130
  specification_version: 4
131
131
  summary: Rake task generator for managing multi-language workspaces.
132
132
  test_files: []