squared 0.5.20 → 0.6.0

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.
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
- require 'date'
5
4
  require 'logger'
6
5
 
7
6
  module Squared
@@ -15,15 +14,17 @@ module Squared
15
14
  include Prompt
16
15
  include Utils
17
16
  include Support
17
+ include Workspace::Support::Variables
18
18
  include Rake::DSL
19
19
 
20
- VAR_SET = %i[parent global script index envname desc dependfile dependindex theme archive env dev prod graph
21
- pass only exclude asdf].freeze
20
+ OPTIONS = Workspace::Support.hashobj
21
+ VAR_SET = %i[parent global script index envname desc dependfile dependname dependindex theme archive env graph
22
+ dev prod pass only exclude asdf].freeze
22
23
  BLK_SET = %i[run depend doc lint test copy clean].freeze
23
24
  SEM_VER = /\b(\d+)(?:(\.)(\d+))?(?:(\.)(\d+))?[-.]?(\S+)?\b/.freeze
24
25
  URI_SCHEME = %r{\A([a-z][a-z\d+-.]*)://[^@:\[\]\\^<>|\s]}i.freeze
25
26
  TASK_METADATA = Rake::TaskManager.record_task_metadata
26
- private_constant :VAR_SET, :BLK_SET, :SEM_VER, :URI_SCHEME, :TASK_METADATA
27
+ private_constant :OPTIONS, :VAR_SET, :BLK_SET, :SEM_VER, :URI_SCHEME, :TASK_METADATA
27
28
 
28
29
  class << self
29
30
  def populate(*); end
@@ -35,13 +36,33 @@ module Squared
35
36
  (%i[build archive graph prereqs] + BLK_SET).freeze
36
37
  end
37
38
 
38
- def as_path(val)
39
- case val
40
- when Pathname
41
- val
42
- when String
43
- Pathname.new(val)
39
+ def options(*args, **kwargs)
40
+ name = nil
41
+ with = []
42
+ proj = []
43
+ opts = []
44
+ args.each do |val|
45
+ case val
46
+ when String
47
+ if name
48
+ opts << val
49
+ else
50
+ name = val
51
+ end
52
+ when Symbol
53
+ if name
54
+ proj << val
55
+ else
56
+ with << val
57
+ end
58
+ end
44
59
  end
60
+ return if !name || (opts.empty? && kwargs.empty? && with.empty?)
61
+
62
+ base = OPTIONS[ref]
63
+ data = [opts.freeze, kwargs.freeze, with.freeze].freeze
64
+ proj << :_ if proj.empty?
65
+ proj.each { |val| (base[val] ||= {})[name.to_s] = data }
45
66
  end
46
67
 
47
68
  def ref
@@ -61,28 +82,39 @@ module Squared
61
82
  def to_s
62
83
  super[/[^:]+\z/, 0]
63
84
  end
85
+
86
+ private
87
+
88
+ def as_path(val)
89
+ case val
90
+ when Pathname
91
+ val
92
+ when String
93
+ Pathname.new(val)
94
+ end
95
+ end
64
96
  end
65
97
 
66
98
  @@tasks = {}
67
99
  @@graph = { _: [] }
68
100
  @@asdf = Pathname.new("#{Dir.home}/.asdf").yield_self do |path|
69
- if path.join('asdf.sh').exist?
70
- [path, 15]
71
- elsif ENV['ASDF_DATA_DIR']
72
- [Pathname.new(ENV['ASDF_DATA_DIR']), 16]
73
- end
101
+ version = if path.join('asdf.sh').exist?
102
+ 15
103
+ elsif ENV['ASDF_DATA_DIR'] && (path = Pathname.new(ENV['ASDF_DATA_DIR'])).exist?
104
+ 16
105
+ end
106
+ Struct.new(:path, :version).new(path, version) if version
74
107
  end
75
108
  @@print_order = 0
76
109
 
77
110
  subtasks({
78
111
  'graph' => %i[run print].freeze,
79
- 'unpack' => %i[zip tar gem ext].freeze,
80
- 'asdf' => %i[set exec current]
112
+ 'unpack' => %i[zip gz tar ext].freeze,
113
+ 'asdf' => %i[set exec current update latest where reshim]
81
114
  })
82
115
 
83
- attr_reader :name, :workspace, :path, :theme, :group, :parent, :dependfile,
84
- :exception, :pipe, :verbose
85
- attr_accessor :global, :project
116
+ attr_reader :name, :project, :workspace, :path, :theme, :group, :parent, :dependfile,
117
+ :exception, :pipe, :verbose, :global
86
118
 
87
119
  def initialize(workspace, path, name, *, group: nil, first: {}, last: {}, error: {}, common: ARG[:COMMON],
88
120
  **kwargs)
@@ -108,20 +140,20 @@ module Squared
108
140
  else
109
141
  val.nil? ? workspace.verbose : val
110
142
  end
143
+ self.global = false
111
144
  @output = []
112
145
  @ref = []
113
146
  @children = []
114
147
  @events = hashobj.update({ first: first, last: last, error: error })
115
148
  @as = hashobj
116
149
  @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
117
- @parent = nil
118
- @global = false
119
150
  @log = nil
120
151
  @dev = nil
121
152
  @prod = nil
122
153
  @withargs = nil
123
154
  @session = nil
124
155
  @index = -1
156
+ parent_set kwargs[:parent]
125
157
  run_set(kwargs[:run], kwargs[:env], opts: kwargs.fetch(:opts, true))
126
158
  graph_set kwargs[:graph]
127
159
  pass_set kwargs[:pass]
@@ -157,32 +189,24 @@ module Squared
157
189
 
158
190
  data = @workspace.script_find(*@ref, @group)
159
191
  if @output[0].nil?
160
- if (scr = data[:script])
192
+ if data[:script]
161
193
  unless kwargs[:script] == false
162
- @global = true
163
- script_set(scr, args: data.fetch(:args, kwargs[:args]), prod: kwargs[:prod])
194
+ script_set(data[:script], args: data.fetch(:args, kwargs[:args]), prod: kwargs[:prod], global: true)
164
195
  end
165
- elsif (run = data[:run])
166
- @global = true
167
- run_set run
196
+ elsif data[:run]
197
+ run_set(data[:run], global: true)
168
198
  end
169
- unless data[:env]
170
- if (scr = kwargs[:script])
171
- @global = false
172
- script_set(scr, args: kwargs[:args])
173
- elsif @script && !data[:global]
174
- if (scr = @script[:script])
175
- @global = false
176
- script_set(scr, args: @script.fetch(:args, kwargs[:args]))
177
- elsif (run = @script[:run])
178
- @global = false
179
- run_set run
180
- end
199
+ if kwargs[:script]
200
+ script_set(kwargs[:script], args: kwargs[:args]) unless data[:env][:script]
201
+ elsif @script
202
+ if @script[:script]
203
+ script_set(@script[:script], args: @script.fetch(:args, kwargs[:args])) unless data[:global][:script]
204
+ elsif @script[:run] && !data[:global][:run]
205
+ run_set @script[:run]
181
206
  end
182
207
  end
183
- elsif data[:env] && data[:run]
184
- @global = true
185
- run_set data[:run]
208
+ elsif data[:run] && data[:env][:run]
209
+ run_set(data[:run], global: true)
186
210
  end
187
211
  end
188
212
 
@@ -196,10 +220,11 @@ module Squared
196
220
  return if @log
197
221
 
198
222
  log = log.is_a?(Hash) ? log.dup : { file: log }
199
- if (file = env('LOG_FILE'))
200
- file = Time.now.strftime(file)
201
- elsif (val = env('LOG_AUTO'))
202
- file = "#{@name}-%s.log" % [case val
223
+ file = if (val = env('LOG_FILE'))
224
+ Time.now.strftime(val)
225
+ elsif (val = env('LOG_AUTO'))
226
+ require 'date'
227
+ "#{@name}-%s.log" % [case val
203
228
  when 'y', 'year'
204
229
  Date.today.year
205
230
  when 'm', 'month'
@@ -209,19 +234,21 @@ module Squared
209
234
  else
210
235
  val.include?('%') ? Time.now.strftime(val) : Time.now.strftime('%FT%T%:z')
211
236
  end]
212
- elsif (val = log[:file])
213
- file = val.is_a?(String) ? Time.now.strftime(val) : "#{@name}-#{Date.today}.log"
214
- end
215
- begin
216
- file &&= @workspace.home.join(env('LOG_DIR', ''), file).realdirpath
217
- rescue StandardError => e
218
- file = nil
219
- print_error e
220
- end
237
+ elsif (val = log[:file])
238
+ if val.is_a?(String)
239
+ Time.now.strftime(val)
240
+ else
241
+ require 'date'
242
+ "#{@name}-#{Date.today}.log"
243
+ end
244
+ end
245
+ .yield_self do |dir|
246
+ @workspace.home.join(env('LOG_DIR', ''), dir).realdirpath if dir
247
+ rescue StandardError => e
248
+ print_error e
249
+ end
221
250
  log[:progname] ||= @name
222
- if (val = env('LOG_LEVEL', ignore: false))
223
- log[:level] = val.match?(/\d/) ? log_sym(val.to_i) : val
224
- end
251
+ log[:level] = val.match?(/\d/) ? log_sym(val.to_i) : val if (val = env('LOG_LEVEL', ignore: false))
225
252
  log.delete(:file)
226
253
  @log = [file, log]
227
254
  end
@@ -229,7 +256,7 @@ module Squared
229
256
  def initialize_env(dev: nil, prod: nil, **)
230
257
  @dev = env_match('BUILD', dev, suffix: 'DEV', strict: true)
231
258
  @prod = env_match('BUILD', prod, suffix: 'PROD', strict: true)
232
- if (val = env('BUILD', suffix: 'ENV')) && @output[2] != false
259
+ if @output[2] != false && (val = env('BUILD', suffix: 'ENV'))
233
260
  @output[2] = parse_json(val, hint: "BUILD_#{@envname}_ENV") || @output[2]
234
261
  end
235
262
  unless @output[0] == false || @output[0].is_a?(Array)
@@ -244,7 +271,6 @@ module Squared
244
271
  @version = val if (val = env('BUILD', suffix: 'VERSION'))
245
272
  return unless (val = env('BUILD', strict: true))
246
273
 
247
- @global = false
248
274
  if val == '0'
249
275
  @output = [false]
250
276
  elsif script?
@@ -326,6 +352,10 @@ module Squared
326
352
  end
327
353
  end
328
354
 
355
+ def global=(val)
356
+ @global = val unless val.nil?
357
+ end
358
+
329
359
  def ref
330
360
  Base.ref
331
361
  end
@@ -353,17 +383,17 @@ module Squared
353
383
  out, done = graph(args, out: [])
354
384
  out.map! do |val|
355
385
  n = done.index { |proj| val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/) }
356
- n ? "#{val} (#{n.succ})" : val
386
+ n ? val.subhint(n.succ) : val
357
387
  end
358
388
  emphasize(out, title: path, right: true, border: borderstyle, sub: [
359
- { pat: /\A(#{Regexp.escape(path.to_s)})(.*)\z/, styles: theme[:header] },
360
- { pat: /\A(#{Regexp.escape(name)})(.*)\z/, styles: theme[:active] },
361
- { pat: /\A((?~ \() \()(\d+)(\).*)\z/, styles: theme[:inline], index: 2 }
389
+ opt_style(theme[:header], /\A(#{Regexp.escape(path.to_s)})(.*)\z/),
390
+ opt_style(theme[:active], /\A(#{Regexp.escape(name)})(.*)\z/),
391
+ opt_style(theme[:inline], /\A((?~ \() \()(\d+)(\).*)\z/, 2)
362
392
  ])
363
393
  end
364
394
  end
365
395
  when 'unpack'
366
- format_desc(action, flag, 'tag/url,dir,digest?,f|force?', before: flag == :ext ? 'ext' : nil)
396
+ format_desc(action, flag, 'tag/url,dir,digest?,f/orce?', before: ('ext' if flag == :ext))
367
397
  params = %i[tag dir digest force]
368
398
  params.unshift(:ext) if flag == :ext
369
399
  task flag, params do |_, args|
@@ -371,21 +401,21 @@ module Squared
371
401
  tag = param_guard(action, flag, args: args, key: :tag)
372
402
  dir = param_guard(action, flag, args: args, key: :dir)
373
403
  unless tag.match?(URI_SCHEME)
374
- if flag == :gem
375
- tag = "https://rubygems.org/downloads/#{File.basename(tag, '.gem')}.gem"
376
- elsif @release
377
- tag = "%s.#{ext}" % [@release.include?('??') ? @release.sub('??', tag) : @release + tag]
378
- else
379
- raise_error("no base uri: #{tag}", hint: ext)
380
- end
381
- end
382
- case (digest = args.digest)
383
- when 'f', 'force'
384
- digest = nil
385
- force = true
386
- else
387
- force = args.fetch(:force, false)
404
+ tag = if ext == 'gem'
405
+ "https://rubygems.org/downloads/#{File.basename(tag, '.gem')}.gem"
406
+ elsif @release
407
+ "%s.#{ext}" % [@release.include?('??') ? @release.sub('??', tag) : @release + tag]
408
+ else
409
+ raise_error ArgumentError, "no base uri: #{tag}", hint: ext
410
+ end
388
411
  end
412
+ force = case (digest = args.digest)
413
+ when 'f', 'force'
414
+ digest = nil
415
+ true
416
+ else
417
+ args.fetch(:force, false)
418
+ end
389
419
  unpack(basepath(dir), uri: tag, digest: digest, ext: ext, force: force)
390
420
  end
391
421
  when 'asdf'
@@ -393,7 +423,7 @@ module Squared
393
423
 
394
424
  case flag
395
425
  when :set
396
- format_desc action, flag, 'version,opts*=u|home,p|parent'
426
+ format_desc action, flag, 'version,dir?=u/home|p/arent'
397
427
  task flag, [:version] do |_, args|
398
428
  args = if (version = args.version)
399
429
  args.extras
@@ -403,14 +433,14 @@ module Squared
403
433
  .map(&:basename)
404
434
  .sort { |a, b| b <=> a }
405
435
  .push('latest', 'system'),
406
- accept: [['Confirm?', false, true]],
407
- values: ['Options'])
436
+ accept: [accept_y('Confirm?')],
437
+ values: 'Options', force: true)
408
438
  OptionPartition.strip(opts)
409
439
  end
410
440
  asdf(flag, args, version: version)
411
441
  end
412
442
  else
413
- format_desc(action, flag, flag == :exec ? 'command' : nil)
443
+ format_desc(action, flag, ('command' if flag == :exec))
414
444
  task flag do |_, args|
415
445
  args = args.to_a
416
446
  args << readline('Enter command', force: true) if args.empty? && flag == :exec
@@ -429,7 +459,7 @@ module Squared
429
459
  end
430
460
 
431
461
  def with(**kwargs, &blk)
432
- @withargs = kwargs.empty? ? nil : kwargs
462
+ @withargs = (kwargs unless kwargs.empty?)
433
463
  if block_given?
434
464
  instance_eval(&blk)
435
465
  @withargs = nil
@@ -450,7 +480,6 @@ module Squared
450
480
  kwargs = hashdup(@withargs).update(kwargs) if @withargs
451
481
  kwargs[:group] = group if group && !kwargs.key?(:group)
452
482
  kwargs[:ref] = ref unless kwargs.key?(:ref)
453
- parent = self
454
483
  proj = nil
455
484
  name = case name
456
485
  when String, Symbol
@@ -458,8 +487,7 @@ module Squared
458
487
  else
459
488
  path.basename
460
489
  end
461
- workspace.add(path, name, **kwargs) do
462
- __send__ :parent_set, parent
490
+ workspace.add(path, name, parent: self, **kwargs) do
463
491
  proj = self
464
492
  instance_eval(&blk) if block_given?
465
493
  end
@@ -475,14 +503,14 @@ module Squared
475
503
 
476
504
  def inject(obj, *args, **kwargs, &blk)
477
505
  if enabled?
478
- out = obj.link(self, *args, **kwargs, &blk) if obj.respond_to?(:link)
479
- if !out
480
- print_error('link not compatible', subject: obj, hint: name)
481
- elsif out.respond_to?(:build)
482
- out.build
483
- end
506
+ raise 'link not compatible' unless obj.respond_to?(:link) && (out = obj.link(self, *args, **kwargs, &blk))
507
+
508
+ out.build if out.respond_to?(:build)
484
509
  end
485
510
  self
511
+ rescue StandardError => e
512
+ print_error(e, subject: obj, hint: name)
513
+ self
486
514
  end
487
515
 
488
516
  def build(*args, sync: invoked_sync?('build'), from: :run, **)
@@ -490,8 +518,8 @@ module Squared
490
518
  if args.empty?
491
519
  return unless from == :run
492
520
 
493
- banner = verbosetype > 1 if from_base?('build')
494
- run_b(@run, sync: sync, from: from, banner: banner) if series?(@run)
521
+ banner = verbose? if from_base?('build')
522
+ run_b(@run, sync: sync, banner: banner, from: from) if series?(@run)
495
523
  args = @output
496
524
  end
497
525
  if args.first.is_a?(Struct)
@@ -503,14 +531,8 @@ module Squared
503
531
  cmd = []
504
532
  var = {}
505
533
  args.each do |val|
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
534
+ next instance_exec(*val[1..-1], &val.first) if val.first.is_a?(Proc)
535
+
514
536
  a, b, c, d, e = val
515
537
  case b
516
538
  when Hash
@@ -520,7 +542,7 @@ module Squared
520
542
  end
521
543
  d = append_hash(d, target: []).join(' ') if d.is_a?(Hash)
522
544
  if a
523
- cmd << [replace_bin(a), d, b].compact.join(' ')
545
+ cmd << [a, d, b].compact.join(' ')
524
546
  else
525
547
  next unless respond_to?(:compose)
526
548
 
@@ -531,33 +553,33 @@ module Squared
531
553
  cmd = cmd.join(' && ')
532
554
  else
533
555
  cmd, opts, var, flags, extra = args
534
- if cmd
535
- return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
556
+ end
557
+ if cmd
558
+ return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
536
559
 
537
- cmd = replace_bin as_get(cmd, from)
538
- opts = compose(opts, script: false) if opts && respond_to?(:compose)
539
- flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
540
- case opts
541
- when Hash
542
- cmd = Array(cmd).push(flags)
543
- .concat(append_hash(opts, target: [], build: true))
544
- .compact
545
- .join(' ')
546
- when Enumerable
547
- cmd = Array(cmd).concat(opts.to_a)
548
- cmd.map! { |val| "#{val} #{flags}" } if flags
549
- cmd = cmd.join(' && ')
550
- else
551
- cmd = [cmd, flags, opts].compact.join(' ') if opts || flags
552
- end
560
+ cmd = as_get cmd, from
561
+ opts = compose(opts, script: false) if opts && respond_to?(:compose)
562
+ flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
563
+ case opts
564
+ when Hash
565
+ cmd = Array(cmd).push(flags)
566
+ .concat(append_hash(opts, target: [], build: true))
567
+ .compact
568
+ .join(' ')
569
+ when Enumerable
570
+ cmd = Array(cmd).concat(opts.to_a)
571
+ cmd.map! { |val| "#{val} #{flags}" } if flags
572
+ cmd = cmd.join(' && ')
553
573
  else
554
- return unless (opts || extra) && respond_to?(:compose)
555
-
556
- cmd = compose(as_get(opts, from), flags, script: true, args: extra, from: from)
557
- from = :script if from == :run && script?
574
+ cmd = [cmd, flags, opts].compact.join(' ') if opts || flags
558
575
  end
576
+ else
577
+ return unless (opts || extra) && respond_to?(:compose)
578
+
579
+ cmd = compose(as_get(opts, from), flags, script: true, args: extra, from: from)
580
+ from = :script if from == :run && script?
559
581
  end
560
- run(cmd, var, sync: sync, from: from, banner: banner)
582
+ run(cmd, var, sync: sync, banner: banner, from: from)
561
583
  end
562
584
 
563
585
  def depend(*, sync: invoked_sync?('depend'), **)
@@ -570,16 +592,16 @@ module Squared
570
592
  next if @@graph[:_].include?(proj)
571
593
 
572
594
  if (val = ENV["PREREQS_#{proj.instance_variable_get(:@envname)}"] || ENV["PREREQS_#{proj.ref.upcase}"])
573
- split_escape(val).each do |meth|
595
+ split_escape(val) do |meth|
574
596
  if proj.respond_to?(meth.to_sym)
575
597
  begin
576
598
  proj.__send__(meth, sync: sync)
599
+ next
577
600
  rescue StandardError => e
578
601
  on_error(e, :prereqs, exception: true)
579
602
  end
580
- else
581
- print_error(name, "method: #{meth}", subject: 'prereqs', hint: 'undefined')
582
603
  end
604
+ print_error(name, 'method not found', subject: 'prereqs', hint: meth)
583
605
  end
584
606
  elsif proj.build?
585
607
  proj.build(sync: sync)
@@ -657,9 +679,9 @@ module Squared
657
679
  end
658
680
 
659
681
  def graph(start = [], tasks = nil, *, sync: invoked_sync?('graph'), pass: [], out: nil, **)
660
- if (val = env('GRAPH', strict: true))
682
+ env('GRAPH', strict: true) do |val|
661
683
  tasks ||= []
662
- split_escape(val).each do |task|
684
+ split_escape(val) do |task|
663
685
  if ref?(task.to_sym) && (script = workspace.script_get(:graph, ref: task.to_sym))
664
686
  tasks.concat(script[:graph])
665
687
  else
@@ -667,7 +689,7 @@ module Squared
667
689
  end
668
690
  end
669
691
  end
670
- pass.concat(split_escape(val)) if (val = env('GRAPH', suffix: 'PASS'))
692
+ env('GRAPH', suffix: 'PASS') { |val| pass.concat(split_escape(val)) }
671
693
  start, neg = start.partition { |name| !name.start_with?('-') }
672
694
  data = graph_collect(self, start, pass: neg.map! { |name| name[1..-1] })
673
695
  unless out
@@ -690,9 +712,9 @@ module Squared
690
712
  if !target.exist?
691
713
  target.mkpath
692
714
  elsif !target.directory?
693
- raise_error('invalid location', hint: target)
715
+ raise_error Errno::EEXIST, target, hint: uri
694
716
  elsif !file && !target.empty?
695
- raise_error('directory not empty', hint: target) unless force || env('UNPACK_FORCE')
717
+ raise_error Errno::EEXIST, target, hint: uri unless force || env('UNPACK_FORCE')
696
718
  create = true
697
719
  end
698
720
  if digest
@@ -712,25 +734,27 @@ module Squared
712
734
  when 128, 'sha512'
713
735
  Digest::SHA512
714
736
  else
715
- raise_error "invalid checksum: #{digest}"
737
+ raise_error "invalid checksum: #{digest}", hint: uri
716
738
  end
717
739
  end
718
- if (val = env('HEADERS')) && (val = parse_json(val, hint: "HEADERS_#{@envname}"))
719
- headers = headers.is_a?(Hash) ? headers.merge(val) : val
740
+ env('HEADERS') do |val|
741
+ if (data = parse_json(val, hint: "HEADERS_#{@envname}"))
742
+ headers = headers.is_a?(Hash) ? headers.merge(data) : data
743
+ end
720
744
  end
721
745
  if file
722
746
  ext ||= File.extname(file)[1..-1]
723
747
  else
724
748
  require 'open-uri'
725
749
  data = nil
726
- (uri = Array(uri)).each_with_index do |url, index|
750
+ (uri = Array(uri)).each_with_index do |url, i|
727
751
  URI.open(url, headers) do |f|
728
752
  data = f.read
729
753
  if algo && algo.hexdigest(data) != digest
730
754
  data = nil
731
- raise_error("checksum failed: #{digest}", hint: url) if index == uri.size - 1
755
+ raise_error "invalid checksum: #{digest}", hint: url if i == uri.size.pred
732
756
  end
733
- next if ext && index == 0
757
+ next if ext && i == 0
734
758
 
735
759
  case f.content_type
736
760
  when 'application/zip'
@@ -745,8 +769,8 @@ module Squared
745
769
  end
746
770
  break uri = url if data
747
771
  end
748
- unless data && (ext ||= URI.decode_www_form_component(URI.parse(uri).path[/\.([\w%]+)(?:\?|\z)/, 1]))
749
- raise_error("no content#{data ? ' type' : ''}", hint: uri)
772
+ unless data && (ext ||= URI.parse(uri).path[/\.(\w+)(?:\?|\z)/, 1])
773
+ raise_error(data ? TypeError : RuntimeError, "no content#{data ? ' type' : ''}", hint: uri)
750
774
  end
751
775
  end
752
776
  ext = ext.downcase
@@ -755,13 +779,13 @@ module Squared
755
779
  end
756
780
  begin
757
781
  unless file
758
- if ext == 'gem'
759
- dir = Dir.mktmpdir
760
- file = File.new(File.join(dir, File.basename(uri)), 'w')
761
- else
762
- require 'tempfile'
763
- file = Tempfile.new("#{name}-")
764
- end
782
+ file = if ext == 'gem'
783
+ dir = Dir.mktmpdir
784
+ File.new(File.join(dir, File.basename(uri)), 'w')
785
+ else
786
+ require 'tempfile'
787
+ Tempfile.new("#{name}-")
788
+ end
765
789
  file.write(data)
766
790
  file.close
767
791
  file = Pathname.new(file)
@@ -775,7 +799,7 @@ module Squared
775
799
  case ext
776
800
  when 'zip', 'aar'
777
801
  session 'unzip', shell_quote(file), quote_option('d', target)
778
- when 'tar', 'tgz', 'txz', 'tar.gz', 'tar.xz', 'gz', 'xz'
802
+ when 'tar', /\A(?:t|tar\.)?[gx]z\z/
779
803
  flags = +(verbose ? 'v' : '')
780
804
  if ext.end_with?('gz')
781
805
  flags += 'z'
@@ -818,29 +842,32 @@ module Squared
818
842
  def asdf(flag, opts = [], version: nil)
819
843
  return unless @asdf
820
844
 
821
- cmd = session 'asdf', flag
845
+ cmd = flag == :update ? session('asdf', 'plugin update') : session('asdf', flag)
822
846
  name = @asdf.first
823
- legacy = @@asdf[1] == 15
847
+ legacy = @@asdf.version == 15
848
+ banner = true
824
849
  case flag
825
850
  when :set
826
- u = has_value?(opts, %w[u home])
851
+ u = has_value?(opts, 'u', 'home')
827
852
  cmd << if legacy
828
853
  cmd.delete(flag)
829
854
  u ? 'global' : 'local'
830
- elsif has_value?(opts, %w[p parent])
855
+ elsif has_value?(opts, 'p', 'parent')
831
856
  '--parent'
832
857
  elsif u
833
858
  '--home'
834
859
  end
835
860
  cmd << name << version
836
861
  when :exec
837
- cmd << name unless opts.first.start_with?(/#{name}\b/)
838
862
  cmd.merge(opts)
839
863
  when :current
840
864
  cmd << '--no-header' unless legacy
841
865
  cmd << name
866
+ else
867
+ cmd << name
868
+ banner = flag == :update || flag == :reshim
842
869
  end
843
- print_success if success?(run(from: :"asdf:#{flag}"), flag == :set)
870
+ success?(run(banner: banner, from: :"asdf:#{flag}"), flag == :set || flag == :reshim)
844
871
  end
845
872
 
846
873
  def first(key, *args, **kwargs, &blk)
@@ -880,28 +907,27 @@ module Squared
880
907
  instance_variable_set :"@#{key}", [blk]
881
908
  end
882
909
  else
883
- log&.warn "series: @#{key} (invalid)"
910
+ log&.warn "series: @#{key}".subhint('invalid')
884
911
  end
885
912
  self
886
913
  end
887
914
 
888
- def run(cmd = @session, var = nil, exception: self.exception, sync: true, from: nil, banner: true, chdir: path,
915
+ def run(cmd = @session, var = nil, exception: self.exception, sync: true, banner: true, from: nil, chdir: path,
889
916
  interactive: nil, hint: nil, series: true, **)
890
- unless cmd
891
- print_error('no command session started', subject: project, hint: from, pass: true)
892
- return
893
- end
917
+ return print_error('no command session started', subject: project, hint: from, pass: true) unless cmd
918
+
894
919
  cmd = cmd.target if cmd.is_a?(OptionPartition)
895
- if interactive && (!@session || !option('y'))
896
- title, y = case interactive
897
- when Array
898
- interactive
899
- when String
900
- [interactive, 'N']
901
- else
902
- ['Run', 'Y']
903
- end
904
- exit 1 unless confirm("#{title}? [#{sub_style(cmd.to_s, styles: theme[:inline])}]", y)
920
+ if interactive && sync && (!@session || !option('y'))
921
+ title, y, hint = case interactive
922
+ when Array
923
+ interactive
924
+ when String
925
+ [interactive, 'N']
926
+ else
927
+ ['Run', 'Y']
928
+ end
929
+ title = "#{title} #{sub_style(hint, styles: theme[:active])}" if hint
930
+ exit 1 unless confirm_basic("#{title}?", cmd, y)
905
931
  end
906
932
  cmd = session_done cmd
907
933
  log&.info cmd
@@ -911,7 +937,7 @@ module Squared
911
937
  log&.warn "ENV discarded: #{var}" if var
912
938
  task_invoke(cmd, exception: exception, warning: warning?)
913
939
  else
914
- print_item(format_banner(hint ? "#{cmd} (#{hint})" : cmd, banner: banner), series: series) if sync
940
+ print_item(format_banner(cmd, banner: banner, hint: hint), series: series) if sync
915
941
  if var != false && (pre = runenv)
916
942
  case pre
917
943
  when Hash
@@ -934,12 +960,6 @@ module Squared
934
960
  end
935
961
  end
936
962
 
937
- def scope(*args, **kwargs, &blk)
938
- namespace name do
939
- task(*args, **kwargs, &blk)
940
- end
941
- end
942
-
943
963
  def variable_set(key, *args, **kwargs, &blk)
944
964
  if block_given?
945
965
  if blocks.include?(key)
@@ -949,14 +969,7 @@ module Squared
949
969
  args = block_args args, &blk
950
970
  end
951
971
  if variables.include?(key) || blocks.include?(key)
952
- val = case args.size
953
- when 0
954
- nil
955
- when 1
956
- args.first
957
- else
958
- args
959
- end
972
+ val = args.size > 1 ? args : args.first
960
973
  case key
961
974
  when :index
962
975
  index_set val
@@ -981,13 +994,12 @@ module Squared
981
994
  when :env
982
995
  run_set(output[0], *args, **kwargs)
983
996
  when :dependfile
984
- @dependindex = nil
985
- @dependfile = val.nil? ? nil : basepath(*args)
997
+ @dependfile = basepath(*args)
986
998
  else
987
999
  instance_variable_set(:"@#{key}", val)
988
1000
  end
989
1001
  else
990
- log&.warn "variable_set: @#{key} (private)"
1002
+ log&.warn "variable_set: @#{key}".subhint('private')
991
1003
  end
992
1004
  self
993
1005
  end
@@ -1010,6 +1022,10 @@ module Squared
1010
1022
  @ref.include?(val)
1011
1023
  end
1012
1024
 
1025
+ def exist?(*args)
1026
+ basepath(*args).exist?
1027
+ end
1028
+
1013
1029
  def build?
1014
1030
  !!@output[0] || script? || series?(@run)
1015
1031
  end
@@ -1083,14 +1099,18 @@ module Squared
1083
1099
  @dependindex ? @dependindex.succ : 0
1084
1100
  end
1085
1101
 
1102
+ def dependname
1103
+ @dependname ||= dependfile&.basename.to_s
1104
+ end
1105
+
1086
1106
  def log
1087
1107
  return @log unless @log.is_a?(Array)
1088
1108
 
1089
- @log = Logger.new(enabled? ? @log.first : nil, **@log.last)
1109
+ @log = Logger.new((@log.first if enabled?), **@log.last)
1090
1110
  end
1091
1111
 
1092
- def allref(&blk)
1093
- @ref.reverse_each(&blk)
1112
+ def allref
1113
+ @ref.reverse_each
1094
1114
  end
1095
1115
 
1096
1116
  def basepath(*args)
@@ -1104,7 +1124,7 @@ module Squared
1104
1124
  path.parent.ascend.each do |dir|
1105
1125
  target = dir.join(*args)
1106
1126
  return target if target.exist?
1107
- break if (ascend.is_a?(String) && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
1127
+ break if (ascend && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
1108
1128
  end
1109
1129
  ret
1110
1130
  end
@@ -1113,6 +1133,12 @@ module Squared
1113
1133
  workspace.task_localname(name)
1114
1134
  end
1115
1135
 
1136
+ def scriptname(from: :run)
1137
+ return unless (name = @output[1]) && respond_to?(:compose)
1138
+
1139
+ as_get name, from
1140
+ end
1141
+
1116
1142
  def inspect
1117
1143
  "#<#{self.class}: #{name} => #{self}>"
1118
1144
  end
@@ -1131,10 +1157,18 @@ module Squared
1131
1157
  log_console(*args, pipe: kwargs[:pipe] || pipe)
1132
1158
  end
1133
1159
 
1134
- def run_s(*cmd, env: nil, sync: true, from: nil, banner: verbose != false, **kwargs)
1160
+ def run_s(*cmd, sync: true, banner: verbosetype > 0, from: nil, **kwargs)
1161
+ cmd.flatten!
1162
+ case cmd.last
1163
+ when Hash, TrueClass, FalseClass
1164
+ var = cmd.pop
1165
+ end
1135
1166
  on :first, from
1136
1167
  begin
1137
- cmd.flatten.each { |val| run(val, env, sync: sync, banner: banner, **kwargs) }
1168
+ cmd.each do |val|
1169
+ print_run val, banner
1170
+ run(val, var, sync: sync, banner: banner, **kwargs)
1171
+ end
1138
1172
  rescue StandardError => e
1139
1173
  ret = on :error, from, e
1140
1174
  raise unless ret == true
@@ -1151,7 +1185,12 @@ module Squared
1151
1185
  when Proc
1152
1186
  instance_eval(&obj)
1153
1187
  when Method
1154
- obj.call
1188
+ args = if (n = obj.arity.abs) > 0
1189
+ Array.new(n).tap { |data| data[0] = self }
1190
+ else
1191
+ []
1192
+ end
1193
+ obj.call(*args)
1155
1194
  else
1156
1195
  if series?(obj)
1157
1196
  obj.each(&:call)
@@ -1167,7 +1206,6 @@ module Squared
1167
1206
  single: false, last: false, context: nil)
1168
1207
  tag = ->(proj) { "#{proj.name}#{SEM_VER.match?(proj.version) ? "@#{proj.version}" : ''}" }
1169
1208
  script = ->(proj) { workspace.script_get(:graph, group: proj.group, ref: proj.allref)&.fetch(:graph, nil) }
1170
- check = ->(deps) { deps.reject { |val| done.include?(val) } }
1171
1209
  dedupe = lambda do |name|
1172
1210
  next [] unless (ret = data[name])
1173
1211
 
@@ -1178,12 +1216,11 @@ module Squared
1178
1216
  end
1179
1217
  ret
1180
1218
  end
1181
- start = target.name
1182
1219
  if depth == 0
1183
- items = check.call(dedupe.call(start))
1220
+ items = dedupe.call(target.name) - done
1184
1221
  single = items.size == 1
1185
1222
  else
1186
- items = check.call(data[start])
1223
+ items = data[target.name] - done
1187
1224
  end
1188
1225
  if out
1189
1226
  a, b, c, d, e = ARG[:GRAPH]
@@ -1198,21 +1235,21 @@ module Squared
1198
1235
  "#{last ? d : c}#{b * 3}#{e} #{f}"
1199
1236
  end
1200
1237
  else
1201
- "#{single ? ' ' : a}#{' ' * (depth - 1)}#{last ? d : c}#{b * 3}#{items.empty? ? b : e} #{f}"
1238
+ "#{single ? ' ' : a}#{' ' * depth.pred}#{last ? d : c}#{b * 3}#{items.empty? ? b : e} #{f}"
1202
1239
  end
1203
1240
  end
1204
1241
  items.each_with_index do |proj, i|
1205
1242
  next if done.include?(proj)
1206
1243
 
1207
- t = dedupe.call(proj.name)
1244
+ t = dedupe.call(name = proj.name)
1208
1245
  j = if out
1209
- if i == items.size - 1 || check.call(post = items[(i + 1)..-1]).empty?
1246
+ if i == items.size.pred || (post = items[i.succ..-1] - done).empty?
1210
1247
  true
1211
1248
  elsif !t.empty? && depth > 0
1212
- post.reject { |pr| t.include?(pr) }.empty?
1249
+ (post - t).empty?
1213
1250
  end
1214
1251
  end
1215
- unless start == proj.name || (none = check.call(t).empty?)
1252
+ unless target.name == name || (none = (t - done).empty?)
1216
1253
  graph_branch(proj, data, tasks, out, sync: sync, pass: pass, done: done, depth: depth.succ,
1217
1254
  single: single, last: j == true, context: target)
1218
1255
  end
@@ -1220,28 +1257,28 @@ module Squared
1220
1257
  (tasks || (subtasks = script.call(proj)) || (dev? ? %w[build copy] : %w[depend build])).each do |meth|
1221
1258
  next if pass.include?(meth)
1222
1259
 
1223
- if workspace.task_defined?(cmd = task_join(proj.name, meth))
1224
- if ENV.key?(key = "BANNER_#{proj.name.upcase}")
1260
+ if workspace.task_defined?(cmd = task_join(name, meth))
1261
+ if ENV.key?(key = "BANNER_#{name.upcase}")
1225
1262
  key = nil
1226
1263
  else
1227
1264
  ENV[key] = '0'
1228
1265
  end
1229
1266
  run(cmd, sync: false, banner: false)
1230
1267
  ENV.delete(key) if key
1231
- elsif proj.has?(meth, tasks || subtasks ? nil : workspace.baseref)
1268
+ elsif proj.has?(meth, (workspace.baseref unless tasks || subtasks))
1232
1269
  proj.__send__(meth.to_sym, sync: sync)
1233
1270
  end
1234
1271
  end
1235
1272
  elsif none
1236
1273
  a, b, c, d = ARG[:GRAPH]
1237
1274
  out << if depth == 0
1238
- "#{i == items.size - 1 ? d : c}#{b * 4} #{tag.call(proj)}"
1275
+ "#{i == items.size.pred ? d : c}#{b * 4} #{tag.call(proj)}"
1239
1276
  else
1240
1277
  s = ''.dup
1241
1278
  k = 0
1242
1279
  final = data.keys.last
1243
1280
  while k < depth
1244
- indent = k > 0 ? ((last && !j) || (j && k == depth - 1) || single) : j && last && depth == 1
1281
+ indent = k > 0 ? ((last && !j) || (j && k == depth.pred) || single) : j && last && depth == 1
1245
1282
  s += "#{indent || (last && data[final].last == context) ? ' ' : a} "
1246
1283
  k += 1
1247
1284
  end
@@ -1259,22 +1296,19 @@ module Squared
1259
1296
  next if pass.include?(val)
1260
1297
 
1261
1298
  if (obj = workspace.find(name: val))
1262
- next unless obj.enabled?
1263
-
1264
- items = [obj]
1299
+ obj.enabled? ? [obj] : []
1265
1300
  else
1266
- items = workspace.find(group: val, ref: val.to_sym)
1267
- end
1268
- items.each do |proj|
1301
+ workspace.find(group: val, ref: val.to_sym)
1302
+ end.each do |proj|
1269
1303
  next if pass.include?(name = proj.name)
1270
1304
 
1271
1305
  if proj.graph? && !data.key?(name) && !root.include?(name)
1272
1306
  graph_collect(proj, data: data, pass: pass, root: root + [name, target.name])
1273
1307
  end
1274
- next if (objs = data.fetch(name, [])).include?(target)
1275
-
1276
- deps << proj
1277
- deps.concat(objs)
1308
+ unless (objs = data.fetch(name, [])).include?(target)
1309
+ deps << proj
1310
+ deps.concat(objs)
1311
+ end
1278
1312
  end
1279
1313
  end
1280
1314
  deps.uniq!
@@ -1300,44 +1334,96 @@ module Squared
1300
1334
  end
1301
1335
 
1302
1336
  def env(key, default = nil, suffix: nil, equals: nil, ignore: nil, strict: false)
1303
- a = "#{key}_#{@envname}"
1337
+ name = "#{key}_#{@envname}"
1304
1338
  ret = if suffix
1305
- ENV.fetch("#{a}_#{suffix}", '')
1339
+ ENV.fetch("#{name}_#{suffix}", '')
1306
1340
  elsif strict
1307
- ENV[a].to_s
1341
+ ENV[name].to_s
1308
1342
  else
1309
1343
  ignore = ['0'].freeze if ignore.nil?
1310
- ENV[a] || ENV.fetch(key, '')
1344
+ ENV[name] || ENV.fetch(key, '')
1311
1345
  end
1312
- return ret == equals.to_s unless equals.nil?
1313
-
1314
- ret.empty? || (ignore && Array(ignore).any? { |val| ret == val.to_s }) ? default : ret
1346
+ if !equals.nil?
1347
+ ret = ret == equals.to_s
1348
+ elsif ret.empty? || (ignore && Array(ignore).any? { |val| ret == val.to_s })
1349
+ ret = default
1350
+ end
1351
+ block_given? && !ret.nil? ? yield(ret) : ret
1315
1352
  end
1316
1353
 
1317
1354
  def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
1318
- prefix = stripext prefix.to_s
1355
+ prefix = prefix.to_s.stripext
1319
1356
  if path && (val = shell_bin(prefix))
1320
1357
  cmd[0] = shell_quote(val, force: false)
1321
1358
  end
1322
1359
  ret = JoinSet.new(cmd.flatten(1))
1323
- if options && (val = env("#{prefix.upcase}_OPTIONS"))
1324
- split_escape(val).each { |opt| ret.last(fill_option(opt), /\A(--?[^\[\]=\s-][^\[\]=\s]*)[=\s].+\z/m) }
1360
+ if options
1361
+ env("#{prefix.upcase}_OPTIONS") do |val|
1362
+ if val.start_with('-')
1363
+ ret.concat(shell_parse(val))
1364
+ else
1365
+ split_escape(val) { |opt| ret.last(fill_option(opt), /\A(--?[^= ]+)[= ].+\z/m) }
1366
+ end
1367
+ end
1325
1368
  end
1326
1369
  main ? @session = ret : ret
1327
1370
  end
1328
1371
 
1329
- def session_delete(*args, target: @session)
1330
- OptionPartition.delete(target, *args)
1331
- end
1332
-
1333
1372
  def session_output(*cmd, **kwargs)
1334
1373
  session(*cmd, main: false, options: false, **kwargs)
1335
1374
  end
1336
1375
 
1376
+ def session_get(val, pass: nil)
1377
+ base = OPTIONS[ref]
1378
+ args = []
1379
+ kwargs = {}
1380
+ with = []
1381
+ [:_, name.to_sym].each do |key|
1382
+ next unless base.key?(key) && (a, b, c = base[key][val])
1383
+
1384
+ args.concat(a)
1385
+ append_keys(kwargs, b, :opts)
1386
+ with.concat(c)
1387
+ end
1388
+ OptionPartition.uniq!(args, pass) if pass
1389
+ [args, kwargs, with]
1390
+ end
1391
+
1392
+ def session_apply(val, args: nil, kwargs: nil, pass: [], keys: [:opts], exclude: [])
1393
+ a = []
1394
+ b = {}
1395
+ Array(val).each do |c|
1396
+ d, e, f = session_get c
1397
+ unless (f -= exclude).empty?
1398
+ h = []
1399
+ i = {}
1400
+ session_apply(f.map!(&:to_s), args: h, kwargs: i, pass: pass, keys: keys, exclude: exclude.concat(f))
1401
+ a.concat(h)
1402
+ append_keys(b, i, *keys)
1403
+ end
1404
+ a.concat(d)
1405
+ append_keys(b, e, *keys)
1406
+ end
1407
+ if args
1408
+ args.unshift(*a)
1409
+ OptionPartition.uniq!(args, pass) if pass
1410
+ end
1411
+ kwargs&.update(b) { |_, val| val }
1412
+ nil
1413
+ end
1414
+
1415
+ def session_opts(val, args: nil, kwargs: nil, pass: nil, keys: [:opts])
1416
+ opts = kwargs.delete(:opts) || []
1417
+ return opts unless val
1418
+
1419
+ session_apply(val, args: args, kwargs: kwargs, pass: pass, keys: keys)
1420
+ kwargs.fetch(:opts, []).concat(opts)
1421
+ end
1422
+
1337
1423
  def session_done(cmd)
1338
- return cmd unless cmd.respond_to?(:done)
1424
+ return cmd.to_s unless cmd.respond_to?(:done)
1339
1425
 
1340
- raise_error('no args added', hint: cmd.first) unless cmd.size > 1
1426
+ raise_error 'no command added', hint: cmd.first unless cmd.size > 1
1341
1427
  @session = nil if cmd == @session
1342
1428
  cmd.done
1343
1429
  end
@@ -1352,33 +1438,40 @@ module Squared
1352
1438
  return unless prefix
1353
1439
 
1354
1440
  args.each do |val|
1355
- next unless (ret = env(env_key(stripext(prefix), val), **kwargs))
1441
+ next unless (ret = env(env_key(prefix.to_s.stripext, val), **kwargs))
1356
1442
 
1357
1443
  return block_given? ? yield(ret) : ret
1358
1444
  end
1359
1445
  nil
1360
1446
  end
1361
1447
 
1362
- def option_clear(opts, target: @session, **kwargs)
1448
+ def option_clear(opts, empty = true, target: @session, **kwargs)
1363
1449
  return unless target
1364
1450
 
1365
1451
  OptionPartition.clear(target, opts, styles: theme[:inline], **kwargs)
1452
+ opts.clear if empty
1453
+ nil
1366
1454
  end
1367
1455
 
1368
- def print_success(*)
1456
+ def print_success
1369
1457
  puts 'Success'
1370
1458
  end
1371
1459
 
1372
1460
  def print_error(*args, loglevel: Logger::WARN, **kwargs)
1373
- return unless warning?
1461
+ warn log_message(loglevel, *args, **kwargs) if warning?
1462
+ end
1374
1463
 
1375
- warn log_message(loglevel, *args, **kwargs)
1464
+ def print_run(cmd, banner = true, verbose: nil, **)
1465
+ return if banner || !stdout? || verbose == false
1466
+
1467
+ puts "\n> #{cmd}"
1468
+ printsucc
1376
1469
  end
1377
1470
 
1378
1471
  def print_item(*val, series: true)
1379
1472
  puts unless printfirst?
1380
1473
  printsucc if series
1381
- puts val unless val.empty? || (val.size == 1 && val.first.nil?)
1474
+ puts val unless val.empty? || (val.size == 1 && !val.first)
1382
1475
  end
1383
1476
 
1384
1477
  def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle, **)
@@ -1407,14 +1500,15 @@ module Squared
1407
1500
 
1408
1501
  def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle, **)
1409
1502
  n = line_width lines
1503
+ sub = as_a sub
1410
1504
  lines.map! do |val|
1411
1505
  s = right ? val.rjust(n) : val.ljust(n)
1412
- sub&.each { |h| s = sub_style(s, **h) }
1506
+ sub.each { |h| s = sub_style(s, **h) }
1413
1507
  s
1414
1508
  end
1415
- ret = [sub_style(ARG[:BORDER][1] * n, styles: border)].concat(lines)
1416
- ret.reverse! if reverse
1417
- ret.join("\n")
1509
+ [sub_style(ARG[:BORDER][1] * n, styles: border)].concat(lines)
1510
+ .tap { |ret| ret.reverse! if reverse }
1511
+ .join("\n")
1418
1512
  end
1419
1513
 
1420
1514
  def print_status(*args, from: nil, **kwargs)
@@ -1423,15 +1517,15 @@ module Squared
1423
1517
  case from
1424
1518
  when :outdated
1425
1519
  out = print_footer("major #{args[0]} / minor #{args[1]} / patch #{args[2]}", right: true).split("\n")
1426
- out[1] = sub_style(out[1], pat: /^( +major )(\d+)(.+)$/, styles: theme[:major], index: 2)
1427
- out[1] = sub_style(out[1], pat: /^(.+)(minor )(\d+)(.+)$/, styles: theme[:active], index: 3)
1520
+ out[1] = sub_style(out[1], **opt_style(theme[:major], /^( +major )(\d+)(.+)$/, 2))
1521
+ out[1] = sub_style(out[1], **opt_style(theme[:active], /^(.+)(minor )(\d+)(.+)$/, 3))
1522
+ out[1] = sub_style(out[1], **opt_style(theme[:current], /^(.+)(patch )(\d+)(.+)$/, 3)) if theme[:current]
1428
1523
  puts out
1429
1524
  when :completed
1430
- if verbose && kwargs[:start]
1431
- msg = sub_style('completed', styles: theme[:active])
1432
- puts log_message(Logger::INFO, *args, msg, subject: kwargs[:subject],
1433
- hint: time_format(time_epoch - kwargs[:start]))
1434
- end
1525
+ return unless verbose && kwargs[:start]
1526
+
1527
+ puts log_message(Logger::INFO, *args, sub_style('completed', styles: theme[:active]),
1528
+ subject: kwargs[:subject], hint: time_format(time_epoch - kwargs[:start]))
1435
1529
  end
1436
1530
  end
1437
1531
 
@@ -1441,7 +1535,7 @@ module Squared
1441
1535
  workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
1442
1536
  end
1443
1537
 
1444
- def format_banner(cmd, banner: true)
1538
+ def format_banner(cmd, banner: true, hint: nil, strip: nil)
1445
1539
  return unless banner && banner?
1446
1540
 
1447
1541
  if (data = workspace.banner_get(*@ref, group: group))
@@ -1455,11 +1549,14 @@ module Squared
1455
1549
  out = []
1456
1550
  if data.command
1457
1551
  if cmd =~ /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+))( |\z)/
1458
- path = $3 || $2 || $1
1459
- name = stripext path
1460
- cmd = cmd.sub(path, data.command == 0 ? name : name.upcase)
1552
+ arg = $3 || $2 || $1
1553
+ cmd = cmd.sub(arg, data.command == 0 ? arg.stripext : arg.stripext.upcase)
1461
1554
  end
1462
- out << cmd
1555
+ if strip || (strip.nil? && data.order.include?(:path))
1556
+ cmd = cmd.gsub(/(?:#{s = Regexp.escape(File.join(path, ''))}?(?=["'])|#{s})/, '')
1557
+ .gsub(/(?: -[^ ])? (?:""|'')/, '')
1558
+ end
1559
+ out << cmd.subhint(hint)
1463
1560
  end
1464
1561
  data.order.each do |val|
1465
1562
  if val.is_a?(Array)
@@ -1487,7 +1584,7 @@ module Squared
1487
1584
  end
1488
1585
  end
1489
1586
 
1490
- def format_list(items, cmd, type, grep: [], from: nil, each: nil)
1587
+ def format_list(items, cmd, type, grep: [], from: nil)
1491
1588
  reg = grep.map { |val| Regexp.new(val) }
1492
1589
  out = []
1493
1590
  unless items.empty?
@@ -1495,29 +1592,29 @@ module Squared
1495
1592
  items.each_with_index do |val, i|
1496
1593
  next unless matchany?(val.first, reg)
1497
1594
 
1498
- out << ('%*d. %s' % [pad, i.succ, each ? each.call(val) : val.first])
1595
+ out << ('%*d. %s' % [pad, i.succ, block_given? ? yield(val) : val.first])
1499
1596
  end
1500
1597
  end
1501
1598
  sub = [headerstyle]
1502
- if out.empty?
1503
- out = ["No #{type} were found:", '']
1504
- unless grep.empty?
1505
- i = 0
1506
- out.concat(grep.map { |s| "#{i += 1}. #{s}" })
1507
- out << ''
1508
- end
1509
- if from
1510
- out << (from = from.to_s)
1511
- pat = /\A(#{Regexp.escape(from)})(.*)\z/
1512
- end
1513
- else
1514
- pat = /\A(\s*\d+\.)(.+)\z/
1515
- unless grep.empty?
1516
- footer = "#{out.size} found "
1517
- sub << { pat: /\A(\d+)( .+)\z/, styles: theme[:inline] }
1518
- end
1519
- end
1520
- sub << { pat: pat, styles: theme[:active] } if pat
1599
+ pat = if out.empty?
1600
+ out = ["No #{type} were found:", '']
1601
+ unless grep.empty?
1602
+ i = 0
1603
+ out.concat(grep.map { |s| "#{i += 1}. #{s}" })
1604
+ out << ''
1605
+ end
1606
+ if from
1607
+ out << (from = from.to_s)
1608
+ /\A(#{Regexp.escape(from)})(.*)\z/
1609
+ end
1610
+ else
1611
+ unless grep.empty?
1612
+ footer = "#{out.size} found "
1613
+ sub << opt_style(theme[:inline], /\A(\d+)( .+)\z/)
1614
+ end
1615
+ /\A(\s*\d+\.)(.+)\z/
1616
+ end
1617
+ sub << opt_style(theme[:active], pat) if pat
1521
1618
  emphasize(out, title: task_join(name, cmd), border: borderstyle, sub: sub, footer: footer, right: true)
1522
1619
  end
1523
1620
 
@@ -1525,14 +1622,13 @@ module Squared
1525
1622
  "#{msg}#{!always && (!obj || obj == 0 || obj.to_s.empty?) ? '' : message(hint: message(title, obj.to_s))}"
1526
1623
  end
1527
1624
 
1528
- def append_repeat(flag, opts, target: @session, **kwargs)
1529
- opts.each { |val| target << shell_option(flag, val, **kwargs) }
1625
+ def append_repeat(flag, opts, target: @session)
1626
+ opts.each { |val| target << shell_option(flag, val) }
1530
1627
  end
1531
1628
 
1532
1629
  def append_hash(data, target: @session || [], build: false)
1533
1630
  if build && (type = env('BUILD', suffix: 'TYPE') || ENV['BUILD_TYPE'])
1534
- type = "__#{type}__"
1535
- if (extra = data[type] || data[type.to_sym]).is_a?(Hash)
1631
+ if (extra = data[type = "__#{type}__"] || data[type.to_sym]).is_a?(Hash)
1536
1632
  data = data.merge(extra)
1537
1633
  else
1538
1634
  extra = nil
@@ -1552,33 +1648,25 @@ module Squared
1552
1648
  target << basic_option(key, val)
1553
1649
  when FalseClass
1554
1650
  target << shell_option(key).sub(/^--(?!no-)/, '--no-')
1555
- when Pathname
1556
- target << shell_option(key, val, escape: false)
1557
1651
  else
1558
- target << shell_option(key, val.is_a?(String) ? val : nil)
1652
+ target << shell_option(key, (val if val.is_a?(String)))
1559
1653
  end
1560
1654
  end
1561
1655
  target
1562
1656
  end
1563
1657
 
1564
1658
  def append_any(val, target: @session, build: false, delim: false)
1565
- return unless val
1566
-
1567
1659
  if delim && !target.include?('--')
1568
1660
  target << '--'
1569
1661
  else
1570
1662
  delim = false
1571
1663
  end
1572
- val = shell_split(val) if val.is_a?(String)
1664
+ val = shell_split val if val.is_a?(String)
1573
1665
  case val
1574
1666
  when Hash
1575
1667
  append_hash(val, target: target, build: build)
1576
1668
  when Enumerable
1577
- if target.is_a?(Array)
1578
- target.concat(val.to_a)
1579
- else
1580
- target.merge(val.to_a)
1581
- end
1669
+ merge_list target, val
1582
1670
  else
1583
1671
  target.delete('--') if delim
1584
1672
  nil
@@ -1599,7 +1687,7 @@ module Squared
1599
1687
  next unless (val = option(opt, **kwargs))
1600
1688
 
1601
1689
  return target << if flag
1602
- shell_option(opt, equals ? val : nil, quote: quote, escape: escape, force: force)
1690
+ shell_option(opt, (val if equals), quote: quote, escape: escape, force: force)
1603
1691
  else
1604
1692
  shell_quote val
1605
1693
  end
@@ -1611,25 +1699,44 @@ module Squared
1611
1699
  **kwargs)
1612
1700
  return if list.empty?
1613
1701
 
1614
- kwargs[:ignore] = false if no && !kwargs.key?(:ignore)
1615
- ret = []
1616
- list.flatten.each do |flag|
1617
- next unless (val = option(flag, target: target, **kwargs))
1702
+ [].tap do |ret|
1703
+ list.flatten.each do |flag|
1704
+ next unless (val = option(flag, target: target, **kwargs))
1618
1705
 
1619
- if no && val == '0'
1620
- flag = "no-#{flag}"
1621
- val = nil
1706
+ if val == '0' && no
1707
+ flag = "no-#{flag}"
1708
+ val = nil
1709
+ end
1710
+ ret << shell_option(flag, (val if equals), escape: escape, quote: quote, force: force)
1622
1711
  end
1623
- ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1712
+ next if ret.empty?
1713
+
1714
+ merge_list target, ret
1624
1715
  end
1625
- ret.each { |val| target << val } unless ret.empty?
1626
- ret
1627
1716
  end
1628
1717
 
1629
1718
  def append_nocolor(target: @session)
1630
1719
  target << '--no-color' if !ARG[:COLOR] || stdin? || option('color', target: target, equals: '0')
1631
1720
  end
1632
1721
 
1722
+ def append_keys(base, data, *keys)
1723
+ out = {}
1724
+ keys.each do |key|
1725
+ next unless data.key?(key)
1726
+
1727
+ out[key] = case (val = data[key])
1728
+ when Array
1729
+ base.fetch(key, []) + val
1730
+ when Hash
1731
+ base.fetch(key, {}).update(val)
1732
+ else
1733
+ val
1734
+ end
1735
+ end
1736
+ base.update(data)
1737
+ .update(out)
1738
+ end
1739
+
1633
1740
  def merge_opts(base, data)
1634
1741
  case data
1635
1742
  when String
@@ -1670,27 +1777,31 @@ module Squared
1670
1777
  end
1671
1778
  end
1672
1779
 
1780
+ def merge_list(base, data)
1781
+ data = Array(data)
1782
+ case base
1783
+ when Array
1784
+ base.concat(data)
1785
+ when Set
1786
+ base.merge(data)
1787
+ else
1788
+ Array(base).concat(data)
1789
+ end
1790
+ end
1791
+
1673
1792
  def collect_hash(data, pass: [])
1674
1793
  ret = []
1675
1794
  data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1676
1795
  ret
1677
1796
  end
1678
1797
 
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
-
1686
1798
  def parse_json(val, kind: Hash, hint: nil)
1687
1799
  ret = JSON.parse(val)
1688
- raise_error("invalid JSON #{kind.name}", val, hint: hint) if kind && !ret.is_a?(kind)
1800
+ raise_error 'invalid JSON'.subhint(kind.name), val, hint: hint if kind && !ret.is_a?(kind)
1801
+ ret
1689
1802
  rescue StandardError => e
1690
1803
  log&.warn e
1691
1804
  print_error(e, subject: name)
1692
- else
1693
- ret
1694
1805
  end
1695
1806
 
1696
1807
  def param_guard(action, flag, args:, key: nil, pat: nil, values: nil)
@@ -1702,12 +1813,16 @@ module Squared
1702
1813
  raise_error(action, "#{flag}[#{key}]", hint: val.nil? ? 'missing' : 'invalid')
1703
1814
  elsif args.is_a?(Array) && args.empty?
1704
1815
  @session = nil
1705
- raise_error(action, "#{flag}+", hint: 'empty')
1816
+ raise_error action, "#{flag}+", hint: 'empty'
1706
1817
  end
1707
1818
  args
1708
1819
  end
1709
1820
 
1710
- def confirm_outdated(pkg, ver, rev, cur = nil, lock: false, col1: 0)
1821
+ def confirm_basic(msg, target, default = 'Y', style: :inline, **kwargs)
1822
+ confirm("#{msg} [#{sub_style(target.to_s, styles: theme[style])}]", default, **kwargs)
1823
+ end
1824
+
1825
+ def confirm_outdated(pkg, ver, rev, cur = nil, lock: false, col1: 0, **kwargs)
1711
1826
  a = sub_style(case rev
1712
1827
  when 1
1713
1828
  'MAJOR'
@@ -1719,16 +1834,17 @@ module Squared
1719
1834
  b = sub_style(pkg.ljust(col1), styles: theme[:inline])
1720
1835
  c = lock ? sub_style((cur || 'locked').rjust(7), styles: color(:red)) : cur&.rjust(7)
1721
1836
  d = rev == 1 || lock ? 'N' : 'Y'
1722
- confirm "#{a}: #{b}#{c} #{sub_style(ver.rjust(col1 > 0 ? 10 : 0), styles: theme[:inline])} ", d
1837
+ confirm("#{a}: #{b}#{c} #{sub_style(ver.rjust(col1 > 0 ? 10 : 0), styles: theme[:inline])} ", d, **kwargs)
1723
1838
  end
1724
1839
 
1725
1840
  def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil, multiple: false,
1726
1841
  force: true, **kwargs)
1727
- puts if !series && !printfirst?
1728
- msg = "#{msg} (optional)" unless force
1729
- unless (ret = choice(msg, list, multiple: multiple, force: force, **kwargs)) && !ret.empty?
1842
+ puts unless series || printfirst?
1843
+ ret = choice(msg, list, multiple: multiple, force: force, **kwargs).tap do |val|
1844
+ next unless val.to_s.empty?
1845
+
1730
1846
  exit 1 if force
1731
- return
1847
+ return nil
1732
1848
  end
1733
1849
  ret = multiple ? ret.map! { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
1734
1850
  if column
@@ -1742,7 +1858,7 @@ module Squared
1742
1858
  ret = Array(ret) if accept.any? { |val| val[1] == true }
1743
1859
  loop do
1744
1860
  item = accept.first
1745
- c = confirm("#{item[0]}#{hint ? " [#{hint}]" : ''}", item[2] ? 'Y' : 'N', timeout: 60)
1861
+ c = confirm("#{item[0]}#{hint ? " [#{hint}]" : ''}", item[2] ? 'Y' : 'N')
1746
1862
  if item[1] == true
1747
1863
  ret << c
1748
1864
  elsif !c
@@ -1763,25 +1879,29 @@ module Squared
1763
1879
  force = false
1764
1880
  end
1765
1881
  val = readline(val, force: force)
1766
- ret << (val.empty? ? nil : val)
1882
+ ret << (val unless val.empty?)
1767
1883
  end
1768
1884
  end
1769
1885
  printsucc unless series
1770
1886
  ret
1771
1887
  end
1772
1888
 
1889
+ def accept_b(val, yes = false)
1890
+ [val, true, yes]
1891
+ end
1892
+
1893
+ def accept_y(val, bool = false)
1894
+ [val, bool, true]
1895
+ end
1896
+
1773
1897
  def command_args(args, min: 0, force: false, **kwargs)
1774
1898
  return if args.size > min || option('i', 'interactive', **kwargs, equals: '0')
1775
1899
 
1776
1900
  readline('Enter arguments', force: force)
1777
1901
  end
1778
1902
 
1779
- def block_args(val = nil, &blk)
1780
- if (ret = instance_eval(&blk)).nil?
1781
- val
1782
- else
1783
- Array(ret)
1784
- end
1903
+ def block_args(fallback = nil, &blk)
1904
+ (ret = instance_eval(&blk)).nil? ? fallback : Array(ret)
1785
1905
  end
1786
1906
 
1787
1907
  def runenv
@@ -1791,7 +1911,7 @@ module Squared
1791
1911
  def command(*args)
1792
1912
  return args.join(' && ') unless workspace.powershell?
1793
1913
 
1794
- "#{shell_bin('powershell.exe')} -Command \"& {#{args.join(' ; ')}}\""
1914
+ "powershell.exe -Command #{shell_quote("& {#{args.join(' ; ')}}", option: false, double: true)}"
1795
1915
  end
1796
1916
 
1797
1917
  def relativepath(*list, all: false)
@@ -1814,12 +1934,10 @@ module Squared
1814
1934
 
1815
1935
  def matchmap(list, prefix = nil)
1816
1936
  list.map do |val|
1817
- if val.is_a?(Regexp)
1818
- val
1819
- else
1820
- val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1821
- Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1822
- end
1937
+ next val if val.is_a?(Regexp)
1938
+
1939
+ val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1940
+ Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1823
1941
  end
1824
1942
  end
1825
1943
 
@@ -1836,7 +1954,8 @@ module Squared
1836
1954
  end
1837
1955
 
1838
1956
  def semscan(val, fill: true)
1839
- val.scan(SEM_VER).first.yield_self { |data| fill ? semver(data) : data }
1957
+ ret = val.scan(SEM_VER).first
1958
+ fill ? semver(ret) : ret
1840
1959
  end
1841
1960
 
1842
1961
  def semcmp(val, other)
@@ -1852,14 +1971,38 @@ module Squared
1852
1971
  end
1853
1972
  [c[0], c[2], c[4] || '0', d]
1854
1973
  end
1855
- a.each_with_index do |c, index|
1856
- next if c == (d = b[index])
1974
+ a.each_with_index do |c, i|
1975
+ next if c == (d = b[i])
1857
1976
 
1858
1977
  return c.to_i < d.to_i ? 1 : -1
1859
1978
  end
1860
1979
  0
1861
1980
  end
1862
1981
 
1982
+ def sembump(val, flag = :patch, join: true)
1983
+ ret = semscan(val, fill: false)
1984
+ case flag
1985
+ when :major
1986
+ ret[2] = if ret[0] != '0' || ret[2].nil?
1987
+ ret[0] = ret[0].succ
1988
+ '0'
1989
+ else
1990
+ ret[2].succ
1991
+ end
1992
+ ret[4] = '0'
1993
+ when :minor
1994
+ if ret[0] == '0'
1995
+ ret[4] &&= ret[4].succ
1996
+ else
1997
+ ret[2] = ret[2].succ
1998
+ ret[4] &&= '0'
1999
+ end
2000
+ when :patch
2001
+ ret[4] &&= ret[4].succ
2002
+ end
2003
+ join ? ret.join : ret
2004
+ end
2005
+
1863
2006
  def semgte?(val, other)
1864
2007
  semcmp(val, other) != 1
1865
2008
  end
@@ -1869,7 +2012,7 @@ module Squared
1869
2012
  end
1870
2013
 
1871
2014
  def indexerror(val, list = nil)
1872
- raise_error("requested index #{val}", hint: list && "of #{list.size}")
2015
+ raise_error IndexError, "requested index #{val}", hint: list && "of #{list.size}"
1873
2016
  end
1874
2017
 
1875
2018
  def indexchar
@@ -1888,10 +2031,6 @@ module Squared
1888
2031
  val.compact.flat_map { |s| color(s) }
1889
2032
  end
1890
2033
 
1891
- def epochtime
1892
- Time.now.strftime('%s%L').to_i
1893
- end
1894
-
1895
2034
  def verbosetype
1896
2035
  case verbose
1897
2036
  when TrueClass
@@ -1923,7 +2062,7 @@ module Squared
1923
2062
  end
1924
2063
  end
1925
2064
 
1926
- def on_error(err, from, exception: self.exception, pass: false, dryrun: false)
2065
+ def on_error(err, from, pass: false, exception: self.exception, dryrun: false)
1927
2066
  log&.error err
1928
2067
  unless dryrun
1929
2068
  ret = on :error, from, err
@@ -1932,19 +2071,17 @@ module Squared
1932
2071
  print_error(err, pass: pass) unless ret
1933
2072
  end
1934
2073
 
1935
- def pwd_set(pass: false, dryrun: false, from: nil)
1936
- return yield if (path.to_s == Dir.pwd || pass == true) && (workspace.mri? || !workspace.windows?)
1937
-
2074
+ def pwd_set(pass: false, exception: self.exception, dryrun: false, from: nil)
1938
2075
  pwd = Dir.pwd
1939
- Dir.chdir(path)
1940
- yield
2076
+ return yield if (path.to_s == pwd || pass == true) && (workspace.mri? || !workspace.windows?)
2077
+
2078
+ Dir.chdir path
2079
+ yield.tap { Dir.chdir pwd }
1941
2080
  rescue StandardError => e
1942
- on_error(e, from, dryrun: dryrun)
1943
- ensure
1944
- Dir.chdir(pwd) if pwd
2081
+ on_error(e, from, exception: exception, dryrun: dryrun)
1945
2082
  end
1946
2083
 
1947
- def run_set(cmd, val = nil, opts: nil, **)
2084
+ def run_set(cmd, val = nil, opts: nil, global: false, **)
1948
2085
  noopt = @output[1] == false && !@output[0].nil?
1949
2086
  noenv = @output[2] == false
1950
2087
  parse = lambda do |data|
@@ -1963,6 +2100,7 @@ module Squared
1963
2100
  ret[2] = data[:env] unless dise
1964
2101
  ret
1965
2102
  end
2103
+ self.global = global
1966
2104
  case cmd
1967
2105
  when Array
1968
2106
  @output = if cmd.all? { |data| data.is_a?(Hash) }
@@ -1994,11 +2132,12 @@ module Squared
1994
2132
  end
1995
2133
  end
1996
2134
 
1997
- def script_set(cmd, prod: nil, args: nil, **)
2135
+ def script_set(cmd, prod: nil, args: nil, global: false, **)
1998
2136
  return if @output[1] == false && @output[0].nil?
1999
2137
 
2138
+ self.global = global
2000
2139
  @output[0] = nil
2001
- @output[1] = if @global && cmd.is_a?(Array)
2140
+ @output[1] = if self.global && cmd.is_a?(Array)
2002
2141
  cmd[prod == true ? 1 : 0]
2003
2142
  else
2004
2143
  cmd
@@ -2043,7 +2182,7 @@ module Squared
2043
2182
 
2044
2183
  def asdf_set(val)
2045
2184
  @asdf = if @@asdf && val
2046
- dir = @@asdf[0].join('installs', val)
2185
+ dir = @@asdf.path.join('installs', val)
2047
2186
  [val, dir] if dir.exist? && !dir.empty?
2048
2187
  end
2049
2188
  end
@@ -2059,9 +2198,12 @@ module Squared
2059
2198
  end
2060
2199
 
2061
2200
  def dependfile_set(list)
2062
- @dependindex = list.index { |file| basepath(file).exist? }.tap do |index|
2063
- @dependfile = basepath(list[index || 0])
2064
- end
2201
+ @dependindex = if @dependname
2202
+ @dependfile = basepath @dependname
2203
+ list.index(@dependname)
2204
+ else
2205
+ list.index { |file| exist?(file) }.tap { |i: 0| @dependfile = basepath(list[i]) }
2206
+ end
2065
2207
  end
2066
2208
 
2067
2209
  def as_get(val, from)
@@ -2103,12 +2245,10 @@ module Squared
2103
2245
  end
2104
2246
 
2105
2247
  def checkdir?(val)
2106
- if val.directory? && !val.empty?
2107
- true
2108
- else
2109
- log&.warn "directory \"#{val}\" (#{val.directory? ? 'empty' : 'not found'})"
2110
- false
2111
- end
2248
+ return true if val.directory? && !val.empty?
2249
+
2250
+ log&.warn "directory \"#{val}\"".subhint(val.directory? ? 'empty' : 'missing')
2251
+ false
2112
2252
  end
2113
2253
 
2114
2254
  def semmajor?(cur, want)
@@ -2121,7 +2261,7 @@ module Squared
2121
2261
 
2122
2262
  def runnable?(val)
2123
2263
  case val
2124
- when String, Enumerable, Proc, Method, Struct
2264
+ when String, Enumerable, Proc, Method
2125
2265
  true
2126
2266
  else
2127
2267
  false
@@ -2148,14 +2288,24 @@ module Squared
2148
2288
  return true if val || from_sync?(ac = workspace.task_name(action))
2149
2289
  return val if group && !(val = from_sync?(ac, group)).nil?
2150
2290
  return val if (base = workspace.find_base(self)) && !(val = from_sync?(ac, base.ref)).nil?
2151
- return false if workspace.series.chain?(val = task_join(name, action))
2152
- return true if task_invoked?(val) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
2291
+ return false if workspace.series.chain?(key = task_join(name, action))
2292
+ return true if task_invoked?(key) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
2153
2293
 
2154
- workspace.series.name_get(action).yield_self { |name| name != action && invoked_sync?(name) }
2294
+ ret = workspace.series.name_get(action)
2295
+ ret != action && invoked_sync?(ret)
2155
2296
  end
2156
2297
 
2157
- def success?(ret, display = true)
2158
- ret == true && display && stdout? && banner?
2298
+ def success?(run, *cond)
2299
+ case run
2300
+ when TrueClass
2301
+ true
2302
+ when FalseClass
2303
+ false
2304
+ else
2305
+ $?.success?
2306
+ end.tap do |ret|
2307
+ print_success if ret && stdout? && banner? && cond.none? { |val| val == false }
2308
+ end
2159
2309
  end
2160
2310
 
2161
2311
  def banner?
@@ -2182,10 +2332,16 @@ module Squared
2182
2332
  workspace.warning
2183
2333
  end
2184
2334
 
2185
- def has_value?(data, other)
2186
- return false unless data.is_a?(Enumerable)
2187
-
2188
- other.is_a?(Enumerable) ? other.any? { |obj,| data.include?(obj) } : data.include?(other)
2335
+ def has_value?(target, *args)
2336
+ args = args.first if args.size == 1 && args.first.is_a?(Enumerable)
2337
+ case target
2338
+ when Hash
2339
+ args.is_a?(Enumerable) ? args.any? { |obj| target.value?(obj) } : target.value?(args)
2340
+ when Enumerable
2341
+ args.is_a?(Enumerable) ? args.any? { |obj| target.include?(obj) } : target.include?(args)
2342
+ else
2343
+ false
2344
+ end
2189
2345
  end
2190
2346
 
2191
2347
  def variables
@@ -2196,28 +2352,16 @@ module Squared
2196
2352
  BLK_SET
2197
2353
  end
2198
2354
 
2199
- def hashobj
2200
- Workspace::Support.hashobj
2201
- end
2202
-
2203
- def hashlist
2204
- Workspace::Support.hashlist
2205
- end
2206
-
2207
- def hashdup
2208
- Workspace::Support.hashdup
2209
- end
2210
-
2211
2355
  def borderstyle
2212
2356
  workspace.banner_get(*@ref, group: group)&.border || theme[:border]
2213
2357
  end
2214
2358
 
2215
2359
  def headerstyle
2216
- { pat: /^(\S+)(\s+)$/, styles: theme[:header] }
2360
+ opt_style theme[:header], /^(\S+)(\s+)$/
2217
2361
  end
2218
2362
 
2219
2363
  def scriptargs
2220
- { target: script? ? @output[1] : @output[0], ref: ref, group: group, global: @global }
2364
+ { target: script? ? @output[1] : @output[0], script: script?, ref: ref, group: group, global: @global }
2221
2365
  end
2222
2366
  end
2223
2367