squared 0.6.3 → 0.6.5

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: ac2411d2abc2d5caca8296cd25de89f28ede8458dd42809216e9ecb60dbc12e3
4
- data.tar.gz: 2574d5d757f7405c47d792c47f83520325a633514801885706404efc3a10fa8b
3
+ metadata.gz: 42ae11aaf561b6e6d821f7590e37982c408cb4f09aa4946084e4d3ec29c6fdff
4
+ data.tar.gz: 3ef75c9101dfd1676c035396055bd5585b33e3be41498dbd19eac2a98ea80a81
5
5
  SHA512:
6
- metadata.gz: 6623132213ca2b9510f867ffd711ce8541ef3d707290a51ae40eeca20dbe8ec027c4b47630d67ba96527faa9630a71b9befd89020bbba2ac329fc76925200c61
7
- data.tar.gz: 909a95ac73cd5261d4d2bf184fb90ae6069d92fc30ba850d574da048f73938090ddea6ea7693fc6d0353df8a63213d964b2a964f44e3afa9f8f4188c47f8f5aa
6
+ metadata.gz: cf24602228107b3e1a760749acf96f367cd0877ea6d45a2b0153ea08ba2a3bb014b547986e2406d2aaca4744e005bad2d9994d4a5807a7cc1afe517191aca775
7
+ data.tar.gz: 87e429e0eb78518ea637ecd863aab3359fef584b3c87340cc5a4d9f83718e7b89ac7b258a9e7a9f7d7ece3169776a8fb61d6e5f575c7420778510f3a122d84db
data/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.5] - 2025-11-18
4
+
5
+ ### Added
6
+
7
+ - Ruby command rubocop with stored command options was created.
8
+
9
+ ### Changed
10
+
11
+ - Node command outdated uses inline interactive prompts.
12
+ - Python task outdated no longer updates packages directly.
13
+
14
+ ### Fixed
15
+
16
+ - Common format method emphasize modified frozen string in Ruby 2.5.
17
+
18
+ ## [0.6.4] - 2025-11-16
19
+
20
+ ### Added
21
+
22
+ - Python command outdated can hide non-upgradable packages.
23
+ - Ruby command outdated option dry-run was implemented.
24
+ - Ruby command outdated option interactive was implemented.
25
+ - Repo binary can be downloaded and run from any local directory.
26
+ - Python command outdated option dry-run was implemented.
27
+ - Common shell defined Array instance method quote!.
28
+ - Python command outdated option interactive was implemented.
29
+
30
+ ### Changed
31
+
32
+ - Gem command outdated uses inline interactive prompts.
33
+ - Log messages with level INFO can be called without level argument.
34
+ - Project base attribute project was converted into an accessor.
35
+
36
+ ### Fixed
37
+
38
+ - Node command tsc did not accept a tsconfig or watch argument.
39
+ - Repo application tasks are not created on Windows.
40
+ - Repo module used conflicting REPO_URL with Repo application.
41
+ - Project task outdated did not check pass and only exclusions.
42
+ - Python command install did not include available options.
43
+ - Workspace static method resolve did nothing when given a String.
44
+ - Project base run command types did not include Struct.
45
+
3
46
  ## [0.6.3] - 2025-11-14
4
47
 
5
48
  ### Added
@@ -16,6 +59,9 @@
16
59
  - Requiring individual project classes in Rakefile is optional.
17
60
  - Application class uses static method register instead of series_wrap.
18
61
  - Project support modules are required inside Base class module.
62
+
63
+ ### Fixed
64
+
19
65
  - Project graph print did not display parent and last child consequetively.
20
66
  - Project outdated interactive prompts use exact column alignments.
21
67
 
@@ -1388,6 +1434,8 @@
1388
1434
 
1389
1435
  - Changelog was created.
1390
1436
 
1437
+ [0.6.5]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.5
1438
+ [0.6.4]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.4
1391
1439
  [0.6.3]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.3
1392
1440
  [0.6.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.2
1393
1441
  [0.6.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.6.1
data/README.md CHANGED
@@ -82,7 +82,7 @@ Workspace::Application.load_ref('lib/squared/workspace/project', gem: 'squared')
82
82
  Workspace::Application
83
83
  .new(Dir.pwd, main: "squared") # Dir.pwd? (main? is implicitly basename)
84
84
  .banner("group", "project", styles: ["yellow", "black"], border: "bold") # name | project | path | ref | group? | parent? | version?
85
- .repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "build:prod"], ref: :node) # Repo (optional)
85
+ .repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "prod"], install: "#{ENV["HOME"]}/.bin", ref: :node) # Repo (optional)
86
86
  .run("rake install", ref: :ruby)
87
87
  .depend(false, group: "default")
88
88
  .clean("rake clean", group: "default")
@@ -154,7 +154,7 @@ Node = Workspace::Project::Node # tsc
154
154
  Node.options("build:dev", "target=es2022", project: "tsconfig.json") # :node (ref)
155
155
  Node.options("build:dev", "outDir=tmp", :squared) # squared (project name)
156
156
 
157
- Ruby = Workspace::Project::Ruby # ruby | gem | rake | bundle | irb | rbs
157
+ Ruby = Workspace::Project::Ruby # ruby | gem | rake | bundle | irb | rbs | rubocop
158
158
  Ruby.options("lint", "rubocop", opts: ["gemfile=Gemfile"]) # :ruby
159
159
  Ruby.options("lint", "--parallel", :squared)
160
160
 
@@ -792,7 +792,7 @@ REPO_DEV # pattern,0,1
792
792
  REPO_PROD # pattern,0,1
793
793
  REPO_WARN # 0,1
794
794
  REPO_SYNC # 0,1
795
- REPO_URL # manifest repository
795
+ REPO_GIT # manifest repository
796
796
  REPO_MANIFEST # e.g. latest,nightly,prod
797
797
  REPO_GROUPS # e.g. base,prod,docs
798
798
  REPO_STAGE # 0,1,2,3,4
@@ -189,12 +189,15 @@ module Squared
189
189
  end
190
190
 
191
191
  def log_message(level, *args, subject: nil, hint: nil, append: true, pass: false, color: ARG[:COLOR])
192
+ if args.empty?
193
+ args.concat(Array(level))
194
+ level = Logger::INFO
195
+ end
192
196
  args = args.map(&:to_s)
193
197
  if level.is_a?(::Numeric)
194
198
  if append && respond_to?(:log)
195
- (log rescue nil).tap do |ref|
196
- ref.add(level, message(subject, *args, hint: hint, space: ', ')) if ref.is_a?(::Logger)
197
- end
199
+ ref = log rescue nil
200
+ ref.add(level, message(subject, *args, hint: hint, space: ', ')) if ref.is_a?(::Logger)
198
201
  end
199
202
  return false unless pass || level >= ARG[:LEVEL]
200
203
  end
@@ -271,7 +274,7 @@ module Squared
271
274
  pr = lambda do |line|
272
275
  s = line.ljust(n)
273
276
  sub.each { |h| sub_style!(s, **h) }
274
- s = "#{b0} #{s} #{b0}"
277
+ s = +"#{b0} #{s} #{b0}"
275
278
  if border
276
279
  [[/\A(#{Regexp.escape(b0)})(.+)\z/om], [/\A(.+)(#{Regexp.escape(b0)})\z/om, 2]].each do |args|
277
280
  sub_style!(s, **opt_style(border, *args))
@@ -10,6 +10,7 @@ module Squared
10
10
  private_constant :QUOTE_VALUE
11
11
 
12
12
  String.define_method(:stripquote) { sub(QUOTE_VALUE, '\2') }
13
+ Array.define_method(:quote!) { |**kwargs| map! { |s| Shell.shell_quote(s, **kwargs) } }
13
14
 
14
15
  module_function
15
16
 
@@ -233,10 +233,8 @@ module Squared
233
233
  end
234
234
  return unless (lines = print_keys(type, doc, keys, file: file, opts: opts))
235
235
 
236
- title = Pathname.new(file)
237
- .realpath
238
- .to_s
239
- .sub(/^#{Regexp.escape(File.join(Dir.pwd, ''))}/, '')
236
+ title = File.realpath(file)
237
+ .sub(/^#{Regexp.escape(File.join(Dir.pwd, ''))}/, '')
240
238
  emphasize(lines, title: title, sub: unless stdin?
241
239
  [
242
240
  opt_style(theme[:banner], /\A((?:[^:]|(?<! ):(?! ))+)\z/),
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.6.3'
4
+ VERSION = '0.6.5'
5
5
  end
@@ -24,7 +24,7 @@ module Squared
24
24
  impl_series.base_set(obj)
25
25
  else
26
26
  kind_project.unshift(obj)
27
- obj.tasks&.each { |task| impl_series.add(task, obj) }
27
+ impl_series.extend_set(obj)
28
28
  end
29
29
  if (args = obj.batchargs)
30
30
  impl_series.batch(*args)
@@ -529,7 +529,7 @@ module Squared
529
529
  if (base = task_base?(key))
530
530
  tasks << key if obj.has?(key, baseref)
531
531
  elsif (batch = series.batch_get(key))
532
- obj.allref.each do |ref|
532
+ obj.allref do |ref|
533
533
  next unless obj.has?(key, ref) && (data = batch[ref])
534
534
 
535
535
  data.each do |val|
@@ -548,7 +548,7 @@ module Squared
548
548
  if tasks.empty?
549
549
  return [] if (base && !obj.ref?(baseref)) || !(data = series.alias_get(key))
550
550
 
551
- obj.allref.each do |ref|
551
+ obj.allref do |ref|
552
552
  next unless obj.has?(key, ref) && (alt = data[ref])
553
553
 
554
554
  ret = task_resolve obj, alt
@@ -628,7 +628,7 @@ module Squared
628
628
  end
629
629
 
630
630
  def task_include?(obj, key, ref = nil)
631
- return false if @series.exclude?(key)
631
+ return false if series.exclude?(key)
632
632
 
633
633
  task_base?(key) ? obj.has?(key, ref || baseref) : task_extend?(obj, key)
634
634
  end
@@ -115,8 +115,9 @@ module Squared
115
115
  'asdf' => %i[set exec current update latest where reshim]
116
116
  })
117
117
 
118
- attr_reader :name, :project, :workspace, :path, :theme, :group, :parent, :children, :dependfile,
118
+ attr_reader :name, :workspace, :path, :theme, :group, :parent, :children, :dependfile,
119
119
  :exception, :pipe, :verbose, :global
120
+ attr_accessor :project
120
121
 
121
122
  def initialize(workspace, path, name, *, group: nil, first: {}, last: {}, error: {}, common: ARG[:COMMON],
122
123
  **kwargs)
@@ -250,7 +251,7 @@ module Squared
250
251
  print_error e
251
252
  end
252
253
  log[:progname] ||= @name
253
- log[:level] = val.match?(/\d/) ? log_sym(val.to_i) : val if (val = env('LOG_LEVEL', ignore: false))
254
+ env('LOG_LEVEL', ignore: false) { |val| log[:level] = val.start_with?(/\d/) ? log_sym(val.to_i) : val }
254
255
  log.delete(:file)
255
256
  @log = [file, log]
256
257
  end
@@ -258,28 +259,24 @@ module Squared
258
259
  def initialize_env(dev: nil, prod: nil, **)
259
260
  @dev = env_match('BUILD', dev, suffix: 'DEV', strict: true)
260
261
  @prod = env_match('BUILD', prod, suffix: 'PROD', strict: true)
261
- if @output[2] != false && (val = env('BUILD', suffix: 'ENV'))
262
- @output[2] = parse_json(val, hint: "BUILD_#{@envname}_ENV") || @output[2]
263
- end
262
+ env('BUILD', suffix: 'ENV') { |val| @output[2] = val if (val = parse_json(val)) } unless @output[0] == false
264
263
  unless @output[0] == false || @output[0].is_a?(Array)
265
- if (val = env('BUILD', suffix: 'OPTS'))
264
+ env('BUILD', suffix: 'OPTS') do |val|
266
265
  n = @output[0] ? 1 : 3
267
266
  @output[n] = merge_opts(@output[n], shell_split(val))
268
267
  end
269
- if (val = env(ref.to_s.upcase, suffix: 'OPTS'))
270
- @output[4] = merge_opts(@output[4], shell_split(val))
268
+ env(ref.to_s.upcase, suffix: 'OPTS') { |val| @output[4] = merge_opts(@output[4], shell_split(val)) }
269
+ end
270
+ env('BUILD', suffix: 'VERSION') { |val| self.version = val }
271
+ env('BUILD', strict: true) do |val|
272
+ if val == '0'
273
+ @output = [false]
274
+ elsif script?
275
+ script_set val
276
+ else
277
+ run_set val
271
278
  end
272
279
  end
273
- @version = val if (val = env('BUILD', suffix: 'VERSION'))
274
- return unless (val = env('BUILD', strict: true))
275
-
276
- if val == '0'
277
- @output = [false]
278
- elsif script?
279
- script_set val
280
- else
281
- run_set val
282
- end
283
280
  end
284
281
 
285
282
  def ==(other)
@@ -559,10 +556,9 @@ module Squared
559
556
  flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
560
557
  cmd = case opts
561
558
  when Hash
562
- Array(cmd).push(flags)
563
- .concat(append_hash(opts, target: [], build: true))
564
- .compact
565
- .join(' ')
559
+ [cmd, flags].concat(append_hash(opts, target: [], build: true))
560
+ .compact
561
+ .join(' ')
566
562
  when Enumerable
567
563
  cmd = Array(cmd).concat(opts.to_a)
568
564
  cmd.map! { |val| "#{val} #{flags}" } if flags
@@ -638,10 +634,10 @@ module Squared
638
634
  case @clean
639
635
  when Struct
640
636
  if (val = instance_eval(&@clean.block) || @clean.run)
641
- temp = @clean
642
- @clean = val
643
- clean(*args, sync: sync, pass: true, **kwargs)
644
- @clean = temp
637
+ @clean = @clean.tap do
638
+ @clean = val
639
+ clean(*args, sync: sync, pass: true, **kwargs)
640
+ end
645
641
  end
646
642
  when String
647
643
  run_s(@clean, sync: sync)
@@ -702,7 +698,9 @@ module Squared
702
698
  if order
703
699
  out.map! do |val|
704
700
  name = ret.find { |proj| val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/) }&.name
705
- (n = name && order[name]) ? val.subhint(n.succ) : val
701
+ next val unless (n = name && order[name])
702
+
703
+ val.subhint(n.succ)
706
704
  end
707
705
  else
708
706
  [out, ret]
@@ -743,9 +741,9 @@ module Squared
743
741
  end
744
742
  end
745
743
  env('HEADERS') do |val|
746
- if (data = parse_json(val, hint: "HEADERS_#{@envname}"))
747
- headers = headers.is_a?(Hash) ? headers.merge(data) : data
748
- end
744
+ next unless (data = parse_json(val))
745
+
746
+ headers = headers.is_a?(Hash) ? headers.merge(data) : data
749
747
  end
750
748
  if file
751
749
  ext ||= File.extname(file)[1..-1]
@@ -888,13 +886,9 @@ module Squared
888
886
  end
889
887
 
890
888
  def event(name, key, *args, override: false, **kwargs, &blk)
891
- data = @events[name.to_sym]
892
- items = if override
893
- data[key.to_sym] = []
894
- else
895
- data[key.to_sym] ||= []
896
- end
897
- items << [block_given? ? [blk] + args : args, kwargs]
889
+ args.unshift(blk) if block_given?
890
+ ev = @events[name.to_sym]
891
+ (override ? ev[key.to_sym] = [] : ev[key.to_sym] ||= []) << [args, kwargs]
898
892
  self
899
893
  end
900
894
 
@@ -919,8 +913,10 @@ module Squared
919
913
 
920
914
  def run(cmd = @session, var = nil, exception: self.exception, sync: true, banner: true, from: nil, chdir: path,
921
915
  interactive: nil, hint: nil, series: false, **)
922
- return print_error('no command session started', subject: project, hint: from, pass: true) unless cmd
923
-
916
+ unless cmd
917
+ print_error('no command session started', subject: project, hint: from, pass: true)
918
+ return
919
+ end
924
920
  cmd = cmd.target if cmd.is_a?(OptionPartition)
925
921
  if interactive && sync && (!@session || !option('y'))
926
922
  msg, y, h = case interactive
@@ -1120,14 +1116,41 @@ module Squared
1120
1116
  @log = Logger.new((@log.first if enabled?), **@log.last)
1121
1117
  end
1122
1118
 
1123
- def allref
1124
- @ref.reverse_each
1119
+ def allref(&blk)
1120
+ @ref.reverse_each(&blk)
1125
1121
  end
1126
1122
 
1127
1123
  def basepath(*args)
1128
1124
  path.join(*args)
1129
1125
  end
1130
1126
 
1127
+ def basepath!(*args, type: nil)
1128
+ ret = basepath(*args)
1129
+ return unless ret.exist?
1130
+
1131
+ if type
1132
+ (type.is_a?(String) ? type.chars : type).each do |ch|
1133
+ case ch
1134
+ when 'f'
1135
+ return nil unless ret.file?
1136
+ when 'd'
1137
+ return nil unless ret.directory?
1138
+ when 'l'
1139
+ return nil unless ret.symlink?
1140
+ when 'r'
1141
+ return nil unless ret.readable?
1142
+ when 'w'
1143
+ return nil unless ret.writable?
1144
+ when 'e'
1145
+ return nil unless ret.executable?
1146
+ else
1147
+ return nil
1148
+ end
1149
+ end
1150
+ end
1151
+ ret
1152
+ end
1153
+
1131
1154
  def rootpath(*args, ascend: nil)
1132
1155
  ret = basepath(*args)
1133
1156
  return ret unless ascend && !ret.exist?
@@ -1162,6 +1185,15 @@ module Squared
1162
1185
  name.to_sym
1163
1186
  end
1164
1187
 
1188
+ protected
1189
+
1190
+ def script_get(*args, key: nil)
1191
+ ret = workspace.script_get(*args, group: group, ref: allref)
1192
+ return ret unless ret && key
1193
+
1194
+ ret.fetch(key, nil)
1195
+ end
1196
+
1165
1197
  private
1166
1198
 
1167
1199
  def puts(*args, **kwargs)
@@ -1216,7 +1248,6 @@ module Squared
1216
1248
  def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], order: nil, depth: 0,
1217
1249
  single: false, last: false, context: nil)
1218
1250
  tag = ->(proj) { "#{proj.name}#{"@#{proj.version}" if SEM_VER.match?(proj.version)}" }
1219
- script = ->(proj) { workspace.script_get(:graph, group: proj.group, ref: proj.allref)&.fetch(:graph, nil) }
1220
1251
  uniq = lambda do |name|
1221
1252
  next [] unless (ret = data[name])
1222
1253
 
@@ -1296,21 +1327,22 @@ module Squared
1296
1327
  end
1297
1328
  end
1298
1329
  else
1299
- (tasks || (subtasks = script.call(proj)) || (dev? ? %w[build copy] : %w[depend build])).each do |meth|
1300
- next if pass.include?(meth)
1330
+ (tasks || (graph = proj.script_get(:graph, key: :graph)) || (dev? ? %w[build copy] : %w[depend build]))
1331
+ .each do |meth|
1332
+ next if pass.include?(meth)
1301
1333
 
1302
- if workspace.task_defined?(cmd = task_join(name, meth))
1303
- if ENV.key?(key = "BANNER_#{name.upcase}")
1304
- key = nil
1305
- else
1306
- ENV[key] = '0'
1334
+ if workspace.task_defined?(cmd = task_join(name, meth))
1335
+ if ENV.key?(key = "BANNER_#{name.upcase}")
1336
+ key = nil
1337
+ else
1338
+ ENV[key] = '0'
1339
+ end
1340
+ run(cmd, sync: false, banner: false)
1341
+ ENV.delete(key) if key
1342
+ elsif proj.has?(meth, (workspace.baseref unless tasks || graph))
1343
+ proj.__send__(meth.to_sym, sync: sync)
1307
1344
  end
1308
- run(cmd, sync: false, banner: false)
1309
- ENV.delete(key) if key
1310
- elsif proj.has?(meth, (workspace.baseref unless tasks || subtasks))
1311
- proj.__send__(meth.to_sym, sync: sync)
1312
1345
  end
1313
- end
1314
1346
  end
1315
1347
  done << proj
1316
1348
  end
@@ -1376,12 +1408,13 @@ module Squared
1376
1408
  ignore = ['0'].freeze if ignore.nil?
1377
1409
  ENV[name] || ENV.fetch(key, '')
1378
1410
  end
1379
- if !equals.nil?
1411
+ if equals.nil?
1412
+ ret = default if ret.empty? || (ignore && Array(ignore).any? { |val| ret == val.to_s })
1413
+ return ret if ret.nil?
1414
+ else
1380
1415
  ret = Array(equals).any? { |val| ret == val.to_s }
1381
- elsif ret.empty? || (ignore && Array(ignore).any? { |val| ret == val.to_s })
1382
- ret = default
1383
1416
  end
1384
- return yield ret if block_given? && !ret.nil?
1417
+ return yield ret if block_given?
1385
1418
 
1386
1419
  ret
1387
1420
  end
@@ -1658,7 +1691,9 @@ module Squared
1658
1691
  end
1659
1692
 
1660
1693
  def empty_status(msg, title, obj, always: false)
1661
- "#{msg}#{message(hint: message(title, obj.to_s)) unless !always && (!obj || obj == 0 || obj.to_s.empty?)}"
1694
+ return msg if !always && (!obj || obj == 0 || obj.to_s.empty?)
1695
+
1696
+ msg.subhint(obj.is_a?(Numeric) ? "#{obj} #{title}" : message(title, obj.to_s))
1662
1697
  end
1663
1698
 
1664
1699
  def append_repeat(flag, opts, target: @session)
@@ -1865,19 +1900,12 @@ module Squared
1865
1900
  end
1866
1901
 
1867
1902
  def confirm_basic(msg, target, default = 'Y', style: :inline, **kwargs)
1868
- confirm("#{msg} [#{sub_style(target.to_s, theme[style])}]", default, **kwargs)
1903
+ confirm("#{msg} [#{sub_style(target.to_s, style.is_a?(Symbol) ? theme[style] : style)}]", default, **kwargs)
1869
1904
  end
1870
1905
 
1871
- def confirm_outdated(pkg, ver, rev, cur = nil, lock: false, col0: 0, col1: 0, col2: nil, col3: 0, col4: 0,
1906
+ def confirm_outdated(pkg, ver, type, cur = nil, lock: false, col0: 0, col1: 0, col2: nil, col3: 0, col4: 0,
1872
1907
  **kwargs)
1873
- h = sub_style(case rev
1874
- when 1
1875
- 'MAJOR'
1876
- when 2
1877
- 'MINOR'
1878
- else
1879
- 'PATCH'
1880
- end, (rev == 1 && theme[:major]) || theme[:header])
1908
+ h = sub_style(semrev(type).upcase, (type == 1 && theme[:major]) || theme[:header])
1881
1909
  case col0
1882
1910
  when 0
1883
1911
  col0 = "#{h}: "
@@ -1893,11 +1921,15 @@ module Squared
1893
1921
  cur = cur.ljust(col2 || cur.size.succ)
1894
1922
  lock ? sub_style(cur, color(:red)) : cur
1895
1923
  end
1896
- d = rev == 1 || lock ? 'N' : 'Y'
1924
+ d = type == 1 || lock ? 'N' : 'Y'
1897
1925
  e = "#{col0}#{b}#{c}#{sub_style(col1 > 0 ? ver.ljust(col3) : ver.rjust(ver.size.succ), theme[:inline])}"
1898
1926
  confirm("#{e}#{col4 > 0 ? ' ' * [col4 - e.stripstyle.size - 1, 2].max : ' '}", d, **kwargs)
1899
1927
  end
1900
1928
 
1929
+ def confirm_semver(msg, type, style: (type == 1 && theme[:major]) || :inline, timeout: 0, **kwargs)
1930
+ confirm_basic(msg, semrev(type), type == 1 ? 'N' : 'Y', style: style, timeout: timeout, **kwargs)
1931
+ end
1932
+
1901
1933
  def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil, multiple: false,
1902
1934
  force: true, **kwargs)
1903
1935
  puts unless series || printfirst?
@@ -1962,7 +1994,9 @@ module Squared
1962
1994
  end
1963
1995
 
1964
1996
  def block_args(fallback = nil, &blk)
1965
- (ret = instance_eval(&blk)).nil? ? fallback : Array(ret)
1997
+ return fallback if (ret = instance_eval(&blk)).nil?
1998
+
1999
+ Array(ret)
1966
2000
  end
1967
2001
 
1968
2002
  def runenv
@@ -1976,8 +2010,6 @@ module Squared
1976
2010
  end
1977
2011
 
1978
2012
  def relativepath(*list, all: false)
1979
- return [] if list.empty?
1980
-
1981
2013
  list.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
1982
2014
  ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
1983
2015
  all && val.to_s.end_with?('/') ? "#{ret}/*" : ret
@@ -2064,6 +2096,27 @@ module Squared
2064
2096
  join ? ret.join : ret
2065
2097
  end
2066
2098
 
2099
+ def semtype(cur, lat)
2100
+ if semmajor?(cur, lat)
2101
+ 1
2102
+ else
2103
+ cur[2] == lat[2] ? 3 : 2
2104
+ end
2105
+ end
2106
+
2107
+ def semrev(type)
2108
+ case type
2109
+ when 1
2110
+ 'major'
2111
+ when 2
2112
+ 'minor'
2113
+ when 3
2114
+ 'patch'
2115
+ else
2116
+ 'unknown'
2117
+ end
2118
+ end
2119
+
2067
2120
  def semgte?(val, other)
2068
2121
  semcmp(val, other) != 1
2069
2122
  end
@@ -2080,6 +2133,30 @@ module Squared
2080
2133
  workspace.windows? ? '=' : '^'
2081
2134
  end
2082
2135
 
2136
+ def shortname(*args, suffix: '?', delim: ',', pass: false)
2137
+ return unless TASK_METADATA || pass
2138
+
2139
+ args.map! do |ch|
2140
+ "#{ch}/#{case ch
2141
+ when 'i'
2142
+ 'nteractive'
2143
+ when 's'
2144
+ 'elect'
2145
+ when 'u'
2146
+ 'pdate'
2147
+ when 'h'
2148
+ 'ide'
2149
+ when 'f'
2150
+ 'orce'
2151
+ when 'd'
2152
+ 'ry-run'
2153
+ else
2154
+ next
2155
+ end}#{suffix}"
2156
+ end.compact
2157
+ .join(delim)
2158
+ end
2159
+
2083
2160
  def printsucc
2084
2161
  @@print_order += 1
2085
2162
  end
@@ -2244,7 +2321,7 @@ module Squared
2244
2321
  def asdf_set(val)
2245
2322
  @asdf = if @@asdf && val
2246
2323
  dir = @@asdf.path.join('installs', val)
2247
- [val, dir] if dir.exist? && !dir.empty?
2324
+ [val, dir] if dir.directory? && !dir.empty?
2248
2325
  end
2249
2326
  end
2250
2327
 
@@ -2268,7 +2345,7 @@ module Squared
2268
2345
  end
2269
2346
 
2270
2347
  def as_get(val, from)
2271
- (@global && @as[from][val]) || val
2348
+ (global && @as[from][val]) || val
2272
2349
  end
2273
2350
 
2274
2351
  def task_build(keys)
@@ -2322,7 +2399,7 @@ module Squared
2322
2399
 
2323
2400
  def runnable?(val)
2324
2401
  case val
2325
- when String, Enumerable, Proc, Method
2402
+ when String, Enumerable, Proc, Method, Struct
2326
2403
  true
2327
2404
  else
2328
2405
  false
@@ -2407,12 +2484,7 @@ module Squared
2407
2484
  return false unless target.is_a?(Enumerable)
2408
2485
 
2409
2486
  args = args.first if args.size == 1 && args.first.is_a?(Enumerable)
2410
- case target
2411
- when Hash
2412
- args.is_a?(Enumerable) ? args.any? { |obj| target.value?(obj) } : target.value?(args)
2413
- else
2414
- args.is_a?(Enumerable) ? args.any? { |obj| target.include?(obj) } : target.include?(args)
2415
- end
2487
+ args.any? { |obj,| target.include?(obj) }
2416
2488
  end
2417
2489
 
2418
2490
  def has_value!(target, *args, first: false)
@@ -2450,7 +2522,7 @@ module Squared
2450
2522
  end
2451
2523
 
2452
2524
  def scriptargs
2453
- { target: script? ? @output[1] : @output[0], script: script?, ref: ref, group: group, global: @global }
2525
+ { target: script? ? @output[1] : @output[0], script: script?, ref: ref, group: group, global: global }
2454
2526
  end
2455
2527
  end
2456
2528