squared 0.5.18 → 0.5.20

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: 7c64d1ba5b8781c76d0dbbccf6cbedf66e217043fbfd2bc33220a8c2d60baa82
4
- data.tar.gz: 0566e40b09f80693e1abbbf7e97de7bb92e952d5d2890be37340da34e0f24be3
3
+ metadata.gz: '0795a62a3426493dde89a22b6a8bcfe57e6c48aecf2dafc5ee661140654f25d6'
4
+ data.tar.gz: 0a0351323f82eab2fbfc164dcd4d5718bf66589e3213340822130cc11d99fa51
5
5
  SHA512:
6
- metadata.gz: 4ff0e0a44837f6fec6016680564f784968e0627367e727dfd30d1533f951f437b57ca746cc5876aa97ae05c11bc89ca79b149783e9082e15adc7d437e95c87e1
7
- data.tar.gz: ac233a17a56d35be1008b1e598d875fe173e85d412eafd13477536a469da1de9e06f16c245c02c5e568b8250a4a8bbf2693b9859fc3e3231ac43ff0a661371e6
6
+ metadata.gz: 7784fe33a1d668f126e6e0b3973d241046387f724b873ea7904e4f7acd6f05d83ce807b4271cc41e0ba9a840e2952242160fed0861e1d25c3178567f39dfaab4
7
+ data.tar.gz: 38b755e356c7593c60cc8a608fa542043bd31bba950decd92ace7cf8139bcdf906f16b5253493691d6786cfbbdb73ce2911517e771a0252433861d372af8280d
data/CHANGELOG.md CHANGED
@@ -1,10 +1,56 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.20] - 2025-12-26
4
+
5
+ ### Fixed
6
+
7
+ - Bundle config get method did not always discard newline.
8
+
9
+ ## [0.4.34] - 2025-12-26
10
+
11
+ ### Added
12
+
13
+ - Project public base method scope for nested tasks was created.
14
+ - Ruby task copy can autodetect "env" using [GEM_HOME|GEM_ROOT].
15
+
16
+ ### Changed
17
+
18
+ - Python virtual environment did not install poetry during initialization.
19
+
20
+ ### Fixed
21
+
22
+ - Workspace global banner never referenced the correct hash key.
23
+ - Python task depend without editable did not append context directory.
24
+ - Docker task build did not parse DOCKER_OPTIONS as command options.
25
+ - Project base method build did not call Method routines.
26
+ - Bundler autodetect did not check for valid gems directory.
27
+ - Ruby copy to version detection did not check for valid gemspec.
28
+
29
+ ## [0.5.19] - 2025-12-07
30
+
31
+ ### Fixed
32
+
33
+ - See `0.4.33`.
34
+
35
+ ## [0.4.33] - 2025-12-07
36
+
37
+ ### Added
38
+
39
+ - Ruby attribute setter gemdir for copy method was created.
40
+ - Project inline run executables binary path are replaced with global alias.
41
+
42
+ ### Fixed
43
+
44
+ - Workspace class Series created non-existent keys when queried.
45
+ - Application class did not support changing base Project class.
46
+ - Git command rev action build did not check build? method.
47
+ - Python command build action python did not use outdir option.
48
+
3
49
  ## [0.5.18] - 2025-11-25
4
50
 
5
51
  ### Fixed
6
52
 
7
- - See `0.5.18`.
53
+ - See `0.4.32`.
8
54
 
9
55
  ## [0.4.32] - 2025-11-25
10
56
 
@@ -1286,6 +1332,9 @@
1286
1332
 
1287
1333
  - Changelog was created.
1288
1334
 
1335
+ [0.5.20]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.20
1336
+ [0.5.19]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.19
1337
+ [0.5.18]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.18
1289
1338
  [0.5.17]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.17
1290
1339
  [0.5.16]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.16
1291
1340
  [0.5.15]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.15
@@ -1304,6 +1353,9 @@
1304
1353
  [0.5.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.2-ruby
1305
1354
  [0.5.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.1-ruby
1306
1355
  [0.5.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.5.0-ruby
1356
+ [0.4.34]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.34
1357
+ [0.4.33]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.33
1358
+ [0.4.32]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.32
1307
1359
  [0.4.31]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.31
1308
1360
  [0.4.30]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.30
1309
1361
  [0.4.29]: https://github.com/anpham6/squared-ruby/releases/tag/v0.4.29
data/README.md CHANGED
@@ -68,7 +68,7 @@ Workspace::Application
68
68
  .add("e-mc", "emc", copy: { from: "publish", scope: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node) # Node
69
69
  .add("pi-r", "pir", copy: { from: "publish", scope: "@pi-r" }, clean: ["publish/**/*.js", "tmp/"]) # Trailing slash required for directories
70
70
  .add("pi-r2", "pir2", copy: { from: :npm, also: %i[squared express], files: ["LICENSE", ["README.md.ruby", "README.md"]] }) # Uses dist files from NPM package spec
71
- .add("squared", init: 'pnpm', script: ["build:stage1", "build:stage2"], group: "app") do # Use pnpm/yarn/berry for depend + Copy target (main)
71
+ .add("squared", init: "pnpm", script: ["build:stage1", "build:stage2"], group: "app") do # Use pnpm/yarn/berry for depend + Copy target (main)
72
72
  # Repo (global)
73
73
  as(:run, "build:dev", "dev") # npm run build:dev -> npm run dev
74
74
  as(:run, { "build:dev": "dev", "build:prod": "prod" })
@@ -521,7 +521,7 @@ Commands which use commit hashes are parsed using a ":" prefix as to not be conf
521
521
 
522
522
  ```sh
523
523
  rake squared:log:view[:af012345] # git log af012345
524
- rake squared:log:view[H1,HEAD^5,all,lib,./H12345] # git log --all @~1 @^5 -- 'lib' 'H12345'
524
+ rake squared:log:view[H1,HEAD^5,all,lib,./H12345] # git log --all @~1 @^5 -- "lib" "H12345"
525
525
  ```
526
526
 
527
527
  ## Environment
@@ -676,7 +676,7 @@ DOCKER_ALL=1 # list every image/container
676
676
  DOCKER_Y=1 # confirm all
677
677
 
678
678
  BUILD_SQUARED_OPTS="NODE_TAG=24 RUBY_VERSION=3.4.0" DOCKER_SQUARED_OPTS="--no-cache --label=v1" rake squared:build
679
- docker build --no-cache --label=v1 --build-arg='NODE_TAG=24' --build-arg='RUBY_VERSION=3.4.0' .
679
+ docker build --no-cache --label=v1 --build-arg="NODE_TAG=24" --build-arg="RUBY_VERSION=3.4.0" .
680
680
  ```
681
681
 
682
682
  | Command | Flag | ENV |
@@ -65,8 +65,9 @@ module Squared
65
65
  while (ch = Readline.readline(msg))
66
66
  unless (ch = ch.strip).empty?
67
67
  if multiple
68
- a = ch.split(/\s*,\s*/)
68
+ a = ch.split(',')
69
69
  b = a.map do |s|
70
+ s.strip!
70
71
  if s =~ /^(\d+)-(\d+)$/
71
72
  next unless valid.call($1) && valid.call($2)
72
73
 
@@ -112,6 +112,7 @@ module Squared
112
112
  end
113
113
 
114
114
  def shell_bin(name, env: true)
115
+ require_relative 'base'
115
116
  key = name.to_s.upcase
116
117
  key = File.basename(key, '.*') if Rake::Win32.windows?
117
118
  shell_quote((env && ENV["PATH_#{key}"]) || PATH[key] || PATH[key.to_sym] || name,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.5.18'
4
+ VERSION = '0.5.20'
5
5
  end
@@ -274,9 +274,9 @@ module Squared
274
274
 
275
275
  def pass(name, group: @group, ref: @ref, &blk)
276
276
  data = if group
277
- @pass[:group][group]
277
+ @pass[:group][group.to_s]
278
278
  elsif ref
279
- @pass[:ref][ref]
279
+ @pass[:ref][ref.to_sym]
280
280
  else
281
281
  @pass[:global]
282
282
  end
@@ -335,9 +335,9 @@ module Squared
335
335
  end
336
336
  proj = ((if !ref.is_a?(Class)
337
337
  Application.find(ref, path: path)
338
- elsif ref < Project::Base
338
+ elsif ref < Application.impl_project
339
339
  ref
340
- end) || @kind[name]&.last || Project::Base).new(self, path, name, **kwargs)
340
+ end) || @kind[name]&.last || Application.impl_project).new(self, path, name, **kwargs)
341
341
  proj.__send__(:index_set, @project.size)
342
342
  @project[name] = proj
343
343
  __get__(:project)[name] = proj unless kwargs[:private]
@@ -580,7 +580,7 @@ module Squared
580
580
  return ret if group && (ret = @banner[:group][group.to_sym])
581
581
 
582
582
  ref.reverse_each { |val| return ret if (ret = @banner[:ref][val]) }
583
- @banner[:ref][:'']
583
+ @banner[:ref][:_]
584
584
  end
585
585
 
586
586
  def enabled?
@@ -403,7 +403,7 @@ module Squared
403
403
  .map(&:basename)
404
404
  .sort { |a, b| b <=> a }
405
405
  .push('latest', 'system'),
406
- force: true, accept: [['Confirm?', false, true]],
406
+ accept: [['Confirm?', false, true]],
407
407
  values: ['Options'])
408
408
  OptionPartition.strip(opts)
409
409
  end
@@ -503,8 +503,14 @@ module Squared
503
503
  cmd = []
504
504
  var = {}
505
505
  args.each do |val|
506
- next instance_exec(*val[1..-1], &val.first) if val.first.is_a?(Proc)
507
-
506
+ case val.first
507
+ when Proc
508
+ instance_exec(*val[1..-1], &val.first)
509
+ next
510
+ when Method
511
+ val.first.call(*val[1..-1])
512
+ next
513
+ end
508
514
  a, b, c, d, e = val
509
515
  case b
510
516
  when Hash
@@ -514,7 +520,7 @@ module Squared
514
520
  end
515
521
  d = append_hash(d, target: []).join(' ') if d.is_a?(Hash)
516
522
  if a
517
- cmd << [a, d, b].compact.join(' ')
523
+ cmd << [replace_bin(a), d, b].compact.join(' ')
518
524
  else
519
525
  next unless respond_to?(:compose)
520
526
 
@@ -528,7 +534,7 @@ module Squared
528
534
  if cmd
529
535
  return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
530
536
 
531
- cmd = as_get(cmd, from)
537
+ cmd = replace_bin as_get(cmd, from)
532
538
  opts = compose(opts, script: false) if opts && respond_to?(:compose)
533
539
  flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
534
540
  case opts
@@ -828,6 +834,7 @@ module Squared
828
834
  end
829
835
  cmd << name << version
830
836
  when :exec
837
+ cmd << name unless opts.first.start_with?(/#{name}\b/)
831
838
  cmd.merge(opts)
832
839
  when :current
833
840
  cmd << '--no-header' unless legacy
@@ -927,6 +934,12 @@ module Squared
927
934
  end
928
935
  end
929
936
 
937
+ def scope(*args, **kwargs, &blk)
938
+ namespace name do
939
+ task(*args, **kwargs, &blk)
940
+ end
941
+ end
942
+
930
943
  def variable_set(key, *args, **kwargs, &blk)
931
944
  if block_given?
932
945
  if blocks.include?(key)
@@ -968,7 +981,8 @@ module Squared
968
981
  when :env
969
982
  run_set(output[0], *args, **kwargs)
970
983
  when :dependfile
971
- @dependfile = basepath(*args)
984
+ @dependindex = nil
985
+ @dependfile = val.nil? ? nil : basepath(*args)
972
986
  else
973
987
  instance_variable_set(:"@#{key}", val)
974
988
  end
@@ -1662,6 +1676,13 @@ module Squared
1662
1676
  ret
1663
1677
  end
1664
1678
 
1679
+ def replace_bin(val)
1680
+ a, b = val.split(' ', 2)
1681
+ return val if val.start_with?(/["']/) || a.include?(File::Separator)
1682
+
1683
+ [shell_bin(a), b].compact.join(' ')
1684
+ end
1685
+
1665
1686
  def parse_json(val, kind: Hash, hint: nil)
1666
1687
  ret = JSON.parse(val)
1667
1688
  raise_error("invalid JSON #{kind.name}", val, hint: hint) if kind && !ret.is_a?(kind)
@@ -13,7 +13,7 @@ module Squared
13
13
  buildx: {
14
14
  common: %w[builder=b D|debug],
15
15
  build: %w[add-host=q annotation=q attest=q build-arg=qq build-context=qq cache-from=q cache-to=q
16
- cgroup-parent=b iidfile=p label=q a-file=p network=b no-cache-filter=b o|output=q platform=b
16
+ cgroup-parent=b iidfile=p label=q network=b no-cache-filter=b o|output=q platform=q
17
17
  q|quiet secret=qq shm-size=b ssh=qq t|tag=b target=b ulimit=q].freeze,
18
18
  bake: %w[print list=q set=q].freeze,
19
19
  shared: %w[check load no-cache pull push allow=q call=b? f|file=p metadata-file=p progress=b provenance=q
@@ -41,14 +41,14 @@ module Squared
41
41
  device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
42
42
  disable-content-trust=b? dns=q dns-option=q dns-search=q domainname=b entrypoint=q e|env=qq
43
43
  env-file=p expose=q gpus=q group-add=b health-cmd=q health-interval=b health-retries=i
44
- health-start-interval=q health-start-period=q health-timeout=q io-maxbandwidth=b io-maxiops=b
45
- ip=b ip6=q ipc=b isolation=b kernel-memory=b l|label=q label-file=q link=b link-local-ip=q
46
- log-driver=b log-opt=q mac-address=q m|memory=b memory-reservation=b memory-swap=n
47
- memory-swappiness=n mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b pids-limit=n
48
- platform=b p|publish=q pull=b restart=b runtime=b security-opt=q shm-size=b stop-signal=b
49
- stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b v|volume=q
50
- volume-driver=b volumes-from=b w|workdir=q].freeze,
51
- run: %w[d|detach detach-keys=q hostname=q sig-proxy=b?].freeze,
44
+ health-start-interval=q health-start-period=q health-timeout=q hostname=q io-maxbandwidth=b
45
+ io-maxiops=b ip=b ip6=q ipc=b isolation=b kernel-memory=b l|label=q label-file=q link=b
46
+ link-local-ip=q log-driver=b log-opt=q mac-address=q m|memory=b memory-reservation=b
47
+ memory-swap=n memory-swappiness=n mount=qq name=b network=b network-alias=b oom-score-adj=b
48
+ pid=b pids-limit=n platform=q p|publish=q pull=b restart=b runtime=b security-opt=q shm-size=b
49
+ stop-signal=b stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b
50
+ v|volume=q volume-driver=b volumes-from=b w|workdir=q].freeze,
51
+ run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
52
52
  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
53
53
  cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=b pids-limit=n
54
54
  restart=q].freeze,
@@ -64,9 +64,9 @@ module Squared
64
64
  }.freeze,
65
65
  image: {
66
66
  list: %w[a|all q|quiet digests no-trunc tree f|filter=q format=q].freeze,
67
- push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
68
- rm: %w[f|force no-prune platform=b].freeze,
69
- save: %w[o|output=p platform=b].freeze
67
+ push: %w[a|all-tags disable-content-trust=b? platform=q q|quiet].freeze,
68
+ rm: %w[f|force no-prune platform=q].freeze,
69
+ save: %w[o|output=p platform=q].freeze
70
70
  }.freeze,
71
71
  network: {
72
72
  connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=q link=b link-local-ip=q].freeze,
@@ -262,21 +262,20 @@ module Squared
262
262
  def compose(opts, flags = nil, script: false, args: nil, from: :run, **)
263
263
  return opts if script == false
264
264
 
265
- ret = docker_session
266
265
  if from == :run
267
266
  if bake?(n = filetype)
268
- ret << 'buildx bake'
267
+ ret = docker_session 'buildx bake'
269
268
  append_file n
270
269
  from = :bake
271
270
  elsif compose?(n)
272
- ret << 'compose build'
271
+ ret = docker_session 'compose build'
273
272
  append_file n
274
273
  from = :compose
275
274
  else
276
- ret << 'build'
275
+ ret = docker_session 'build'
277
276
  end
278
277
  else
279
- ret << from
278
+ ret = docker_session from
280
279
  end
281
280
  case opts
282
281
  when String
@@ -864,6 +864,8 @@ module Squared
864
864
  rev_parse(flag, ref: ref, size: size)
865
865
  end
866
866
  when :build
867
+ next unless build?
868
+
867
869
  format_desc action, flag, 'opts*'
868
870
  task flag do |_, args|
869
871
  revbuild flag, args.to_a
@@ -1122,7 +1124,7 @@ module Squared
1122
1124
  end
1123
1125
  end
1124
1126
  out = choice_index('Choose a stash', git_spawn('stash list', stdout: false),
1125
- values: values, column: /^[^@]+@\{(\d+)\}/, force: true)
1127
+ values: values, column: /^[^@]+@\{(\d+)\}/)
1126
1128
  if values
1127
1129
  op.merge(out.reverse)
1128
1130
  else
@@ -1771,7 +1773,7 @@ module Squared
1771
1773
  files << "#{sub_style(b, styles: color(:red))} #{a}"
1772
1774
  end
1773
1775
  unless files.empty?
1774
- files = choice_index('Select files', files, multiple: true, force: true, trim: /^\S+\s/,
1776
+ files = choice_index('Select files', files, multiple: true, trim: /^\S+\s/,
1775
1777
  accept: [['Add?', false, true]])
1776
1778
  end
1777
1779
  op.swap(list + files)
@@ -1838,7 +1840,7 @@ module Squared
1838
1840
  return args ? [IO.popen(cmd), banner || '', from] : IO.popen(cmd)
1839
1841
  elsif stdin? ? sync : stdout
1840
1842
  print_item banner unless multiple
1841
- ret = `#{cmd}`
1843
+ ret = `#{cmd}`.chomp
1842
1844
  if !ret.empty?
1843
1845
  puts ret
1844
1846
  elsif success?(!banner.nil?)
@@ -27,7 +27,8 @@ module Squared
27
27
  use-running-store-server use-store-server child-concurrency=i hoist-pattern=q lockfile-dir=p
28
28
  modules-dir=p network-concurrency=i package-import-method=b public-hoist-pattern=q
29
29
  reporter=b].freeze,
30
- install_base: %w[global-dir ignore-scripts offline prefer-offline store-dir=p virtual-store-dir=p].freeze,
30
+ install_base: %w[dangerously-allow-all-builds global-dir ignore-scripts offline prefer-offline store-dir=p
31
+ virtual-store-dir=p].freeze,
31
32
  install_no: %w[frozen-lockfile verify-store-integrity].freeze,
32
33
  install_as: %w[D|dev no-optional P|prod].freeze,
33
34
  update: %w[g|global i|interactive L|latest depth=i].freeze,
@@ -456,6 +457,7 @@ module Squared
456
457
  split_escape(val).each { |opt| cmd << shell_option('public-hoist-pattern', opt) }
457
458
  end
458
459
  cmd << '--ignore-workspace' if env('NODE_WORKSPACES', equals: '0')
460
+ cmd << '--dangerously-allow-all-builds' if option('approve-builds')
459
461
  append_nocolor
460
462
  else
461
463
  cmd = session 'npm', 'install'
@@ -30,15 +30,15 @@ module Squared
30
30
  OPT_POETRY = {
31
31
  common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet v|verbose P|project=p].freeze,
32
32
  build: %w[clean config-settings=qq f|format=b o|output=p].freeze,
33
- publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=b r|repository=q
34
- u|username=b].freeze
33
+ publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=q r|repository=q
34
+ u|username=qq].freeze
35
35
  }.freeze
36
36
  OPT_PDM = {
37
37
  common: %w[I|ignore-python no-cache n|non-interactive].freeze,
38
38
  build: %w[C=bm no-clean no-isolation no-sdist no-wheel quiet verbose config-setting=q d|dest=p p|project=p
39
39
  k|skip=b].freeze,
40
40
  publish: %w[no-build no-very-ssl quiet S|sign skip-existing verbose ca-certs=p c|comment=q d|dest=p
41
- i|identity=b P|password=q p|project=p r|repository=q k|skip=b u|username=b].freeze
41
+ i|identity=b P|password=q p|project=p r|repository=q k|skip=b u|username=qq].freeze
42
42
  }.freeze
43
43
  OPT_HATCH = {
44
44
  common: %w[color interactive no-color no-interactive cache-dir=p config=p data-dir=p e|env=b p|project=b
@@ -50,7 +50,7 @@ module Squared
50
50
  OPT_TWINE = {
51
51
  publish: %w[attestations disable-progress-bar non-interactive s|sign skip-existing verbose cert=p
52
52
  client-cert=p c|comment=q config-file=p i|identity=b p|password=q r|repository=b repository-url=q
53
- sign-with=b u|username=q].freeze
53
+ sign-with=b u|username=qq].freeze
54
54
  }.freeze
55
55
  private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_PDM, :OPT_HATCH, :OPT_TWINE
56
56
 
@@ -78,7 +78,7 @@ module Squared
78
78
 
79
79
  attr_reader :venv, :editable
80
80
 
81
- def initialize(*, editable: '.', verbose: nil, asdf: 'python', **kwargs)
81
+ def initialize(*, editable: '.', asdf: 'python', **kwargs)
82
82
  super
83
83
  if @pass.include?(Python.ref)
84
84
  initialize_ref Python.ref
@@ -310,10 +310,10 @@ module Squared
310
310
  next unless build_backend == 'hatchling.build'
311
311
  end
312
312
  format_desc(action, flag, 'opts*', after: case flag
313
- when :python then 'srcdir?'
314
313
  when :poetry then 'output?'
315
314
  when :pdm then 'dest?'
316
315
  when :hatch then 'location?'
316
+ else 'outdir?'
317
317
  end)
318
318
  task flag do |_, args|
319
319
  build! flag, args.to_a
@@ -352,6 +352,7 @@ module Squared
352
352
  cmd << '--no-root' if option('no-root')
353
353
  else
354
354
  cmd = pip_session 'install'
355
+ cmd << '--upgrade-strategy=eager' if env('PYTHON_UPDATE')
355
356
  if flag
356
357
  case flag
357
358
  when :user
@@ -487,9 +488,6 @@ module Squared
487
488
 
488
489
  def build!(flag, opts = [])
489
490
  case flag
490
- when :python
491
- cmd, opts = python_session('-m build', opts: opts)
492
- list = OPT_PYTHON[:build]
493
491
  when :poetry
494
492
  cmd = poetry_session 'build'
495
493
  list = OPT_POETRY[:build] + OPT_POETRY[:common]
@@ -499,37 +497,31 @@ module Squared
499
497
  when :hatch
500
498
  cmd, opts = hatch_session('build', opts: opts)
501
499
  list = OPT_HATCH[:build]
500
+ else
501
+ cmd, opts = python_session('-m build', opts: opts)
502
+ list = OPT_PYTHON[:build]
502
503
  end
503
- srcdir = nil
504
504
  op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
505
- op.each do |opt|
506
- if !srcdir && basepath(opt.chomp('*')).exist? && projectpath?(opt.chomp('*'))
507
- srcdir = opt
508
- else
509
- op.found << opt
510
- end
511
- end
512
- op.swap
513
505
  case flag
514
- when :poetry, :pdm
515
- if srcdir
516
- args = flag == :pdm ? ['d', 'dest'] : ['o', 'output']
517
- if op.arg?(*args)
518
- op.push(srcdir)
519
- else
520
- op << quote_option(args.last, basepath(srcdir))
521
- end
522
- srcdir = nil
523
- end
524
506
  when :hatch
525
- if ENV['HATCH_BUILD_LOCATION']
526
- srcdir = nil
527
- else
528
- srcdir ||= path
507
+ if !ENV['HATCH_BUILD_LOCATION'] && (outdir ||= op.shift)
508
+ op.add_path(outdir)
509
+ end
510
+ else
511
+ unless op.empty?
512
+ args = case flag
513
+ when :poetry
514
+ %w[o output]
515
+ when :pdm
516
+ %w[d dest]
517
+ else
518
+ srcdir = true
519
+ %w[o outdir]
520
+ end
521
+ op << quote_option(args.last, basepath(op.shift)) unless op.arg?(*args)
529
522
  end
530
- op << basic_option('p', project) unless ENV['HATCH_PROJECT'] || op.arg?('p', 'project')
531
523
  end
532
- op.add_path(srcdir) if srcdir
524
+ op.exist?(add: true, first: true) if srcdir
533
525
  op.clear
534
526
  run(from: :"#{flag}:build")
535
527
  end
@@ -603,17 +595,21 @@ module Squared
603
595
  end
604
596
  case key
605
597
  when :dependfile
606
- req = basepath(*val)
607
- if (index = DEP_PYTHON.index(req.basename.to_s))
608
- @dependindex = index
609
- @dependfile = req
598
+ if val.first.nil?
599
+ super
610
600
  else
611
- log.warn "variable_set: @#{key}=#{req} (not supported)"
601
+ req = basepath(*val)
602
+ if (index = DEP_PYTHON.index(req.basename.to_s))
603
+ @dependindex = index
604
+ @dependfile = req
605
+ else
606
+ log.warn "variable_set: @#{key}=#{req} (not supported)"
607
+ end
612
608
  end
613
609
  when :editable
614
610
  editable_set val.first
615
611
  when :venv
616
- instance_variable_set(:"@#{key}", val.empty? ? nil : basepath(*val))
612
+ @venv = val.empty? || val.first.nil? ? nil : basepath(*val)
617
613
  else
618
614
  super
619
615
  end
@@ -634,10 +630,11 @@ module Squared
634
630
  end
635
631
 
636
632
  def python_session(*cmd, opts: nil)
637
- return session('python', *preopts(quiet: false), *cmd, path: venv.nil?) unless opts
633
+ pre = preopts(quiet: false)
634
+ return session('python', *pre, *cmd, path: venv.nil?) unless opts
638
635
 
639
636
  op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: singleopt(:python))
640
- ret = session('python', *op.to_a, *cmd, path: venv.nil?)
637
+ ret = session('python', *pre, *op.to_a, *cmd, path: venv.nil?)
641
638
  [ret, op.extras]
642
639
  end
643
640
 
@@ -658,8 +655,8 @@ module Squared
658
655
  def create_session(*cmd, name:, common:, opts: nil)
659
656
  return session(name, *preopts, *cmd, path: venv.nil?) unless opts
660
657
 
661
- op = OptionPartition.new(opts, common, project: self, single: singleopt)
662
- ret = session(name, *op.to_a, *cmd, path: venv.nil?)
658
+ op = OptionPartition.new(opts, common, project: self, single: singleopt(name.to_sym))
659
+ ret = session(name, *preopts, *op.to_a, *cmd, path: venv.nil?)
663
660
  [ret, op.extras]
664
661
  end
665
662
 
@@ -714,14 +711,16 @@ module Squared
714
711
  OptionPartition.delete_key(target, 'e', 'editable')
715
712
  case val
716
713
  when '0', 'false'
717
- return
714
+ return unless installable?
718
715
  else
719
716
  val = basepath val
720
717
  end
721
- elsif session_arg?('e', 'editable', target: target) || !(val = editable)
718
+ elsif session_arg?('e', 'editable', target: target) || !installable?
722
719
  return
720
+ else
721
+ val = editable
723
722
  end
724
- target << quote_option('e', basepath(val))
723
+ target << (val ? quote_option('e', basepath(val)) : '.')
725
724
  end
726
725
 
727
726
  def append_global(target: @session)
@@ -897,6 +896,7 @@ module Squared
897
896
  .clear(pass: false)
898
897
  .arg?(/\A-v+\z/)
899
898
  run(op, env, exception: true, banner: banner)
899
+ install(:upgrade, ['poetry']) if poetry?
900
900
  puts(dir.directory? ? "Success: #{dir}" : 'Failed') if banner && !status
901
901
  end
902
902
 
@@ -24,10 +24,10 @@ module Squared
24
24
  common: %w[no-color V|verbose r|retry=i].freeze,
25
25
  install: %w[frozen no-cache no-prune system binstubs=p? path=p standalone=q? target-rbconfig=p trust-policy=b
26
26
  with=q without=q].freeze,
27
- install_base: %w[force full-index quiet redownload gemfile=p j|jobs=i].freeze,
27
+ install_base: %w[force full-index local quiet redownload gemfile=p j|jobs=i].freeze,
28
28
  update: %w[all conservative local major minor patch pre ruby strict bundler=b? g|group=q source=b].freeze,
29
- outdated: %w[filter-major filter-minor filter-patch groups local parseable pre only-explicit strict
30
- update-strict group=q source=b].freeze,
29
+ outdated: %w[filter-major filter-minor filter-patch filter-strict groups local parseable porcelain pre
30
+ only-explicit strict update-strict group=q source=b].freeze,
31
31
  exec: %w[gemfile=p].freeze,
32
32
  cache: %w[all all-platforms frozen no-all no-install no-prune quiet cache-path=p gemfile=p path=p].freeze,
33
33
  check: %w[dry-run gemfile=p path=p].freeze
@@ -38,7 +38,7 @@ module Squared
38
38
  install_base: %w[E f w b|both clear-sources conservative default development development-all explain
39
39
  ignore-dependencies l|local N|no-document r|remote vendor n|bindir=p build-root=p
40
40
  B|bulk-threshold=i document=b? g|file=p? p|http-proxy=q? i|install-dir=p platform=q
41
- s|source=q target-rbconfig=p? P|trust-policy=b without=b].freeze,
41
+ s|source=q target-rbconfig=p? P|trust-policy=b without=q].freeze,
42
42
  update: %w[system=b?].freeze,
43
43
  uninstall: %w[a D I x vendor n|bindir=p i|install-dir=p platform=b v|version=q].freeze,
44
44
  outdated: %w[b|both clear-sources l|local r|remote B|bulk-threshold=i p|http-proxy=q? platform=q
@@ -69,7 +69,7 @@ module Squared
69
69
  end
70
70
 
71
71
  def bannerargs
72
- %i[dependfile gemname].freeze
72
+ %i[dependfile gemname gemdir].freeze
73
73
  end
74
74
 
75
75
  def config?(val)
@@ -93,6 +93,8 @@ module Squared
93
93
  'irb' => nil
94
94
  })
95
95
 
96
+ attr_reader :gemdir
97
+
96
98
  def initialize(*, autodetect: false, gemspec: nil, asdf: 'ruby', **kwargs)
97
99
  super
98
100
  if @pass.include?(Ruby.ref)
@@ -126,6 +128,14 @@ module Squared
126
128
  end
127
129
  end
128
130
 
131
+ def gemdir=(val)
132
+ @gemdir = if val.is_a?(Pathname)
133
+ val
134
+ else
135
+ Pathname.new(val).realdirpath rescue nil
136
+ end
137
+ end
138
+
129
139
  def ref
130
140
  Ruby.ref
131
141
  end
@@ -241,7 +251,7 @@ module Squared
241
251
  else
242
252
  a, b, c = choice_index('Select a file', Dir.glob(file || '*.rb', base: path),
243
253
  values: (file ? [] : ['Options']).push('Arguments'),
244
- force: true, series: true)
254
+ series: true)
245
255
  if file
246
256
  file = a
247
257
  b
@@ -293,7 +303,7 @@ module Squared
293
303
  end
294
304
  end
295
305
 
296
- def copy(from: gemlib, into: @gemdir, override: false, **kwargs)
306
+ def copy(from: gemlib, into: gemdir, override: false, **kwargs)
297
307
  return if @copy == false
298
308
 
299
309
  glob = kwargs[:include]
@@ -754,7 +764,7 @@ module Squared
754
764
  file = basepath(if !n && (spec = gemspec)
755
765
  "#{spec.name}-#{spec.version}.gem"
756
766
  else
757
- choice_index('Select a file', Dir.glob('*.gem', base: path), force: true)
767
+ choice_index 'Select a file', Dir.glob('*.gem', base: path)
758
768
  end)
759
769
  else
760
770
  file = op.shift.yield_self { |val| val.include?('.') ? val : "#{val}.gem" }
@@ -882,62 +892,63 @@ module Squared
882
892
 
883
893
  def copy?
884
894
  return true if @copy.is_a?(Hash) ? copy[:into] : super
885
- return gemdir? if @gemdir
895
+ return gemdir? if gemdir
886
896
 
887
897
  if version
888
898
  begin
889
899
  case @autodetect
890
900
  when 'rvm'
891
- @gemdir = pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
901
+ self.gemdir = pwd_set { `rvm info homes` }[/^\s+gem:\s+"(.+)"$/, 1]
892
902
  when 'rbenv'
893
903
  if pwd_set { `rbenv which ruby` } =~ %r{^(.+[\\/]versions[\\/](\d\.\d)\.[^\\/]+)[\\/]bin[\\/]ruby$}
894
- @gemdir = File.join($1, 'lib/ruby/gems', "#{$2}.0")
904
+ self.gemdir = File.join($1, 'lib/ruby/gems', "#{$2}.0")
895
905
  end
896
906
  when 'asdf'
897
- @gemdir = pwd_set { `asdf where ruby` }
898
- @gemdir = @gemdir =~ /(\d\.\d)\.[^.]+$/ && File.join(@gemdir, 'lib/ruby/gems', "#{$1}.0")
907
+ val = pwd_set { `asdf where ruby` }
908
+ self.gemdir = File.join(val, 'lib/ruby/gems', "#{$1}.0") if val =~ /(\d\.\d)\.[^.]+$/
909
+ when 'env'
910
+ ENV['GEM_HOME'] || ENV['GEM_ROOT']
899
911
  when /bundler?/
900
- @gemdir = pwd_set { `bundle env` }[/^\s+Gem Home\s+(.+)$/, 1]
912
+ path = pwd_set { `bundle env` }[/^\s+Gem Path\s+(.+)$/, 1]
913
+ self.gemdir = path.split(File::PATH_SEPARATOR).find { |val| Dir.exist?(val) }
901
914
  end
902
915
  rescue StandardError => e
903
916
  log.debug e
904
- else
905
- @gemdir = Pathname.new(@gemdir) if @gemdir
906
917
  end
907
918
  return true if gemdir?
908
919
  end
909
920
  return false unless @autodetect
910
921
 
911
922
  set = lambda do |val, path|
912
- if (ver = version) && ver != val
913
- log.warn "using version #{val} (given #{ver})"
914
- end
923
+ base = Pathname.new(path.strip)
924
+ return false unless base.join(gempath(val, 'specification')).exist?
925
+
926
+ log.warn "using version #{val} (given #{version})" if version && version != val
915
927
  self.version = val
916
- @gemdir = Pathname.new(path.strip) + gempath
928
+ self.gemdir = base + gempath
917
929
  end
918
930
  if version
919
931
  opt = gempwd
920
932
  pwd_set(pass: !opt.nil?) do
921
933
  out = `#{gem_output(opt, 'list --local -d', gemname)}`
922
- if out =~ /#{Regexp.escape(gemname)} \(([^)]+)\)/
934
+ if out =~ /#{Regexp.escape(gemname)}\s+\((.+)\)$/
923
935
  split_escape($1)
924
936
  .unshift(version)
925
937
  .uniq
926
938
  .each do |val|
927
- next unless out =~ /\(#{Regexp.escape(val)}(?:,[^)]+|\b)\):([^\n]+)/
939
+ next unless out =~ /(?:\(#{Regexp.escape(val)}[^)]*\)|Installed at):\s+(.+)$/
928
940
 
929
- set.call(val, $1)
930
- return gemdir? if @gemdir
941
+ return gemdir? if set.call(val, $1)
931
942
  end
932
943
  end
933
944
  end
934
- @gemdir = Pathname.new(Gem.dir) + gempath
945
+ self.gemdir = Pathname.new(Gem.dir) + gempath
935
946
  else
936
947
  parse = lambda do |path|
937
948
  next unless path
938
949
 
939
950
  lib = Regexp.new(['', 'gems', "#{gemname}-([^#{File::SEPARATOR}]+)", ''].join(File::SEPARATOR))
940
- if (ver = path[lib, 1]) && (val = path[/\A(.+)#{gempath(ver[1])}/, 1])
951
+ if (ver = path[lib, 1]) && (val = path[/\A(.+)#{Regexp.escape(gempath(ver))}/, 1])
941
952
  set.call(ver, val)
942
953
  end
943
954
  end
@@ -945,7 +956,7 @@ module Squared
945
956
  target = RUBY_VERSION.start_with?('2.6') ? RubyVM : $LOAD_PATH
946
957
  parse.call(target.resolve_feature_path(gemname)&.last)
947
958
  end
948
- if !@gemdir && !pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
959
+ if !gemdir && !pwd_set { parse.call(`#{bundle_output('show', gemname)}`) }
949
960
  raise_error 'gems directory not found'
950
961
  end
951
962
  end
@@ -1021,19 +1032,19 @@ module Squared
1021
1032
  end
1022
1033
 
1023
1034
  def config_get(key)
1024
- ret = if pwd_set { `#{bundle_output('config get --parseable', key)}` } =~ /\A([^=]+)=(.*)\z/ && $1 == key
1025
- $2.chomp
1026
- end
1027
- case ret
1035
+ out = pwd_set { `#{bundle_output('config get --parseable', key)}`.chomp }
1036
+ return unless out =~ /\A([^=]+)=(.*)\z/ && $1 == key
1037
+
1038
+ case (out = $2)
1028
1039
  when 'true'
1029
1040
  true
1030
1041
  when '', '[]'
1031
1042
  nil
1032
1043
  else
1033
- if ret =~ /\A\[:(.+)\]\z/
1034
- $1.split(', :').map! { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1044
+ if out =~ /\A\[:(.+)\]\z/
1045
+ $1.split(', :').map { |val| ((val.delete_prefix!('"') && val.delete_suffix!('"')) || val).to_sym }
1035
1046
  else
1036
- ret || false
1047
+ out || false
1037
1048
  end
1038
1049
  end
1039
1050
  end
@@ -1101,14 +1112,16 @@ module Squared
1101
1112
  end
1102
1113
  end
1103
1114
 
1104
- def gempath(val = version)
1105
- File.join('gems', "#{gemname}-#{val}")
1115
+ def gempath(val = version, dir = 'gems')
1116
+ ret = File.join(dir, "#{gemname}-#{val}")
1117
+ ret += '.gemspec' if dir == 'specifications'
1118
+ ret
1106
1119
  end
1107
1120
 
1108
1121
  def gemdir?
1109
- return false unless @gemdir
1122
+ return false unless gemdir
1110
1123
 
1111
- @gemdir.exist? && !@gemdir.empty?
1124
+ gemdir.exist? && !gemdir.empty?
1112
1125
  end
1113
1126
  end
1114
1127
 
@@ -114,7 +114,7 @@ module Squared
114
114
 
115
115
  def matchopts(list, value = false)
116
116
  a, b = Array(list).partition { |val| val.size == 1 || val.match?(OPT_SINGLE) }
117
- return /\A#{shortopt(*a)}}/ if b.empty?
117
+ return /\A#{shortopt(*a)}/ if b.empty?
118
118
  return /\A#{longopt(*b, value)}/ if a.empty?
119
119
 
120
120
  /\A(?:#{shortopt(*a)}|#{longopt(*b, value)})/
@@ -135,9 +135,9 @@ module Squared
135
135
 
136
136
  def_delegators :@target, :+, :-, :<<, :any?, :none?, :include?, :add, :add?, :find, :find_all, :find_index,
137
137
  :merge, :compact, :delete, :delete?, :delete_if, :grep, :grep_v, :inspect, :to_a, :to_s
138
- def_delegators :@extras, :empty?, :each, :each_with_index, :partition, :dup, :first, :last, :shift, :unshift,
139
- :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select, :select!, :reject, :size,
140
- :delete_at
138
+ def_delegators :@extras, :empty?, :member?, :each, :each_with_index, :each_with_object, :partition, :dup,
139
+ :first, :shift, :unshift, :pop, :push, :concat, :index, :join, :map, :map!, :detect, :select,
140
+ :select!, :reject, :size
141
141
 
142
142
  def_delegator :@extras, :delete, :remove
143
143
  def_delegator :@extras, :delete_at, :remove_at
@@ -309,6 +309,7 @@ module Squared
309
309
  else
310
310
  add val
311
311
  end
312
+ found << val if args.empty?
312
313
  end
313
314
  self
314
315
  end
@@ -437,26 +438,26 @@ module Squared
437
438
  end
438
439
 
439
440
  def splice(*exclude, quote: true, delim: true, path: false, pattern: false, &blk)
440
- found, other = if block_given?
441
- partition(&blk)
442
- elsif exclude.first.is_a?(Symbol)
443
- partition(&exclude.first)
444
- else
445
- partition do |val|
446
- next false if pattern && OptionPartition.pattern?(val)
447
-
448
- exclude.none? { |pat| val.match?(Regexp.new(pat)) }
449
- end
450
- end
451
- unless found.empty?
441
+ temp, other = if block_given?
442
+ partition(&blk)
443
+ elsif exclude.first.is_a?(Symbol)
444
+ partition(&exclude.first)
445
+ else
446
+ partition do |val|
447
+ next false if pattern && OptionPartition.pattern?(val)
448
+
449
+ exclude.none? { |pat| val.match?(Regexp.new(pat)) }
450
+ end
451
+ end
452
+ unless temp.empty?
452
453
  add '--' if delim
453
454
  extras.clear
454
455
  concat other
455
456
  if path
456
- found.each { |val| add_path(val) }
457
+ temp.each { |val| add_path(val) }
457
458
  else
458
- found.map! { |val| shell_quote(val) } if quote
459
- merge found
459
+ temp.map! { |val| shell_quote(val) } if quote
460
+ merge temp
460
461
  end
461
462
  end
462
463
  self
@@ -229,9 +229,8 @@ module Squared
229
229
 
230
230
  path = sub_style(root, styles: theme[:inline])
231
231
  @repo_override = Common::Prompt.confirm(
232
- "#{log_title(:warn)} \"#{path}\" is not empty. Continue with installation?",
233
- 'N',
234
- timeout: env('REPO_TIMEOUT', 15, ignore: '0')
232
+ "#{log_title(:warn)} \"#{path}\" is not empty. Continue with installation?", 'N',
233
+ timeout: env('REPO_TIMEOUT').to_i.yield_self { |n| n > 0 ? n : 15 }
235
234
  )
236
235
  end
237
236
 
@@ -31,15 +31,19 @@ module Squared
31
31
  end
32
32
  elsif (data = TASK_BATCH[obj])
33
33
  args.each { |ref| data.delete(ref) }
34
- TASK_KEYS.delete(obj) if data.empty?
34
+ if data.empty?
35
+ TASK_KEYS.delete(obj)
36
+ TASK_BATCH.delete(obj)
37
+ end
35
38
  end
36
39
  end
37
40
 
38
41
  def alias(ref, obj)
39
42
  if obj.is_a?(Hash)
40
43
  obj.each { |key, val| TASK_ALIAS[key][ref] = val }
41
- else
42
- TASK_ALIAS[obj]&.delete(ref)
44
+ elsif TASK_ALIAS.key?(obj)
45
+ TASK_ALIAS[obj].delete(ref)
46
+ TASK_ALIAS.delete(obj) if TASK_ALIAS[obj].empty?
43
47
  end
44
48
  end
45
49
 
@@ -165,6 +169,8 @@ module Squared
165
169
  end
166
170
 
167
171
  def alias_get(key)
172
+ return unless TASK_ALIAS.key?(key)
173
+
168
174
  TASK_ALIAS[key]
169
175
  end
170
176
 
@@ -182,12 +188,14 @@ module Squared
182
188
  end
183
189
 
184
190
  def extend?(obj, key)
185
- return false unless (items = TASK_EXTEND[key]) && !(items = items.select { |kind| obj.is_a?(kind) }).empty?
191
+ return false unless TASK_EXTEND.key?(key)
186
192
 
187
193
  meth = :"#{key}?"
188
194
  ret = false
189
- items.each do |kind|
190
- if kind.instance_methods.include?(meth)
195
+ TASK_EXTEND[key].each do |kind|
196
+ next unless obj.is_a?(kind)
197
+
198
+ if kind.method_defined?(meth)
191
199
  out = obj.__send__(meth)
192
200
  return true if out == 1
193
201
  return out if obj.ref?(kind.ref)
@@ -228,7 +236,7 @@ module Squared
228
236
  end
229
237
 
230
238
  def exclude?(key, empty = false)
231
- @exclude.include?(key) || (empty && @data[key].empty?)
239
+ @exclude.include?(key) || (empty && (!@data.key?(key) || @data[key].empty?))
232
240
  end
233
241
 
234
242
  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.5.18
4
+ version: 0.5.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham