squared 0.9.0 → 0.9.1

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: 41f1d82942dcd7279ec182d1fc0bdc8f90ccc2e476860c6ca70bc89286417598
4
- data.tar.gz: e6b7773f70fd0d2867fe13bdae57324dd6c90048c8bea3b27937738edf4df78c
3
+ metadata.gz: ed5b9e99423ac6374d36ad8dffd6a50f2714cef292d6324e358ae19d6a547ed9
4
+ data.tar.gz: 1e6358b439d1defc1f6d9601e915c49b619b00614fc104911ac0554dd61ce6cd
5
5
  SHA512:
6
- metadata.gz: cc02b93ac5fbfd34e23ff87cda66a3665fca3490c942e7a5942edb9ba6540eb6b95e1c87d8273444ec4bbac6d56841b7b3213295ce9ee856c15383794ecac97a
7
- data.tar.gz: ee1c5b726339754ee878ff97cd9994983cbc5ca4822d2ffa25170c2f3512414966f367dcc5c4f0f2bc1eb8156667b88fcffc294ccbab1468c57ef465bb387243
6
+ metadata.gz: 0e973fe0eb9137cd8e822b3eb5e28fc8275e69f17414373205944c28350b616cb58dae4fe41eb647ab2a018f5a21a3e7e43854cf0711a4f59c15614948bb328c
7
+ data.tar.gz: '0293c7c12c7c8d66a7783162a4d1fd5b9a0ae87f3476e0c0dae73f9a3d918fcda4d93a31894b327161a6b571be9c4988bcb0fb2abff0c10995fe4a64af4e93de'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.1] - 2026-10-22
4
+
5
+ ### Added
6
+
7
+ - sqd: Option --gemfile for rerouting commands through bundle exec.
8
+ - Python install manager for Windows is supported.
9
+ - sqd: Option --sync for running threads in a series was implemented.
10
+
11
+ ## [0.8.7]
12
+
13
+ ### Added
14
+
15
+ - OptionPartition can be initialized with enum and pattern options.
16
+
17
+ ### Fixed
18
+
19
+ - Python venv creation did not run post-install for package managers.
20
+
21
+ ## [0.7.14]
22
+
23
+ ### Changed
24
+
25
+ - Python program meson prefix options use local path resolution.
26
+ - Bundle exec wrapper can be configured per application.
27
+
28
+ ### Fixed
29
+
30
+ - Bundle exec wrapper did not accept boolean values.
31
+
32
+ ## [0.6.21]
33
+
34
+ ### Fixed
35
+
36
+ - RuboCop did not resolve config to project directory.
37
+ - Python program did not use correct separator for double dash options.
38
+
3
39
  ## [0.9.0] - 2026-10-12
4
40
 
5
41
  ### Added
@@ -2198,7 +2234,9 @@
2198
2234
 
2199
2235
  - Changelog was created.
2200
2236
 
2237
+ [0.9.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.9.1
2201
2238
  [0.9.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.9.0
2239
+ [0.8.7]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.7
2202
2240
  [0.8.6]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.6
2203
2241
  [0.8.5]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.5
2204
2242
  [0.8.4]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.4
@@ -2206,6 +2244,7 @@
2206
2244
  [0.8.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.2
2207
2245
  [0.8.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.1
2208
2246
  [0.8.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.0
2247
+ [0.7.14]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.14
2209
2248
  [0.7.13]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.13
2210
2249
  [0.7.12]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.12
2211
2250
  [0.7.11]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.11
@@ -2220,6 +2259,7 @@
2220
2259
  [0.7.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.2
2221
2260
  [0.7.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.1
2222
2261
  [0.7.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.0
2262
+ [0.6.21]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.21
2223
2263
  [0.6.20]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.20
2224
2264
  [0.6.19]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.19
2225
2265
  [0.6.18]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.18
data/README.md CHANGED
@@ -43,13 +43,15 @@ Usage: sqd [options] [rake options] [global...] [:targets...] -- [rake targets..
43
43
 
44
44
  -s, --[no-]strict Do not run commands with invalid arguments
45
45
  --update Use update strategy for applicable commands
46
- --parallel STR,PAT Run standard tasks with multi-task threads
46
+ -u, --parallel STR,PAT Run standard tasks with multi-task threads
47
+ -U, --sync STR,PAT Run parallel tasks in a series sequentially
47
48
  -F, --force [STR,STR] Enable force through ENV by application
48
49
  -y, --yes [STR,STR] Enable auto-approve through ENV by application
49
50
  -d, --dry-run Run commands without any modifying actions
50
51
 
51
52
  -S, --select PAGE[:CHOICE] Prompt menu selection pagination display (e.g. 15:75)
52
53
 
54
+ -g, --gemfile Execute commands using bundle exec and nearest Gemfile
53
55
  -p, --pipe 0|1|2 Set file descriptor for communication
54
56
  -o, --pipe-out FILE Write command data stream to a file
55
57
  --fail 0..4 Set application exception level
@@ -102,11 +104,11 @@ Workspace::Application
102
104
  .clean("rake clean", group: "default")
103
105
  .clean(["build/"], group: "app")
104
106
  .log({ file: "tmp/%Y-%m-%d.log", level: "debug" }, group: "app")
105
- .add("pathname", run: "rake compile", copy: "rake install", test: "rake test", group: "default", env: { # Ruby (with C extensions)
107
+ .add("pathname", run: "rake compile", copy: "rake install", test: "rake test", exec: true, group: "default", env: { # Ruby (with C extensions)
106
108
  "CFLAGS" => "-fPIC -O1"
107
109
  })
108
- .add("optparse", doc: "rake rdoc", gemspec: "optparse.gemspec", group: "default") # Uses bundler/gem_tasks (without C extensions)
109
- .add("logger", copy: { from: "lib", glob: "**/*.rb", into: "/home/user/.local/gem/ruby/4.0.0/gems/logger-1.7.0" }, clean: ["tmp/"]) do # autodetect: true | "rvm" | "rbenv" | "asdf" | "mise" | bundler"
110
+ .add("optparse", doc: "rake rdoc", gemspec: "optparse.gemspec", exec: { rake: true, ruby: '../Gemfile', rbs: false }, group: "default") # Uses bundler/gem_tasks (without C extensions)
111
+ .add("logger", copy: { from: "lib", glob: "**/*.rb", into: "/home/user/.local/gem/ruby/4.0.0/gems/logger-1.7.0" }, clean: ["tmp/"]) do # autodetect: true | "rvm" | "rbenv" | "asdf" | "mise" | bundler"
110
112
  self.gemdir = "/home/user/.local/gem/ruby/4.0.0/gems/logger-1.7.0" # Default for "into"
111
113
  end
112
114
  .add("e-mc", "emc", copy: { from: "publish", scope: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node)
data/exe/sqd CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ $:.unshift(File.expand_path('../lib', __dir__)) if File.exist?(File.expand_path('../squared.gemspec', __dir__))
5
+
4
6
  require 'squared'
5
7
  require 'rake'
6
8
  require 'optparse'
@@ -43,6 +45,19 @@ class SqdParser < OptionParser
43
45
  end
44
46
  end
45
47
 
48
+ def parse_series(list, session:, flag:)
49
+ pattern, parallel = list.partition do |val|
50
+ next false if val.match?(%r{^[\w~`!@#%&\-=:;"'<>/]+$})
51
+
52
+ Regexp.new(val)
53
+ rescue RegexpError
54
+ false
55
+ end
56
+ s = flag == :sync ? '!' : ''
57
+ (session[:APP][:"build_pattern#{s}"] ||= []).concat(pattern.map { |pat| Regexp.new(pat) }) unless pattern.empty?
58
+ (session[:APP][:"build_parallel#{s}"] ||= []).concat(parallel) unless parallel.empty?
59
+ end
60
+
46
61
  def banner_set(val, session:)
47
62
  session[:BANNER] = val
48
63
  ENV['BANNER'] = val ? 'true' : 'false'
@@ -101,18 +116,6 @@ class SqdParser < OptionParser
101
116
  ARGV[index] = ''
102
117
  end
103
118
  end
104
- if options[:parallel]
105
- pattern, parallel = options[:parallel].partition do |val|
106
- next false if val.match?(%r{^[\w~`!@#%&\-=:;"'<>/]+$})
107
-
108
- Regexp.new(val)
109
- rescue RegexpError
110
- false
111
- end
112
- session = Squared::Common::ARG
113
- session[:APP][:build_pattern] = pattern.map { |pat| Regexp.new(pat) } unless pattern.empty?
114
- session[:APP][:build_parallel] = parallel unless parallel.empty?
115
- end
116
119
  ARGV.reject!(&:empty?)
117
120
  Rake.application.run(global.concat(ARGV))
118
121
  end
@@ -128,6 +131,7 @@ end
128
131
 
129
132
  SqdParser.new do |sqd|
130
133
  session = Squared::Common::ARG
134
+ argv = ARGV.dup
131
135
  sqd.banner = 'Usage: sqd [options] [rake options] [global...] [:targets...] -- [rake targets...]'
132
136
  sqd.separator ''
133
137
  sqd.on('-R', '--ref STR,STR', Array, 'Run global commands by class reference') do |val|
@@ -154,7 +158,12 @@ SqdParser.new do |sqd|
154
158
  sqd.on('--update', 'Use update strategy for applicable commands') do
155
159
  ENV['UPDATE'] = 'true'
156
160
  end
157
- sqd.on('--parallel STR,PAT', Array, 'Run standard tasks with multi-task threads')
161
+ sqd.on('-u', '--parallel STR,PAT', Array, 'Run standard tasks with multi-task threads') do |val|
162
+ sqd.parse_series(val, session: session, flag: :parallel)
163
+ end
164
+ sqd.on('-U', '--sync STR,PAT', Array, 'Run parallel tasks in a series sequentially') do |val|
165
+ sqd.parse_series(val, session: session, flag: :sync)
166
+ end
158
167
  sqd.on('-F', '--force [STR,STR]', Array, 'Enable force through ENV by application') do |val|
159
168
  sqd.parse_common(val, session: session, arg: :FORCE)
160
169
  end
@@ -173,6 +182,24 @@ SqdParser.new do |sqd|
173
182
  ENV['SELECT'] = "#{page}:#{choice}"
174
183
  end
175
184
  sqd.separator ''
185
+ sqd.on('-g', '--gemfile', 'Execute commands using bundle exec and nearest Gemfile') do
186
+ require 'pathname'
187
+ next if ENV['RUBYOPT']&.match?(/(^|\b)-r.+#{File::SEPARATOR}bundler#{File::SEPARATOR}setup\b/)
188
+
189
+ gemfile = Pathname.pwd.ascend { |path| break path if path.join('Gemfile').file? } || ENV['BUNDLE_GEMFILE']
190
+ next unless gemfile && (argv.delete('--gemfile') || argv.delete('-g'))
191
+
192
+ include Squared::Common
193
+ include Shell
194
+
195
+ argv.unshift('sqd')
196
+ if gemfile.is_a?(String)
197
+ argv.unshift("--gemfile=#{shell_escape(Pathname.new(gemfile).relative_path_from(Dir.pwd), quote: true)}")
198
+ gemfile = Dir.pwd
199
+ end
200
+ ENV['BUNDLE_GEMFILE'] = nil
201
+ exec(shell_bin('bundle'), 'exec', *argv, chdir: gemfile)
202
+ end
176
203
  sqd.on('-p', '--pipe 0|1|2', Integer, 'Set file descriptor for communication') do |val|
177
204
  sqd.error_exit('out of range', val) unless val.between?(0, 2)
178
205
  session[:PIPE] = val
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.9.0'
4
+ VERSION = '0.9.1'
5
5
  end
@@ -167,6 +167,11 @@ module Squared
167
167
  @series.ref_min = app[:series_ref_min] if app.key?(:series_ref_min)
168
168
  kwargs[:pattern].concat(app[:build_pattern]) if app.key?(:build_pattern)
169
169
  kwargs[:parallel].concat(app[:build_parallel]) if app.key?(:build_parallel)
170
+ kwargs[:parallel].reject! { |val| app[:build_parallel!].include?(val) } if app.key?(:build_parallel!)
171
+ app[:build_pattern!]&.each do |pat|
172
+ kwargs[:pattern].delete(pat)
173
+ kwargs[:parallel].reject! { |val| pat.match?(val) }
174
+ end
170
175
  self.closed = 'begin'
171
176
  pb = @stage.key?('project:begin')
172
177
  pe = @stage.key?('project:end')
@@ -7,16 +7,18 @@ module Squared
7
7
  DEP_PYTHON = %w[poetry.lock pdm.lock pylock.toml 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
24
  common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input no-proxy-env
@@ -49,6 +51,13 @@ module Squared
49
51
  uninstall: %w[break-system-packages y|yes r|requirement=p root-user-action=b].freeze,
50
52
  wheel: %w[no-verify w|wheel-dir=p].freeze
51
53
  }.freeze
54
+ OPT_PY = {
55
+ common: %w[q|quiet=+ v|verbose=+ y|yes config=p].freeze,
56
+ exec: %w[3=/^\.\d+(\.\d+)?$/m? V=/^:./m?].freeze,
57
+ install: %w[by-id configure dry-run f|force refresh u|update d|download=p s|source=q t|target=p].freeze,
58
+ list: %w[1|one online only-managed format={table,json,jsonl,csv,exe,prefix,url,formats} s|source=q].freeze,
59
+ uninstall: %w[by-id cleanup purge].freeze
60
+ }.freeze
52
61
  OPT_POETRY = {
53
62
  common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet v|verbose=+ P|project=p].freeze,
54
63
  install: %w[all-extras all-groups compile dry-run no-directory no-root only-root E|extras=q only=q with=q
@@ -99,7 +108,8 @@ module Squared
99
108
  install: %w[C config-settings c constraint extra-index-url no-binary only-binary platform
100
109
  r requirement].freeze,
101
110
  list: %w[exclude extra-index-url no-binary only-binary].freeze
102
- }.freeze
111
+ }.freeze,
112
+ py: %w[q v quiet verbose].freeze
103
113
  }.freeze
104
114
  private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_PDM, :OPT_HATCH, :OPT_TWINE,
105
115
  :PASS_PYTHON
@@ -149,6 +159,7 @@ module Squared
149
159
  'outdated' => %i[major minor patch].freeze,
150
160
  'build' => %i[poetry pdm hatch meson python].freeze,
151
161
  'publish' => %i[twine poetry pdm hatch].freeze,
162
+ 'py' => %i[exec install list uninstall].freeze,
152
163
  'run' => nil,
153
164
  'exec' => nil
154
165
  })
@@ -281,7 +292,7 @@ module Squared
281
292
  args = args.to_a
282
293
  if args.empty?
283
294
  args = readline('Enter command', force: true).split(' ', 2)
284
- elsif args.size == 1 && option('interactive', notequals: '0', prefix: ref)
295
+ elsif args.size == 1 && option('interactive', notequals: '0', prefix: 'python')
285
296
  args << readline('Enter arguments', force: false) unless args.first.include?(' ')
286
297
  end
287
298
  venv_init
@@ -351,18 +362,18 @@ module Squared
351
362
  case flag
352
363
  when :upgrade
353
364
  task flag, [:strategy] do |_, args|
354
- install flag, (case args.strategy
355
- when 'eager'
356
- 'eager'
357
- when /^only-if|needed$/
358
- 'only-if-needed'
359
- end.then do |val|
360
- if val
361
- args.extras << "upgrade-strategy=#{val}"
362
- else
363
- args.to_a
364
- end
365
- end)
365
+ val = case args.strategy
366
+ when 'eager'
367
+ 'eager'
368
+ when /^only-if/, /needed$/
369
+ 'only-if-needed'
370
+ end
371
+ args = if val
372
+ args.extras << "upgrade-strategy=#{val}"
373
+ else
374
+ args.to_a
375
+ end
376
+ install flag, args
366
377
  end
367
378
  when :requirement, :target
368
379
  task flag, [:path] do |_, args|
@@ -419,6 +430,26 @@ module Squared
419
430
  end)
420
431
  end
421
432
  break if be
433
+ when 'py'
434
+ break unless windows?
435
+
436
+ case flag
437
+ when :exec
438
+ format_desc action, flag, 'opts*'
439
+ task flag do |_, args|
440
+ opts = args.to_a
441
+ if (n = opts.index { |val| val.start_with?('3.') }) && (opts[n] =~ SEM_VER)
442
+ opts[n] = $3.to_i >= 5 ? "3=.#{$3}.#{$5 || '0'}" : '3'
443
+ end
444
+ command = ENV['PY_EXEC'] || readline('Enter command', force: true)
445
+ py(flag, command, opts: opts)
446
+ end
447
+ else
448
+ format_desc(action, flag, 'opts*', after: flag == :list ? 'filter*' : 'tag*')
449
+ task flag do |_, args|
450
+ py(flag, opts: args.to_a, banner: true)
451
+ end
452
+ end
422
453
  end
423
454
  end
424
455
  end
@@ -751,10 +782,17 @@ module Squared
751
782
  run(from: symjoin(flag, 'publish'), interactive: ['Publish', 'N', project])
752
783
  end
753
784
 
754
- def python(*args, sync: true, banner: verbose?, with: nil, pass: PASS_PYTHON[:python], **kwargs)
755
- op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), OPT_PYTHON[:common],
756
- session('python', path: !venv?),
757
- project: self, strict: strict?, auto: %w[help V|version],
785
+ def python(*args, sync: true, banner: verbose?, with: nil, pass: PASS_PYTHON[:python], from: :python, **kwargs)
786
+ py = from == :py && windows?
787
+ list = OPT_PYTHON[:common]
788
+ if py
789
+ list += OPT_PY[:exec]
790
+ list.delete('V=+')
791
+ from = :'py:exec'
792
+ end
793
+ op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), list,
794
+ py ? session('py', 'exec') : session('python', path: !venv?),
795
+ project: self, strict: strict?, auto: %w[help V|version], sep: ' ',
758
796
  multiple: [/^-c/], single: singleopt(:python), args: true, stdin: true)
759
797
  op.concat(args)
760
798
  if op.include?('-')
@@ -768,8 +806,14 @@ module Squared
768
806
  op.append(escape: kwargs.fetch(:escape, false), quote: kwargs.fetch(:quote, false))
769
807
  end
770
808
  end
809
+ var = kwargs[:env]
810
+ if var == true
811
+ var = nil
812
+ elsif py && var.nil?
813
+ var = false
814
+ end
771
815
  print_run(op, banner, **kwargs) unless op.stdout
772
- run(sync: sync, banner: op.style?(banner), exception: exception?(**kwargs), from: :python)
816
+ run(op, var, sync: sync, banner: op.style?(banner), exception: exception?(**kwargs), from: from)
773
817
  end
774
818
 
775
819
  def pip(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -855,6 +899,28 @@ module Squared
855
899
  .then { |val| ret || val }
856
900
  end
857
901
 
902
+ def py(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
903
+ flag = flag.to_sym
904
+ if flag == :exec
905
+ pass = PASS_PYTHON[:python] if pass.nil?
906
+ python(*args, sync: sync, banner: banner, with: with, pass: pass, from: :py, **kwargs)
907
+ else
908
+ pass = PASS_PYTHON[:py] if pass.nil?
909
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
910
+ list = OPT_PY[:common] + OPT_PY[flag]
911
+ op = OptionPartition.new(opts, list, session('py', flag), project: self, strict: strict?,
912
+ single: singleopt(:py))
913
+ case flag
914
+ when :install, :uninstall
915
+ raise ArgumentError, message('no tags listed', hint: flag) if op.empty?
916
+ end
917
+ op << '-y' if option('y')
918
+ op.append
919
+ print_run(op, banner, **kwargs)
920
+ run(sync: sync, banner: banner, exception: exception?(**kwargs), from: symjoin('py', flag))
921
+ end
922
+ end
923
+
858
924
  def serve(root, opts = [], bind: 'localhost', port: nil, **)
859
925
  shell(python_session('-m http.server', port, basic_option('b', bind), opts: opts).first.done, chdir: root)
860
926
  end
@@ -956,7 +1022,8 @@ module Squared
956
1022
  pre = preopts(quiet: quiet)
957
1023
  return session(name, *pre, *cmd, path: !venv?) unless opts
958
1024
 
959
- op = OptionPartition.new(opts, common, project: self, strict: strict?, single: singleopt(name.to_sym))
1025
+ op = OptionPartition.new(opts, common, project: self, strict: strict?, sep: name == 'python' ? ' ' : '=',
1026
+ single: singleopt(name.to_sym))
960
1027
  [session(name, *pre, *op.to_a, *cmd, path: !venv?), op.extras]
961
1028
  end
962
1029
 
@@ -1131,6 +1198,8 @@ module Squared
1131
1198
  when :pdm, :poetry
1132
1199
  /\Av+\z/
1133
1200
  when :twine, :meson then nil
1201
+ when :py
1202
+ /\A(vv|qq)\z/
1134
1203
  else
1135
1204
  /\A(v+|q+)\z/
1136
1205
  end
@@ -1235,6 +1304,7 @@ module Squared
1235
1304
  status = true
1236
1305
  :virtualenv
1237
1306
  else
1307
+ status = !banner
1238
1308
  :venv
1239
1309
  end
1240
1310
  cmd, opts = python_session('-m', flag, opts: opts)
@@ -1242,34 +1312,31 @@ module Squared
1242
1312
  op.append(dir, delim: true)
1243
1313
  .clear(pass: false)
1244
1314
  status ||= op.arg?(/\A-v+\z/)
1245
- ret = run(op, env, exception: true, banner: banner)
1246
- args = []
1247
- requires = read_pyproject 'build-system', 'requires'
1248
- args.concat(requires) if requires.is_a?(Array)
1249
- if poetry?
1250
- requires = read_pyproject 'tool.poetry', 'requires-poetry'
1251
- args << "poetry#{requires}"
1252
- elsif setuptools? && !virtualenv?
1253
- args << 'setuptools' << 'wheel'
1254
- end
1255
- pip(:install, *args, banner: false) unless args.empty?
1256
- success?(ret, banner, !status) do |out|
1257
- if out && dir.directory?
1258
- if poetry?
1259
- dir += (windows? ? 'Scripts/python.exe' : 'bin/python')
1260
- cmd = poetry_session 'env use'
1261
- elsif pdm?
1262
- cmd = pdm_session 'use -f'
1263
- else
1264
- puts "Success: #{dir}"
1265
- return
1266
- end
1267
- cmd << shell_quote(dir)
1268
- print_run cmd, silent?
1269
- run(banner: false)
1315
+ if run(op, env, exception: true, banner: banner) && dir.directory?
1316
+ args = []
1317
+ requires = read_pyproject 'build-system', 'requires'
1318
+ args.concat(requires) if requires.is_a?(Array)
1319
+ if poetry?
1320
+ requires = read_pyproject 'tool.poetry', 'requires-poetry'
1321
+ args << "poetry#{requires}"
1322
+ elsif setuptools? && !virtualenv?
1323
+ args << 'setuptools' << 'wheel'
1324
+ end
1325
+ pip(:install, *args, banner: false) unless args.empty?
1326
+ if poetry?
1327
+ dir += (windows? ? 'Scripts/python.exe' : 'bin/python')
1328
+ poetry_session 'env use'
1329
+ elsif pdm?
1330
+ pdm_session 'use -f'
1270
1331
  else
1271
- puts 'Failed'
1332
+ puts "Success: #{dir}" unless status
1333
+ return
1272
1334
  end
1335
+ cmd << shell_quote(dir)
1336
+ print_run cmd, silent?
1337
+ run(banner: false)
1338
+ elsif !status
1339
+ puts 'Failed'
1273
1340
  end
1274
1341
  end
1275
1342
 
@@ -227,7 +227,7 @@ module Squared
227
227
  serve_set kwargs[:serve]
228
228
  gemfile_set kwargs[:gemspec]
229
229
  self.autodetect = autodetect
230
- @bundle = { exec: exec.is_a?(String) ? basepath!(exec) : exec }
230
+ @bundle = { exec: exec }
231
231
  @steepfile = basepath! steep if steep
232
232
  rubocop = rootpath('.rubocop.yml', ascend: Dir.home) if rubocop == true
233
233
  @rubocopfile = basepath! rubocop if rubocop
@@ -1601,7 +1601,7 @@ module Squared
1601
1601
 
1602
1602
  def rdbg(*args, banner: verbose?, with: nil, pass: PASS_RUBY[:rdbg], **kwargs)
1603
1603
  opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
1604
- cmd = session 'rdbg'
1604
+ cmd = session(*bundle_args('rdbg'))
1605
1605
  args.each_with_index do |val, i|
1606
1606
  break if val == '--'
1607
1607
  next unless exist?(val, type: 'f')
@@ -1881,7 +1881,7 @@ module Squared
1881
1881
  end
1882
1882
 
1883
1883
  def ruby_session(*cmd, **kwargs)
1884
- session('ruby', *preopts, *cmd, **kwargs)
1884
+ session(*bundle_args('ruby'), *preopts, *cmd, **kwargs)
1885
1885
  end
1886
1886
 
1887
1887
  def gem_session(*cmd, **kwargs)
@@ -1899,11 +1899,10 @@ module Squared
1899
1899
  end
1900
1900
 
1901
1901
  def rbs_session(*cmd, opts: nil)
1902
- args = bundle_args 'rbs'
1903
- return session(*args, *cmd) unless opts
1902
+ return session(*bundle_args('rbs'), *cmd) unless opts
1904
1903
 
1905
1904
  op = OptionPartition.new(opts, OPT_RUBY[:rbs], project: self, strict: strict?)
1906
- [session(*args, *op.to_a, *cmd), op.extras]
1905
+ [session(*bundle_args('rbs'), *op.to_a, *cmd), op.extras]
1907
1906
  end
1908
1907
 
1909
1908
  def gem_output(*cmd, **kwargs)
@@ -1911,7 +1910,7 @@ module Squared
1911
1910
  end
1912
1911
 
1913
1912
  def ruby_output(*cmd, **kwargs)
1914
- session_output('ruby', *cmd, **kwargs)
1913
+ session_output(*bundle_args('ruby'), *cmd, **kwargs)
1915
1914
  end
1916
1915
 
1917
1916
  def bundle_output(*cmd, **kwargs)
@@ -1924,7 +1923,20 @@ module Squared
1924
1923
 
1925
1924
  def bundle_args(*args)
1926
1925
  if (val = @bundle[:exec])
1927
- args.unshift(quote_option('gemfile', val)) unless val == true
1926
+ app = args.first
1927
+ if val.is_a?(Hash)
1928
+ env = val[app.to_s] || val[app.to_sym]
1929
+ return args unless env
1930
+
1931
+ val = env.is_a?(String) ? basepath!(env) : true
1932
+ elsif app == 'ruby'
1933
+ return args
1934
+ end
1935
+ if val.is_a?(String)
1936
+ return args unless (val = basepath!(val))
1937
+
1938
+ args.unshift("--gemfile=#{shell_escape(val.relative_path_from(path), quote: true)}")
1939
+ end
1928
1940
  args.unshift('bundle', 'exec')
1929
1941
  end
1930
1942
  args
@@ -248,7 +248,7 @@ module Squared
248
248
  end
249
249
 
250
250
  def parse(list, opts = extras, no: nil, single: nil, args: false, multiple: nil, first: nil, underscore: nil,
251
- explicit: false, bool: %w[true false], auto: [], stdin: false, &blk)
251
+ explicit: false, bool: %w[true false], auto: [], regexp: {}, enum: {}, stdin: false, &blk)
252
252
  @extras = []
253
253
  @values = []
254
254
  bare = []
@@ -265,8 +265,6 @@ module Squared
265
265
  bl = []
266
266
  ex = []
267
267
  ml = []
268
- re = {}
269
- ar = {}
270
268
  sw = {}
271
269
  se = {}
272
270
  list.flat_map do |val|
@@ -288,10 +286,10 @@ module Squared
288
286
  end
289
287
  if (n = val.index('='))
290
288
  flag = val[0, n]
291
- enum = flag.split(':')
292
- mod = if enum.size > 1
293
- pre = enum.shift
294
- enum.map { |s| "#{pre}:#{s}" }
289
+ opt = flag.split(':')
290
+ mod = if opt.size > 1
291
+ pre = opt.shift
292
+ opt.map { |s| "#{pre}:#{s}" }
295
293
  else
296
294
  [flag]
297
295
  end
@@ -326,14 +324,14 @@ module Squared
326
324
  pat = eval(val)
327
325
  raise SyntaxError unless pat.is_a?(Regexp)
328
326
 
329
- mod.each { |key| re[key] = pat }
327
+ mod.each { |key| regexp[key] = pat }
330
328
  rescue SyntaxError
331
329
  warn "#{self.class}: invalid pattern (#{val})"
332
330
  end
333
331
  when '{'
334
332
  group = val[(n + 2)..].chomp!('}')&.split(',')
335
333
  if group
336
- mod.each { |key| ar[key] = group }
334
+ mod.each { |key| enum[key] = group }
337
335
  else
338
336
  warn "#{self.class}: invalid group (#{val})"
339
337
  end
@@ -440,8 +438,8 @@ module Squared
440
438
  else
441
439
  push opt
442
440
  end
443
- elsif !ar.empty? && has.call(ar.keys, key)
444
- group = get.call(ar)
441
+ elsif !enum.empty? && has.call(enum.keys, key)
442
+ group = get.call(enum)
445
443
  if group.include?(val)
446
444
  add shell_option(key, val, force: false, **kwargs)
447
445
  elsif @strict
@@ -449,10 +447,10 @@ module Squared
449
447
  else
450
448
  push opt
451
449
  end
452
- elsif !re.empty? && has.call(re.keys, key)
453
- pat = get.call(re)
450
+ elsif !regexp.empty? && has.call(regexp.keys, key)
451
+ pat = get.call(regexp)
454
452
  if val.match?(pat)
455
- add quote_option(key, val, **kwargs)
453
+ add __send__(kwargs[:merge] ? :basic_option : :quote_option, key, val, **kwargs)
456
454
  elsif @strict
457
455
  clear([opt], hint: "match != /#{pat.source}/")
458
456
  else
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.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
142
  - !ruby/object:Gem::Version
143
143
  version: '0'
144
144
  requirements: []
145
- rubygems_version: 4.0.21
145
+ rubygems_version: 4.0.23
146
146
  specification_version: 4
147
147
  summary: Rake task generator for managing multi-language workspaces.
148
148
  test_files: []