squared 0.4.36 → 0.5.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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'json'
4
4
  require 'date'
5
+ require 'time'
5
6
  require 'logger'
6
7
 
7
8
  module Squared
@@ -18,10 +19,10 @@ module Squared
18
19
  include Rake::DSL
19
20
 
20
21
  VAR_SET = %i[parent global script index envname desc dependfile dependindex theme archive env dev prod graph
21
- pass only exclude].freeze
22
+ pass exclude].freeze
22
23
  BLK_SET = %i[run depend doc lint test copy clean].freeze
23
- SEM_VER = /\b(\d+)(?:(\.)(\d+))?(?:(\.)(\d+))?[-.]?(\S+)?\b/.freeze
24
- URI_SCHEME = %r{\A([a-z][a-z\d+-.]*)://[^@:\[\]\\^<>|\s]}i.freeze
24
+ SEM_VER = /\b(\d+)(?:(\.)(\d+))?(?:(\.)(\d+)(\S+)?)?\b/.freeze
25
+ URI_SCHEME = %r{^([a-z][a-z\d+-.]*)://[^@:\[\]\\^<>|\s]}i.freeze
25
26
  TASK_METADATA = Rake::TaskManager.record_task_metadata
26
27
  private_constant :VAR_SET, :BLK_SET, :SEM_VER, :URI_SCHEME, :TASK_METADATA
27
28
 
@@ -72,9 +73,8 @@ module Squared
72
73
  'unpack' => %i[zip tar gem ext].freeze
73
74
  })
74
75
 
75
- attr_reader :name, :workspace, :path, :theme, :group, :parent, :dependfile,
76
- :exception, :pipe, :verbose
77
- attr_accessor :global, :project
76
+ attr_reader :name, :project, :workspace, :path, :theme, :exception, :pipe, :verbose,
77
+ :group, :parent, :dependfile
78
78
 
79
79
  def initialize(workspace, path, name, *, group: nil, first: {}, last: {}, error: {}, common: ARG[:COMMON],
80
80
  **kwargs)
@@ -83,36 +83,38 @@ module Squared
83
83
  @name = name.to_s.freeze
84
84
  @project = @path.basename.to_s.freeze
85
85
  @group = group&.to_s.freeze
86
- @envname = env_key(@name).freeze
87
86
  @depend = kwargs[:depend]
88
87
  @doc = kwargs[:doc]
89
88
  @lint = kwargs[:lint]
90
89
  @test = kwargs[:test]
91
90
  @copy = kwargs[:copy]
92
91
  @clean = kwargs[:clean]
92
+ @version = kwargs[:version]
93
93
  @release = kwargs[:release]
94
- self.version = kwargs[:version]
95
- self.exception = env_bool(kwargs[:exception], workspace.exception, strict: true)
96
- self.pipe = env_pipe(kwargs[:pipe], workspace.pipe, strict: true)
97
- self.verbose = case (val = env('VERBOSE', kwargs[:verbose]))
98
- when String
99
- env_bool(val, workspace.verbose, strict: true, index: true)
100
- else
101
- val.nil? ? workspace.verbose : val
102
- end
103
94
  @output = []
104
95
  @ref = []
105
96
  @children = []
106
- @events = hashobj.update({ first: first, last: last, error: error })
107
- @as = hashobj
97
+ @events = {
98
+ first: first,
99
+ last: last,
100
+ error: error
101
+ }
102
+ @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
108
103
  @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
109
104
  @parent = nil
110
105
  @global = false
106
+ @log = nil
107
+ @dev = nil
108
+ @prod = nil
109
+ @withargs = nil
110
+ @session = nil
111
111
  @index = -1
112
112
  run_set(kwargs[:run], kwargs[:env], opts: kwargs.fetch(:opts, true))
113
+ exception_set kwargs[:exception]
114
+ pipe_set kwargs[:pipe]
115
+ verbose_set kwargs[:verbose]
113
116
  graph_set kwargs[:graph]
114
117
  pass_set kwargs[:pass]
115
- only_set kwargs[:only]
116
118
  exclude_set kwargs[:exclude]
117
119
  archive_set kwargs[:archive]
118
120
  theme_set common
@@ -144,10 +146,8 @@ module Squared
144
146
  data = @workspace.script_find(*@ref, @group)
145
147
  if @output[0].nil?
146
148
  if (scr = data[:script])
147
- unless kwargs[:script] == false
148
- @global = true
149
- script_set(scr, args: data.fetch(:args, kwargs[:args]), prod: kwargs[:prod])
150
- end
149
+ @global = true
150
+ script_set(scr, args: data.fetch(:args, kwargs[:args]), prod: kwargs[:prod])
151
151
  elsif (run = data[:run])
152
152
  @global = true
153
153
  run_set run
@@ -170,12 +170,15 @@ module Squared
170
170
  @global = true
171
171
  run_set data[:run]
172
172
  end
173
+ @global = true
173
174
  end
174
175
 
175
176
  def initialize_events(ref, **)
176
177
  return unless (events = @workspace.events_get(group: @group, ref: ref))
177
178
 
178
- events.each { |task, data| data.each { |ev, blk| @events[ev][task] ||= [blk] } }
179
+ events.each do |task, data|
180
+ data.each { |ev, blk| (@events[ev] ||= {})[task] ||= [blk] }
181
+ end
179
182
  end
180
183
 
181
184
  def initialize_logger(log: nil, **)
@@ -202,7 +205,7 @@ module Squared
202
205
  file &&= @workspace.home.join(env('LOG_DIR', ''), file).realdirpath
203
206
  rescue StandardError => e
204
207
  file = nil
205
- print_error e
208
+ warn log_message(Logger::WARN, e, pass: true) if warning?
206
209
  end
207
210
  log[:progname] ||= @name
208
211
  if (val = env('LOG_LEVEL', ignore: false))
@@ -215,7 +218,7 @@ module Squared
215
218
  def initialize_env(dev: nil, prod: nil, **)
216
219
  @dev = env_match('BUILD', dev, suffix: 'DEV', strict: true)
217
220
  @prod = env_match('BUILD', prod, suffix: 'PROD', strict: true)
218
- if (val = env('BUILD', suffix: 'ENV')) && @output[2] != false
221
+ if @output[2] != false && (val = env('BUILD', suffix: 'ENV'))
219
222
  @output[2] = parse_json(val, hint: "BUILD_#{@envname}_ENV") || @output[2]
220
223
  end
221
224
  unless @output[0] == false || @output[0].is_a?(Array)
@@ -240,26 +243,21 @@ module Squared
240
243
  end
241
244
  end
242
245
 
243
- def ==(other)
244
- equal?(other)
245
- end
246
-
247
246
  def <=>(other)
248
- return unless workspace == other.workspace
249
- return 0 if equal?(other)
247
+ return 0 unless workspace == other.workspace
250
248
 
251
249
  a, b = graph_deps
252
250
  return 1 if a.include?(other)
253
251
 
254
252
  c, d = graph_deps other
255
- e = b - d
256
- f = d - b
253
+ e = b.reject { |val| d.include?(val) }
254
+ f = d.reject { |val| b.include?(val) }
257
255
  if parent == other.parent
258
256
  g = []
259
257
  h = []
260
258
  else
261
- g = a - c
262
- h = c - a
259
+ g = a.reject { |val| c.include?(val) }
260
+ h = c.reject { |val| a.include?(val) }
263
261
  end
264
262
  g << self
265
263
  h << other
@@ -269,47 +267,18 @@ module Squared
269
267
  -1
270
268
  elsif h.any? { |val| e.include?(val) }
271
269
  1
272
- elsif e.any? { |val| f.include?(val) } # rubocop:disable Lint/DuplicateBranch
270
+ elsif e.any? { |val| f.include?(val) }
273
271
  -1
274
- elsif f.any? { |val| e.include?(val) } # rubocop:disable Lint/DuplicateBranch
272
+ elsif f.any? { |val| e.include?(val) }
275
273
  1
276
274
  elsif @index >= 0 && (i = other.instance_variable_get(:@index)) >= 0
277
- @index <=> i
275
+ @index <= i ? -1 : 1
276
+ else
277
+ 0
278
278
  end
279
279
  rescue StandardError => e
280
280
  log&.debug e
281
- nil
282
- end
283
-
284
- def version=(val)
285
- @version = val&.to_s
286
- end
287
-
288
- def exception=(val)
289
- @exception = case val
290
- when Numeric, TrueClass, FalseClass
291
- val
292
- else
293
- workspace.exception
294
- end
295
- end
296
-
297
- def pipe=(val)
298
- @pipe = case val
299
- when Numeric, Pathname
300
- val
301
- else
302
- workspace.pipe
303
- end
304
- end
305
-
306
- def verbose=(val)
307
- @verbose = case val
308
- when Numeric, TrueClass, FalseClass
309
- val
310
- else
311
- workspace.verbose
312
- end
281
+ 0
313
282
  end
314
283
 
315
284
  def ref
@@ -322,13 +291,13 @@ module Squared
322
291
 
323
292
  namespace name do
324
293
  Base.subtasks do |action, flags|
325
- next if task_pass?(action)
294
+ next if @pass.include?(action)
326
295
 
327
296
  namespace action do
328
297
  flags.each do |flag|
329
298
  case action
330
299
  when 'graph'
331
- break unless graph?
300
+ next unless graph?
332
301
 
333
302
  format_desc action, flag, '(-)project*'
334
303
  task flag do |_, args|
@@ -338,20 +307,25 @@ module Squared
338
307
  else
339
308
  out, done = graph(args, out: [])
340
309
  out.map! do |val|
341
- n = done.index { |proj| val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/) }
342
- n ? "#{val} (#{n.succ})" : val
310
+ done.each_with_index do |proj, i|
311
+ if val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/)
312
+ val += " (#{i.succ})"
313
+ break
314
+ end
315
+ end
316
+ val
343
317
  end
344
318
  emphasize(out, title: path, right: true, border: borderstyle, sub: [
345
319
  { pat: /\A(#{Regexp.escape(path.to_s)})(.*)\z/, styles: theme[:header] },
346
320
  { pat: /\A(#{Regexp.escape(name)})(.*)\z/, styles: theme[:active] },
347
- { pat: /\A(.+ )(\()(\d+)(\))(.*)\z/, styles: theme[:inline], index: 3 }
321
+ { pat: /\A((?~ \() \()(\d+)(\).*)\z/, styles: theme[:inline], index: 2 }
348
322
  ])
349
323
  end
350
324
  end
351
325
  when 'unpack'
352
326
  format_desc(action, flag, 'tag/url,dir,digest?,f|force?', before: flag == :ext ? 'ext' : nil)
353
327
  params = %i[tag dir digest force]
354
- params.unshift(:ext) if flag == :ext
328
+ params.prepend(:ext) if flag == :ext
355
329
  task flag, params do |_, args|
356
330
  ext = flag == :ext ? param_guard(action, flag, args: args, key: :ext) : flag.to_s
357
331
  tag = param_guard(action, flag, args: args, key: :tag)
@@ -372,7 +346,7 @@ module Squared
372
346
  else
373
347
  force = args.fetch(:force, false)
374
348
  end
375
- unpack(basepath(dir), uri: tag, digest: digest, ext: ext, force: force)
349
+ unpack(path + dir, uri: tag, digest: digest, ext: ext, force: force)
376
350
  end
377
351
  end
378
352
  end
@@ -395,33 +369,35 @@ module Squared
395
369
  end
396
370
 
397
371
  def add(path, name = nil, **kwargs, &blk)
398
- if path.is_a?(String) && path =~ %r{\A(.+)[\\/]\*+\z}
399
- return self unless checkdir?(path = basepath($1))
372
+ if path.is_a?(String) && (seg = path[%r{\A(.+)[\\/]\*+\z}, 1])
373
+ return self unless checkdir?(path = basepath(seg))
400
374
 
401
375
  path = path.children.select { |val| checkdir?(val) }
402
376
  end
403
377
  if path.is_a?(Array)
404
- name = self.name if name == true
378
+ name = @name if name == true
405
379
  path.each { |val| add(val, name && task_join(name, File.basename(val)), **kwargs, &blk) }
406
- elsif projectpath?(path = basepath(path)) && checkdir?(path)
407
- kwargs = hashdup(@withargs).update(kwargs) if @withargs
408
- kwargs[:group] = group if group && !kwargs.key?(:group)
409
- kwargs[:ref] = ref unless kwargs.key?(:ref)
410
- parent = self
411
- proj = nil
412
- name = case name
413
- when String, Symbol
414
- name.to_s
415
- else
416
- path.basename
417
- end
418
- workspace.add(path, name, **kwargs) do
419
- __send__ :parent_set, parent
420
- proj = self
421
- instance_eval(&blk) if block_given?
422
- end
423
- @children << proj
424
- end
380
+ return self
381
+ elsif !projectpath?(path = basepath(path)) || !checkdir?(path)
382
+ return self
383
+ end
384
+ kwargs = @withargs.yield_self { |data| data.dup.update(kwargs) } if @withargs
385
+ kwargs[:group] = group unless kwargs.key?(:group)
386
+ kwargs[:ref] = ref unless kwargs.key?(:ref)
387
+ parent = self
388
+ proj = nil
389
+ name = case name
390
+ when String, Symbol
391
+ name.to_s
392
+ else
393
+ path.basename
394
+ end
395
+ workspace.add(path, name, **kwargs) do
396
+ __send__ :parent_set, parent
397
+ proj = self
398
+ end
399
+ @children << proj
400
+ proj.instance_eval(&blk) if block_given?
425
401
  self
426
402
  end
427
403
 
@@ -431,13 +407,13 @@ module Squared
431
407
  end
432
408
 
433
409
  def inject(obj, *args, **kwargs, &blk)
434
- if enabled?
435
- out = obj.link(self, *args, **kwargs, &blk) if obj.respond_to?(:link)
436
- if !out
437
- print_error('link not compatible', subject: obj, hint: name)
438
- elsif out.respond_to?(:build)
439
- out.build
440
- end
410
+ return self unless enabled?
411
+
412
+ out = obj.link(self, *args, **kwargs, &blk) if obj.respond_to?(:link)
413
+ if !out
414
+ warn log_message(Logger::WARN, 'link not compatible', subject: obj.to_s, hint: name, pass: true)
415
+ elsif out.respond_to?(:build)
416
+ out.build
441
417
  end
442
418
  self
443
419
  end
@@ -447,72 +423,62 @@ module Squared
447
423
  if args.empty?
448
424
  return unless from == :run
449
425
 
450
- banner = verbosetype > 1 if from_base?('build')
451
- run_b(@run, sync: sync, from: from, banner: banner) if series?(@run)
426
+ run_b(@run, sync: sync, from: from) if series?(@run)
452
427
  args = @output
428
+ banner = verbosetype > 1 if from_base?('build')
453
429
  end
454
430
  if args.first.is_a?(Struct)
455
431
  f, blk = args.first.to_a
456
432
  args[0] = instance_eval(&blk) || f
457
- return unless args.first
433
+ return unless args[0]
458
434
  end
459
- if args.all?(Array)
435
+ if args.all? { |val| val.is_a?(Array) }
460
436
  cmd = []
461
437
  var = {}
462
438
  args.each do |val|
463
- case val.first
464
- when Proc
465
- instance_exec(*val[1..-1], &val.first)
466
- next
467
- when Method
468
- val.first.call(*val[1..-1])
469
- next
470
- end
439
+ next instance_exec(*val[1..-1], &val[0]) if val.first.is_a?(Proc)
440
+
471
441
  a, b, c, d, e = val
472
442
  case b
473
443
  when Hash
474
- b = append_hash(b, target: [], build: true).join(' ')
444
+ b = append_hash(b, build: true).join(' ')
475
445
  when Enumerable
476
446
  b = b.to_a.join(' ')
477
447
  end
478
- d = append_hash(d, target: []).join(' ') if d.is_a?(Hash)
448
+ d = append_hash(d).join(' ') if d.is_a?(Hash)
479
449
  if a
480
- cmd << [replace_bin(a), d, b].compact.join(' ')
450
+ cmd << [a, d, b].compact.join(' ')
481
451
  else
482
452
  next unless respond_to?(:compose)
483
453
 
484
- cmd << a if (a = compose(as_get(b, from), d, script: true, args: e, from: from))
454
+ cmd << a if (a = compose(as_get(b), d, script: true, args: e, from: from))
485
455
  end
486
- var.merge!(c) if c.is_a?(Hash)
456
+ var.update(c) if c.is_a?(Hash)
487
457
  end
488
458
  cmd = cmd.join(' && ')
489
459
  else
490
460
  cmd, opts, var, flags, extra = args
491
- if cmd
492
- return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
493
-
494
- cmd = replace_bin as_get(cmd, from)
495
- opts = compose(opts, script: false) if opts && respond_to?(:compose)
496
- flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
497
- case opts
498
- when Hash
499
- cmd = Array(cmd).push(flags)
500
- .concat(append_hash(opts, target: [], build: true))
501
- .compact
502
- .join(' ')
503
- when Enumerable
504
- cmd = Array(cmd).concat(opts.to_a)
505
- cmd.map! { |val| "#{val} #{flags}" } if flags
506
- cmd = cmd.join(' && ')
507
- else
508
- cmd = [cmd, flags, opts].compact.join(' ') if opts || flags
509
- end
461
+ end
462
+ if cmd
463
+ cmd = as_get cmd
464
+ opts = compose(opts, script: false) if opts && respond_to?(:compose)
465
+ flags = append_hash(flags).join(' ') if flags.is_a?(Hash)
466
+ case opts
467
+ when Hash
468
+ opts = append_hash(opts, build: true)
469
+ cmd = as_a(cmd).append(flags).concat(opts).compact.join(' ')
470
+ when Enumerable
471
+ cmd = as_a(cmd).concat(opts.to_a)
472
+ cmd.map! { |val| "#{val} #{flags}" } if flags
473
+ cmd = cmd.join(' && ')
510
474
  else
511
- return unless (opts || extra) && respond_to?(:compose)
512
-
513
- cmd = compose(as_get(opts, from), flags, script: true, args: extra, from: from)
514
- from = :script if from == :run && script?
475
+ cmd = [cmd, flags, opts].compact.join(' ') if opts || flags
515
476
  end
477
+ else
478
+ return unless (opts || extra) && respond_to?(:compose)
479
+
480
+ cmd = compose(as_get(opts), flags, script: true, args: extra, from: from)
481
+ from = :script if from == :run && script?
516
482
  end
517
483
  run(cmd, var, sync: sync, from: from, banner: banner)
518
484
  end
@@ -527,15 +493,19 @@ module Squared
527
493
  next if @@graph[:_].include?(proj)
528
494
 
529
495
  if (val = ENV["PREREQS_#{proj.instance_variable_get(:@envname)}"] || ENV["PREREQS_#{proj.ref.upcase}"])
530
- split_escape(val).each do |meth|
496
+ val.split(/\s*,\s*/).each do |meth|
531
497
  if proj.respond_to?(meth.to_sym)
532
498
  begin
533
499
  proj.__send__(meth, sync: sync)
534
500
  rescue StandardError => e
535
- on_error(e, :prereqs, exception: true)
501
+ ret = on(:error, :prereqs, e)
502
+ raise unless ret == true
503
+ else
504
+ next
536
505
  end
537
- else
538
- print_error(name, "method: #{meth}", subject: 'prereqs', hint: 'undefined')
506
+ end
507
+ if warning?
508
+ warn log_message(Logger::WARN, name, 'method not found', subject: 'prereqs', hint: meth, pass: true)
539
509
  end
540
510
  end
541
511
  elsif proj.build?
@@ -553,7 +523,7 @@ module Squared
553
523
  end
554
524
 
555
525
  def doc(*, sync: invoked_sync?('doc'), **)
556
- run_b(@doc, sync: sync, banner: verbosetype > (from_base?('doc') ? 1 : 0), from: :doc)
526
+ run_b(@doc, sync: sync, from: :doc, banner: from_base?('doc') ? verbosetype > 1 : verbose)
557
527
  end
558
528
 
559
529
  def lint(*, sync: invoked_sync?('lint'), **)
@@ -574,9 +544,9 @@ module Squared
574
544
  on :first, :clean unless pass
575
545
  case @clean
576
546
  when Struct
577
- if (val = instance_eval(&@clean.block) || @clean.run)
547
+ if (any = instance_eval(&@clean.block) || @clean.run)
578
548
  temp = @clean
579
- @clean = val
549
+ @clean = any
580
550
  clean(*args, sync: sync, pass: true, **kwargs)
581
551
  @clean = temp
582
552
  end
@@ -586,15 +556,17 @@ module Squared
586
556
  begin
587
557
  @clean.each { |cmd, opts| build(cmd.to_s, opts, sync: sync) }
588
558
  rescue StandardError => e
589
- on_error e, :clean
559
+ log&.error e
560
+ ret = on(:error, from, e)
561
+ raise if exception && ret != true
590
562
  end
591
563
  else
592
564
  if @clean.is_a?(Enumerable) && !series?(@clean)
593
565
  @clean.each do |val|
594
- entry = basepath(val = val.to_s)
566
+ entry = path + (val = val.to_s)
595
567
  if entry.directory? && val.match?(%r{[\\/]\z})
596
568
  log&.warn "rm -rf #{entry}"
597
- rm_rf(entry, verbose: verbosetype > 0)
569
+ rm_rf(entry, verbose: verbose)
598
570
  else
599
571
  log&.warn "rm #{entry}"
600
572
  (val.include?('*') ? Dir[entry] : [entry]).each do |file|
@@ -635,7 +607,8 @@ module Squared
635
607
  end
636
608
  ret = graph_branch(self, data, tasks, out, sync: sync, pass: pass)
637
609
  rescue StandardError => e
638
- on_error(e, :graph, exception: true)
610
+ ret = on(:error, :graph, e)
611
+ raise unless ret == true
639
612
  else
640
613
  if out
641
614
  [out, ret]
@@ -644,13 +617,13 @@ module Squared
644
617
  end
645
618
  end
646
619
 
647
- def unpack(target, file = nil, uri: nil, sync: true, digest: nil, ext: nil, force: false, depth: 1, headers: {},
648
- verbose: self.verbose, from: :unpack)
620
+ def unpack(target, uri:, sync: true, digest: nil, ext: nil, force: false, depth: 1, headers: {}, from: :unpack)
621
+ require 'open-uri'
649
622
  if !target.exist?
650
623
  target.mkpath
651
624
  elsif !target.directory?
652
625
  raise_error('invalid location', hint: target)
653
- elsif !file && !target.empty?
626
+ elsif !target.empty?
654
627
  raise_error('directory not empty', hint: target) unless force || env('UNPACK_FORCE')
655
628
  create = true
656
629
  end
@@ -671,95 +644,87 @@ module Squared
671
644
  when 128, 'sha512'
672
645
  Digest::SHA512
673
646
  else
674
- raise_error "invalid checksum: #{digest}"
647
+ raise_error("invalid checksum: #{digest}", hint: name)
675
648
  end
676
649
  end
677
650
  if (val = env('HEADERS')) && (val = parse_json(val, hint: "HEADERS_#{@envname}"))
678
651
  headers = headers.is_a?(Hash) ? headers.merge(val) : val
679
652
  end
680
- if file
681
- ext ||= File.extname(file)[1..-1]
682
- else
683
- data = nil
684
- (uri = Array(uri)).each_with_index do |url, index|
685
- fetch_uri(url, headers) do |f|
686
- data = f.read
687
- if algo && algo.hexdigest(data) != digest
688
- data = nil
689
- raise_error("checksum failed: #{digest}", hint: url) if index == uri.size - 1
690
- end
691
- next if ext && index == 0
692
-
693
- case f.content_type
694
- when 'application/zip'
695
- ext = 'zip'
696
- when %r{application/(?:x-)?gzip}
697
- ext = 'tgz'
698
- when 'application/x-xz'
699
- ext = 'txz'
700
- when 'application/x-7z-compressed'
701
- ext = '7z'
702
- end
653
+ data = nil
654
+ (uri = as_a(uri)).each_with_index do |url, index|
655
+ URI.open(url, headers) do |f|
656
+ data = f.read
657
+ if algo && algo.hexdigest(data) != digest
658
+ data = nil
659
+ raise_error("checksum failed: #{digest}", hint: url) if index == uri.size - 1
660
+ end
661
+ next if ext && index == 0
662
+
663
+ case f.content_type
664
+ when 'application/zip'
665
+ ext = 'zip'
666
+ when 'application/x-gzip'
667
+ ext = 'tgz'
668
+ when 'application/x-xz'
669
+ ext = 'txz'
703
670
  end
704
- break uri = url if data
705
- end
706
- unless data && (ext ||= URI.decode_www_form_component(URI.parse(uri).path[/\.([\w%]+)(?:\?|\z)/, 1]))
707
- raise_error("no content#{data ? ' type' : ''}", hint: uri)
708
671
  end
672
+ break uri = url if data
673
+ end
674
+ unless data && (ext ||= URI.parse(uri).path[/\.(\w+)(?:\?|\z)/, 1])
675
+ raise_error("no content#{data ? ' type' : ''}", hint: uri)
709
676
  end
710
677
  ext = ext.downcase
711
678
  if (val = env("#{%w[zip 7z gem].include?(ext) ? ext.upcase : 'TAR'}_DEPTH", ignore: false))
712
679
  depth = val.to_i
713
680
  end
714
681
  begin
715
- unless file
716
- if ext == 'gem'
717
- dir = Dir.mktmpdir
718
- file = File.new(File.join(dir, File.basename(uri)), 'w')
719
- else
720
- require 'tempfile'
721
- file = Tempfile.new("#{name}-")
722
- end
723
- file.write(data)
724
- file.close
725
- file = Pathname.new(file)
726
- delete = true
682
+ if ext == 'gem'
683
+ dir = Dir.mktmpdir
684
+ file = File.new(File.join(dir, File.basename(uri)), 'w')
685
+ else
686
+ require 'tempfile'
687
+ file = Tempfile.new("#{name}-")
727
688
  end
689
+ file.write(data)
690
+ file.close
728
691
  if create
729
- print_error('force remove', subject: name, hint: target)
692
+ warn log_message(Logger::WARN, 'force remove', subject: name, hint: target, pass: true)
730
693
  target.rmtree
731
694
  target.mkpath
732
695
  end
733
696
  case ext
734
697
  when 'zip', 'aar'
735
- session 'unzip', shell_quote(file), quote_option('d', target)
736
- when 'tar', 'tgz', 'txz', 'tar.gz', 'tar.xz', 'gz', 'xz'
698
+ session 'unzip', shell_quote(file.path), quote_option('d', target)
699
+ when 'tar', 'tgz', 'tar.gz', 'tar.xz', 'gz', 'xz'
737
700
  flags = +(verbose ? 'v' : '')
738
701
  if ext.end_with?('gz')
739
702
  flags += 'z'
740
703
  elsif ext.end_with?('xz')
741
704
  flags += 'J'
742
705
  end
743
- session 'tar', "-x#{flags}", basic_option('strip-components', depth), quote_option('f', file),
706
+ session 'tar', "-x#{flags}", basic_option('strip-components', depth), quote_option('f', file.path),
744
707
  quote_option('C', target)
745
708
  depth = 0
746
709
  when '7z'
747
- session '7z', 'x', shell_quote(file), "-o#{shell_quote(target)}"
710
+ session '7z', 'x', shell_quote(file.path), "-o#{shell_quote(target)}"
748
711
  when 'gem'
749
- session 'gem', 'unpack', shell_quote(file), quote_option('target', target)
712
+ session 'gem', 'unpack', shell_quote(file.path), quote_option('target', target)
750
713
  depth = 0 unless val
751
714
  else
752
- raise_error("unsupported format: #{ext}", hint: uri || file)
715
+ raise_error("unsupported format: #{ext}", hint: uri)
753
716
  end
754
- run(sync: sync, banner: verbose, from: from)
717
+ run(sync: sync, from: from)
755
718
  while depth > 0 && target.children.size == 1
756
719
  entry = target.children.first
757
720
  break unless entry.directory?
758
721
 
759
722
  i = 0
760
- i += 1 while (dest = target + "#{File.basename(file)}-#{i}").exist?
723
+ while (dest = target + "#{File.basename(file.path)}-#{i}").exist?
724
+ i += 1
725
+ end
761
726
  FileUtils.mv(entry, dest)
762
- dest.children.each { |child| FileUtils.mv(child, target) }
727
+ dest.each_child { |child| FileUtils.mv(child, target) }
763
728
  dest.rmdir
764
729
  target = entry
765
730
  depth -= 1
@@ -767,8 +732,8 @@ module Squared
767
732
  ensure
768
733
  if dir
769
734
  remove_entry dir
770
- elsif delete && file&.exist?
771
- file.unlink
735
+ else
736
+ file&.unlink
772
737
  end
773
738
  end
774
739
  end
@@ -786,7 +751,7 @@ module Squared
786
751
  end
787
752
 
788
753
  def event(name, key, *args, override: false, **kwargs, &blk)
789
- data = @events[name.to_sym]
754
+ data = @events[name.to_sym] ||= {}
790
755
  items = if override
791
756
  data[key.to_sym] = []
792
757
  else
@@ -797,8 +762,9 @@ module Squared
797
762
  end
798
763
 
799
764
  def as(cmd, script, to = nil)
800
- data = @as[cmd.to_sym]
801
- (to ? [[script, to]] : script).each { |key, val| data[key.to_s] = val }
765
+ script = { "#{script}": to } if to
766
+ data = (@as ||= {})[cmd.to_sym] ||= {}
767
+ script.each { |key, val| data[key.to_s] = val }
802
768
  self
803
769
  end
804
770
 
@@ -815,63 +781,14 @@ module Squared
815
781
  self
816
782
  end
817
783
 
818
- def run(cmd = @session, var = nil, exception: self.exception, sync: true, from: nil, banner: true, chdir: path,
819
- interactive: nil, hint: nil, **)
820
- unless cmd
821
- print_error('no command session started', subject: project, hint: from || 'unknown', pass: true)
822
- return
823
- end
824
- cmd = cmd.target if cmd.is_a?(OptionPartition)
825
- if interactive && (!@session || !option('y'))
826
- title, y = case interactive
827
- when Array
828
- interactive
829
- when String
830
- [interactive, 'N']
831
- else
832
- ['Run', 'Y']
833
- end
834
- yn = y == 'Y' ? 'Y/n' : 'y/N'
835
- exit 1 unless confirm("#{title}? [#{sub_style(cmd.to_s, styles: theme[:inline])}] [#{yn}] ", y)
836
- end
837
- cmd = session_done cmd
838
- log&.info cmd
839
- on :first, from
840
- begin
841
- if cmd.match?(/\A[^:]+:[^:]/) && workspace.task_defined?(cmd)
842
- log&.warn "ENV discarded: #{var}" if var
843
- task_invoke(cmd, exception: exception, warning: warning?)
844
- else
845
- print_item format_banner(hint ? "#{cmd} (#{hint})" : cmd, banner: banner) if sync
846
- if var != false && (pre = runenv)
847
- case pre
848
- when Hash
849
- var = var.is_a?(Hash) ? pre.merge(var) : pre
850
- when Enumerable
851
- cmd = command(*pre.to_a, cmd)
852
- else
853
- cmd = command(pre, cmd)
854
- end
855
- end
856
- args = var.is_a?(Hash) ? [var, cmd] : [cmd]
857
- ret = shell(*args, chdir: chdir, exception: exception)
784
+ def variable_set(key, *args, **kwargs, &blk)
785
+ if block_given?
786
+ if blocks.include?(key)
787
+ series key, &blk
788
+ return self
858
789
  end
859
- rescue StandardError => e
860
- on_error(e, from, exception: true)
861
- false
862
- else
863
- on :last, from
864
- ret
865
- end
866
- end
867
-
868
- def scope(*args, **kwargs, &blk)
869
- namespace name do
870
- task(*args, **kwargs, &blk)
790
+ args = block_args args, &blk
871
791
  end
872
- end
873
-
874
- def variable_set(key, *args, **kwargs, &blk)
875
792
  if variables.include?(key) || blocks.include?(key)
876
793
  val = case args.size
877
794
  when 0
@@ -888,14 +805,10 @@ module Squared
888
805
  graph_set val
889
806
  when :pass
890
807
  pass_set val
891
- when :only
892
- only_set val
893
808
  when :exclude
894
809
  exclude_set val
895
810
  when :parent
896
811
  parent_set val
897
- when :archive
898
- archive_set val
899
812
  when :run
900
813
  run_set(*args, **kwargs)
901
814
  when :script
@@ -903,17 +816,9 @@ module Squared
903
816
  when :env
904
817
  run_set(output[0], *args, **kwargs)
905
818
  when :dependfile
906
- @dependindex = nil
907
- @dependfile = val.nil? ? nil : basepath(*args)
819
+ @dependfile = basepath(*args)
908
820
  else
909
- if block_given?
910
- if blocks.include?(key)
911
- series key, &blk
912
- return self
913
- end
914
- val = block_args val, &blk
915
- end
916
- instance_variable_set(:"@#{key}", val.first)
821
+ instance_variable_set(:"@#{key}", val)
917
822
  end
918
823
  else
919
824
  log&.warn "variable_set: @#{key} (private)"
@@ -921,8 +826,6 @@ module Squared
921
826
  self
922
827
  end
923
828
 
924
- alias apply variable_set
925
-
926
829
  def enabled?(ref = nil, **)
927
830
  return false if ref && !ref?(ref)
928
831
 
@@ -1015,11 +918,11 @@ module Squared
1015
918
  def log
1016
919
  return @log unless @log.is_a?(Array)
1017
920
 
1018
- @log = Logger.new(enabled? ? @log.first : nil, **@log.last)
921
+ @log = Logger.new(enabled? ? @log[0] : nil, **@log[1])
1019
922
  end
1020
923
 
1021
- def allref(&blk)
1022
- @ref.reverse_each(&blk)
924
+ def allref
925
+ @ref.reverse_each
1023
926
  end
1024
927
 
1025
928
  def basepath(*args)
@@ -1056,8 +959,51 @@ module Squared
1056
959
 
1057
960
  private
1058
961
 
1059
- def puts(*args, **kwargs)
1060
- log_console(*args, pipe: kwargs[:pipe] || pipe)
962
+ def puts(*args)
963
+ puts_oe(*args, pipe: pipe)
964
+ end
965
+
966
+ def run(cmd = @session, var = nil, exception: @exception, sync: true, from: nil, banner: true, chdir: path, **)
967
+ unless cmd
968
+ if warning?
969
+ from &&= from.to_s
970
+ warn log_message(Logger::WARN, from || 'unknown', subject: project, hint: 'no command given', pass: true)
971
+ end
972
+ return
973
+ end
974
+ cmd = cmd.target if cmd.is_a?(OptionPartition)
975
+ cmd = session_done cmd
976
+ log&.info cmd
977
+ on :first, from
978
+ begin
979
+ if cmd.start_with?(/[^:]+:[^:]/) && workspace.task_defined?(cmd)
980
+ log&.warn "ENV discarded: #{var}" if var
981
+ task_invoke(cmd, exception: exception, warning: warning?)
982
+ else
983
+ print_item format_banner(cmd, banner: banner) if sync
984
+ if var != false && (pre = runenv)
985
+ case pre
986
+ when Hash
987
+ var = var.is_a?(Hash) ? pre.merge(var) : pre
988
+ when Enumerable
989
+ cmd = command(*pre.to_a, cmd)
990
+ else
991
+ cmd = command pre, cmd
992
+ end
993
+ end
994
+ args = var.is_a?(Hash) ? [var, cmd] : [cmd]
995
+ ret = shell(*args, chdir: chdir, exception: exception)
996
+ end
997
+ rescue StandardError => e
998
+ log&.error e
999
+ ret = on(:error, from, e)
1000
+ raise unless ret == true
1001
+
1002
+ false
1003
+ else
1004
+ on :last, from
1005
+ ret
1006
+ end
1061
1007
  end
1062
1008
 
1063
1009
  def run_s(*cmd, env: nil, sync: true, from: nil, banner: verbose != false, **kwargs)
@@ -1065,7 +1011,8 @@ module Squared
1065
1011
  begin
1066
1012
  cmd.flatten.each { |val| run(val, env, sync: sync, banner: banner, **kwargs) }
1067
1013
  rescue StandardError => e
1068
- on_error(e, from, exception: kwargs.fetch(:exception, exception))
1014
+ ret = on(:error, from, e)
1015
+ raise unless ret == true
1069
1016
  end
1070
1017
  on :last, from
1071
1018
  end
@@ -1073,8 +1020,8 @@ module Squared
1073
1020
  def run_b(obj, **kwargs)
1074
1021
  case obj
1075
1022
  when Struct
1076
- if (val = instance_eval(&obj.block) || obj.run)
1077
- run_b(val, **kwargs)
1023
+ if (any = instance_eval(&obj.block) || obj.run)
1024
+ run_b(any, **kwargs)
1078
1025
  end
1079
1026
  when Proc
1080
1027
  instance_eval(&obj)
@@ -1086,7 +1033,7 @@ module Squared
1086
1033
  elsif obj.is_a?(Array) && obj.any? { |val| !val.is_a?(String) }
1087
1034
  build(*obj, **kwargs)
1088
1035
  elsif obj
1089
- run_s(*Array(obj), **kwargs)
1036
+ run_s(obj.is_a?(Enumerable) ? obj.to_a : obj, **kwargs)
1090
1037
  end
1091
1038
  end
1092
1039
  end
@@ -1094,7 +1041,6 @@ module Squared
1094
1041
  def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], depth: 0,
1095
1042
  single: false, last: false, context: nil)
1096
1043
  tag = ->(proj) { "#{proj.name}#{SEM_VER.match?(proj.version) ? "@#{proj.version}" : ''}" }
1097
- script = ->(proj) { workspace.script_get(:graph, group: proj.group, ref: proj.allref)&.fetch(:graph, nil) }
1098
1044
  check = ->(deps) { deps.reject { |val| done.include?(val) } }
1099
1045
  dedupe = lambda do |name|
1100
1046
  next [] unless (ret = data[name])
@@ -1113,8 +1059,6 @@ module Squared
1113
1059
  else
1114
1060
  items = check.call(data[start])
1115
1061
  end
1116
- return done if items.empty?
1117
-
1118
1062
  if out
1119
1063
  a, b, c, d, e = ARG[:GRAPH]
1120
1064
  f = tag.call(target)
@@ -1136,7 +1080,7 @@ module Squared
1136
1080
 
1137
1081
  t = dedupe.call(proj.name)
1138
1082
  j = if out
1139
- if i == items.size - 1 || check.call(post = items[(i + 1)..-1]).empty?
1083
+ if i == items.size - 1 || check.call(post = items[i + 1..-1]).empty?
1140
1084
  true
1141
1085
  elsif !t.empty? && depth > 0
1142
1086
  post.reject { |pr| t.include?(pr) }.empty?
@@ -1147,7 +1091,10 @@ module Squared
1147
1091
  single: single, last: j == true, context: target)
1148
1092
  end
1149
1093
  if !out
1150
- (tasks || (subtasks = script.call(proj)) || (dev? ? %w[build copy] : %w[depend build])).each do |meth|
1094
+ if !tasks && (script = workspace.script_get(:graph, group: proj.group, ref: proj.allref))
1095
+ group = script[:graph]
1096
+ end
1097
+ (tasks || group || (dev? ? ['build', 'copy'] : ['depend', 'build'])).each do |meth|
1151
1098
  next if pass.include?(meth)
1152
1099
 
1153
1100
  if workspace.task_defined?(cmd = task_join(proj.name, meth))
@@ -1158,7 +1105,7 @@ module Squared
1158
1105
  end
1159
1106
  run(cmd, sync: false, banner: false)
1160
1107
  ENV.delete(key) if key
1161
- elsif proj.has?(meth, tasks || subtasks ? nil : workspace.baseref)
1108
+ elsif proj.has?(meth, tasks || group ? nil : workspace.baseref)
1162
1109
  proj.__send__(meth.to_sym, sync: sync)
1163
1110
  end
1164
1111
  end
@@ -1183,7 +1130,7 @@ module Squared
1183
1130
  done
1184
1131
  end
1185
1132
 
1186
- def graph_collect(target, start = [], data: {}, pass: [], root: [])
1133
+ def graph_collect(target, start = [], data: {}, pass: [])
1187
1134
  deps = []
1188
1135
  (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
1189
1136
  next if pass.include?(val)
@@ -1195,13 +1142,12 @@ module Squared
1195
1142
  else
1196
1143
  items = workspace.find(group: val, ref: val.to_sym)
1197
1144
  end
1145
+
1198
1146
  items.each do |proj|
1199
- next if pass.include?(name = proj.name)
1147
+ next if pass.include?(proj.name)
1200
1148
 
1201
- if proj.graph? && !data.key?(name) && !root.include?(name)
1202
- graph_collect(proj, data: data, pass: pass, root: root + [name, target.name])
1203
- end
1204
- next if (objs = data.fetch(name, [])).include?(target)
1149
+ graph_collect(proj, data: data, pass: pass) if proj.graph? && !data.key?(proj.name)
1150
+ next if (objs = data.fetch(proj.name, [])).include?(target)
1205
1151
 
1206
1152
  deps << proj
1207
1153
  deps.concat(objs)
@@ -1241,23 +1187,31 @@ module Squared
1241
1187
  end
1242
1188
  return ret == equals.to_s unless equals.nil?
1243
1189
 
1244
- ret.empty? || (ignore && Array(ignore).any? { |val| ret == val.to_s }) ? default : ret
1190
+ ret.empty? || (ignore && as_a(ignore).any? { |val| ret == val.to_s }) ? default : ret
1245
1191
  end
1246
1192
 
1247
1193
  def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
1248
- prefix = stripext prefix.to_s
1249
- if path && (val = shell_bin(prefix))
1194
+ prefix = stripext(prefix.to_s).upcase
1195
+ if path && (val = ENV["PATH_#{prefix}"] || PATH[prefix] || PATH[prefix.to_sym])
1250
1196
  cmd[0] = shell_quote(val, force: false)
1251
1197
  end
1252
1198
  ret = JoinSet.new(cmd.flatten(1))
1253
- if options && (val = env("#{prefix.upcase}_OPTIONS"))
1254
- split_escape(val).each { |opt| ret.last(fill_option(opt), /\A(--?[^\[\]=\s-][^\[\]=\s]*)[=\s].+\z/m) }
1199
+ if options && (val = env("#{prefix}_OPTIONS"))
1200
+ split_escape(val).each { |opt| ret.last(fill_option(opt), /\A(--?[^ =]+)[ =].+\z/m) }
1255
1201
  end
1256
1202
  main ? @session = ret : ret
1257
1203
  end
1258
1204
 
1259
1205
  def session_delete(*args, target: @session)
1260
- OptionPartition.delete(target, *args)
1206
+ ret = []
1207
+ args.each do |val|
1208
+ pat = /\A#{Regexp.escape(shell_option(val))}(?: |=|\z)/
1209
+ if (key = target.find { |opt| opt.match?(pat) })
1210
+ target.delete(key)
1211
+ ret << key
1212
+ end
1213
+ end
1214
+ ret
1261
1215
  end
1262
1216
 
1263
1217
  def session_output(*cmd, **kwargs)
@@ -1267,7 +1221,7 @@ module Squared
1267
1221
  def session_done(cmd)
1268
1222
  return cmd unless cmd.respond_to?(:done)
1269
1223
 
1270
- raise_error('no args added', hint: cmd.first) unless cmd.size > 1
1224
+ raise_error('no args added', hint: cmd.first || name) unless cmd.size > 1
1271
1225
  @session = nil if cmd == @session
1272
1226
  cmd.done
1273
1227
  end
@@ -1279,20 +1233,17 @@ module Squared
1279
1233
  end
1280
1234
 
1281
1235
  def option(*args, target: @session, prefix: target&.first, **kwargs)
1282
- return unless prefix
1236
+ if prefix
1237
+ args.each do |val|
1238
+ next unless (ret = env("#{stripext(prefix)}_#{val.gsub(/\W/, '_')}".upcase, **kwargs))
1239
+ return ret unless block_given?
1283
1240
 
1284
- args.each do |val|
1285
- ret = env(env_key(stripext(prefix), val), **kwargs)
1286
- return ret if ret
1241
+ return yield ret
1242
+ end
1287
1243
  end
1288
1244
  nil
1289
1245
  end
1290
1246
 
1291
- def option_sanitize(opts, list, target: @session, **kwargs)
1292
- op = OptionPartition.new(opts, list, target, project: self, **kwargs)
1293
- [op.extras, op.values]
1294
- end
1295
-
1296
1247
  def option_clear(opts, target: @session, **kwargs)
1297
1248
  return unless target
1298
1249
 
@@ -1303,19 +1254,13 @@ module Squared
1303
1254
  puts 'Success'
1304
1255
  end
1305
1256
 
1306
- def print_error(*args, loglevel: Logger::WARN, **kwargs)
1307
- return unless warning?
1308
-
1309
- warn log_message(loglevel, *args, **kwargs)
1310
- end
1311
-
1312
1257
  def print_item(*val)
1313
1258
  puts unless printfirst?
1314
1259
  printsucc
1315
1260
  puts val unless val.empty? || (val.size == 1 && val.first.nil?)
1316
1261
  end
1317
1262
 
1318
- def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle, **)
1263
+ def print_banner(*lines, styles: theme[:banner], border: borderstyle, client: false)
1319
1264
  pad = 0
1320
1265
  if styles
1321
1266
  if styles.any? { |s| s.to_s.end_with?('!') }
@@ -1324,7 +1269,7 @@ module Squared
1324
1269
  styles = [:bold] + styles
1325
1270
  end
1326
1271
  end
1327
- n = Project.max_width(lines)
1272
+ n = line_width lines
1328
1273
  ch = ' ' * pad
1329
1274
  index = -1
1330
1275
  lines.map! do |val|
@@ -1339,64 +1284,45 @@ module Squared
1339
1284
  (lines << sub_style(ARG[:BORDER][1] * n, styles: border)).join("\n")
1340
1285
  end
1341
1286
 
1342
- def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle, **)
1343
- n = Project.max_width(lines)
1287
+ def print_footer(*lines, sub: nil, reverse: false, right: false, **kwargs)
1288
+ n = line_width lines
1344
1289
  lines.map! do |val|
1345
1290
  s = right ? val.rjust(n) : val.ljust(n)
1346
1291
  sub&.each { |h| s = sub_style(s, **h) }
1347
1292
  s
1348
1293
  end
1349
- ret = [sub_style(ARG[:BORDER][1] * n, styles: border)].concat(lines)
1294
+ ret = [sub_style(ARG[:BORDER][1] * n, styles: kwargs.key?(:border) ? kwargs[:border] : borderstyle), *lines]
1350
1295
  ret.reverse! if reverse
1351
1296
  ret.join("\n")
1352
1297
  end
1353
1298
 
1354
- def print_status(*args, from: nil, **kwargs)
1355
- return if stdin?
1356
-
1357
- case from
1358
- when :outdated
1359
- out = print_footer("major #{args[0]} / minor #{args[1]} / patch #{args[2]}", right: true).split("\n")
1360
- out[1] = sub_style(out[1], pat: /^( +major )(\d+)(.+)$/, styles: theme[:major], index: 2)
1361
- out[1] = sub_style(out[1], pat: /^(.+)(minor )(\d+)(.+)$/, styles: theme[:active], index: 3)
1362
- puts out
1363
- when :completed
1364
- if verbose && kwargs[:start]
1365
- msg = sub_style('completed', styles: theme[:active])
1366
- puts log_message(Logger::INFO, *args, msg, subject: kwargs[:subject],
1367
- hint: time_format(epochtime - kwargs[:start]))
1368
- end
1369
- end
1370
- end
1371
-
1372
1299
  def format_desc(action, flag, opts = nil, **kwargs)
1373
1300
  return unless TASK_METADATA
1374
1301
 
1375
- ret = [@desc, action]
1376
- ret << flag if flag
1377
- workspace.format_desc(ret, opts, **kwargs)
1302
+ workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
1378
1303
  end
1379
1304
 
1380
1305
  def format_banner(cmd, banner: true)
1381
1306
  return unless banner && banner?
1382
1307
 
1383
1308
  if (data = workspace.banner_get(*@ref, group: group))
1384
- return if data.empty?
1309
+ return if !data.command && data.order.empty?
1385
1310
 
1386
1311
  client = true
1387
1312
  else
1388
- data = { command: true, order: [:path], styles: theme[:banner], border: theme[:border] }
1313
+ data = Workspace::Support::BannerData.new(true, [:path], theme[:banner], theme[:border])
1389
1314
  end
1390
1315
  if verbose
1391
1316
  out = []
1392
- if data[:command]
1317
+ if data.command
1393
1318
  if cmd =~ /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+))( |\z)/
1394
1319
  path = $3 || $2 || $1
1395
- cmd = cmd.sub(path, stripext(path).upcase)
1320
+ name = stripext path
1321
+ cmd = cmd.sub(path, data.command == 0 ? name : name.upcase)
1396
1322
  end
1397
1323
  out << cmd
1398
1324
  end
1399
- data[:order].each do |val|
1325
+ data.order.each do |val|
1400
1326
  if val.is_a?(Array)
1401
1327
  s = ' '
1402
1328
  found = false
@@ -1406,7 +1332,7 @@ module Squared
1406
1332
  meth
1407
1333
  elsif respond_to?(meth)
1408
1334
  found = true
1409
- __send__(meth)
1335
+ __send__ meth
1410
1336
  end
1411
1337
  end
1412
1338
  val = val.compact.join(s)
@@ -1416,9 +1342,9 @@ module Squared
1416
1342
  end
1417
1343
  out << val.to_s
1418
1344
  end
1419
- print_banner(*out, styles: data[:styles], border: data[:border], client: client)
1345
+ print_banner(*out, styles: data.styles, border: data.border, client: client)
1420
1346
  elsif workspace.series.multiple?
1421
- "## #{__send__(data[:order].first || :path)} ##"
1347
+ "## #{__send__(data.order.first || :path)} ##"
1422
1348
  end
1423
1349
  end
1424
1350
 
@@ -1428,9 +1354,9 @@ module Squared
1428
1354
  unless items.empty?
1429
1355
  pad = items.size.to_s.size
1430
1356
  items.each_with_index do |val, i|
1431
- next unless matchany?(val.first, reg)
1357
+ next unless reg.empty? || reg.any? { |pat| val[0].match?(pat) }
1432
1358
 
1433
- out << "#{i.succ.to_s.rjust(pad)}. #{each ? each.call(val) : val.first}"
1359
+ out << "#{i.succ.to_s.rjust(pad)}. #{each ? each.call(val) : val[0]}"
1434
1360
  end
1435
1361
  end
1436
1362
  sub = [headerstyle]
@@ -1442,13 +1368,13 @@ module Squared
1442
1368
  out << ''
1443
1369
  end
1444
1370
  if from
1445
- out << (from = from.to_s)
1371
+ out << from
1446
1372
  pat = /\A(#{Regexp.escape(from)})(.*)\z/
1447
1373
  end
1448
1374
  else
1449
1375
  pat = /\A(\s*\d+\.)(.+)\z/
1450
1376
  unless grep.empty?
1451
- footer = "#{out.size} found "
1377
+ footer = "#{out.size} found"
1452
1378
  sub << { pat: /\A(\d+)( .+)\z/, styles: theme[:inline] }
1453
1379
  end
1454
1380
  end
@@ -1460,11 +1386,11 @@ module Squared
1460
1386
  "#{msg}#{!always && (!obj || obj == 0 || obj.to_s.empty?) ? '' : message(hint: message(title, obj.to_s))}"
1461
1387
  end
1462
1388
 
1463
- def append_repeat(flag, opts, target: @session, **kwargs)
1464
- opts.each { |val| target << shell_option(flag, val, **kwargs) }
1389
+ def append_repeat(flag, opts, target: @session)
1390
+ opts.each { |val| target << shell_option(flag, val) }
1465
1391
  end
1466
1392
 
1467
- def append_hash(data, target: @session || [], build: false)
1393
+ def append_hash(data, target: @session, build: false)
1468
1394
  if build && (type = env('BUILD', suffix: 'TYPE') || ENV['BUILD_TYPE'])
1469
1395
  type = "__#{type}__"
1470
1396
  if (extra = data[type] || data[type.to_sym]).is_a?(Hash)
@@ -1477,7 +1403,7 @@ module Squared
1477
1403
  next if (key = key.to_s).start_with?('__')
1478
1404
 
1479
1405
  if val.nil? || extra || session_arg?(key, target: target)
1480
- OptionPartition.delete_key(target, key)
1406
+ session_delete(key, target: target)
1481
1407
  next if val.nil?
1482
1408
  end
1483
1409
  case val
@@ -1487,8 +1413,6 @@ module Squared
1487
1413
  target << basic_option(key, val)
1488
1414
  when FalseClass
1489
1415
  target << shell_option(key).sub(/^--(?!no-)/, '--no-')
1490
- when Pathname
1491
- target << shell_option(key, val, escape: false)
1492
1416
  else
1493
1417
  target << shell_option(key, val.is_a?(String) ? val : nil)
1494
1418
  end
@@ -1497,14 +1421,15 @@ module Squared
1497
1421
  end
1498
1422
 
1499
1423
  def append_any(val, target: @session, build: false, delim: false)
1424
+ return unless val
1425
+
1500
1426
  if delim && !target.include?('--')
1501
1427
  target << '--'
1502
1428
  else
1503
1429
  delim = false
1504
1430
  end
1431
+ val = shell_split(val) if val.is_a?(String)
1505
1432
  case val
1506
- when String
1507
- target << val
1508
1433
  when Hash
1509
1434
  append_hash(val, target: target, build: build)
1510
1435
  when Enumerable
@@ -1535,7 +1460,7 @@ module Squared
1535
1460
  return target << (if flag
1536
1461
  shell_option(opt, equals ? val : nil, quote: quote, escape: escape, force: force)
1537
1462
  else
1538
- shell_quote(val)
1463
+ shell_quote val
1539
1464
  end)
1540
1465
  end
1541
1466
  nil
@@ -1545,18 +1470,18 @@ module Squared
1545
1470
  **kwargs)
1546
1471
  return if list.empty?
1547
1472
 
1548
- kwargs[:ignore] = false if no && !kwargs.key?(:ignore)
1549
- ret = []
1550
- list.flatten.each do |flag|
1551
- next unless (val = option(flag, target: target, **kwargs))
1473
+ [].tap do |ret|
1474
+ list.flatten.each do |flag|
1475
+ next unless (val = option(flag, target: target, **kwargs))
1552
1476
 
1553
- if no && val == '0'
1554
- flag = "no-#{flag}"
1555
- val = nil
1477
+ if val == '0' && no
1478
+ flag = "no-#{flag}"
1479
+ val = nil
1480
+ end
1481
+ ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1556
1482
  end
1557
- ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1483
+ ret.each { |val| target << val } unless ret.empty?
1558
1484
  end
1559
- ret.each { |val| target << val } unless ret.empty?
1560
1485
  end
1561
1486
 
1562
1487
  def append_nocolor(target: @session)
@@ -1570,7 +1495,7 @@ module Squared
1570
1495
  when String
1571
1496
  "#{base} #{data}"
1572
1497
  when Hash
1573
- "#{append_hash(base, target: []).join(' ')} #{data}"
1498
+ "#{append_hash(base).join(' ')} #{data}"
1574
1499
  when Enumerable
1575
1500
  "#{base.to_a.join(' ')} #{data}"
1576
1501
  else
@@ -1579,11 +1504,11 @@ module Squared
1579
1504
  when Hash
1580
1505
  case base
1581
1506
  when String
1582
- "#{base} #{append_hash(data, target: []).join(' ')}"
1507
+ "#{base} #{append_hash(data).join(' ')}"
1583
1508
  when Hash
1584
1509
  base.merge(data)
1585
1510
  when Enumerable
1586
- Set.new(base.to_a + append_hash(data, target: [])).to_a
1511
+ Set.new(base.to_a + append_hash(data)).to_a
1587
1512
  else
1588
1513
  data
1589
1514
  end
@@ -1592,7 +1517,7 @@ module Squared
1592
1517
  when String
1593
1518
  "#{base} #{data.to_a.join(' ')}"
1594
1519
  when Hash
1595
- "#{append_hash(base, target: []).join(' ')} #{data.to_a.join(' ')}"
1520
+ "#{append_hash(base).join(' ')} #{data.to_a.join(' ')}"
1596
1521
  when Enumerable
1597
1522
  Set.new(base.to_a + data.to_a).to_a
1598
1523
  else
@@ -1604,16 +1529,9 @@ module Squared
1604
1529
  end
1605
1530
 
1606
1531
  def collect_hash(data, pass: [])
1607
- ret = []
1608
- data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1609
- ret
1610
- end
1611
-
1612
- def replace_bin(val)
1613
- a, b = val.split(' ', 2)
1614
- return val if val.start_with?(/["']/) || a.include?(File::Separator)
1615
-
1616
- [shell_bin(a), b].compact.join(' ')
1532
+ [].tap do |ret|
1533
+ data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1534
+ end
1617
1535
  end
1618
1536
 
1619
1537
  def parse_json(val, kind: Hash, hint: nil)
@@ -1621,20 +1539,11 @@ module Squared
1621
1539
  raise_error("invalid JSON #{kind.name}", val, hint: hint) if kind && !ret.is_a?(kind)
1622
1540
  rescue StandardError => e
1623
1541
  log&.warn e
1624
- print_error(e, subject: name)
1542
+ warn log_message(Logger::WARN, e, subject: name, pass: true) if warning?
1625
1543
  else
1626
1544
  ret
1627
1545
  end
1628
1546
 
1629
- def fetch_uri(*args, **kwargs, &blk)
1630
- require 'open-uri'
1631
- if RUBY_VERSION < '2.5'
1632
- open(*args, **kwargs, &blk)
1633
- else
1634
- URI.open(*args, **kwargs, &blk)
1635
- end
1636
- end
1637
-
1638
1547
  def param_guard(action, flag, args:, key: nil, pat: nil, values: nil)
1639
1548
  if args && key
1640
1549
  val = args.fetch(key, nil)
@@ -1649,7 +1558,7 @@ module Squared
1649
1558
  args
1650
1559
  end
1651
1560
 
1652
- def confirm_outdated(pkg, ver, rev, lock: false)
1561
+ def confirm_outdated(pkg, ver, rev, cur = nil, lock: false, col1: 0)
1653
1562
  a = sub_style(case rev
1654
1563
  when 1
1655
1564
  'MAJOR'
@@ -1657,49 +1566,53 @@ module Squared
1657
1566
  'MINOR'
1658
1567
  else
1659
1568
  'PATCH'
1660
- end, styles: theme[:header])
1661
- b = sub_style("#{pkg} #{ver}", styles: theme[:inline])
1569
+ end, styles: (rev == 1 && theme[:major]) || theme[:header])
1570
+ b = sub_style(pkg.ljust(col1), styles: theme[:inline])
1662
1571
  c, d = rev == 1 || lock ? ['y/N', 'N'] : ['Y/n', 'Y']
1663
- e = lock ? " #{sub_style('(locked)', styles: color(:red))}" : ''
1664
- confirm "Upgrade to #{a}? #{b + e} [#{c}] ", d
1572
+ e = lock ? sub_style((cur || 'locked').rjust(7), styles: color(:red)) : cur&.rjust(7)
1573
+ confirm "#{a}: #{b}#{e} #{sub_style(ver.rjust(col1 > 0 ? 10 : 0), styles: theme[:inline])} [#{c}] ", d
1665
1574
  end
1666
1575
 
1667
- def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil, multiple: false,
1668
- force: true, **kwargs)
1576
+ def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil,
1577
+ multiple: false, force: true, **kwargs)
1669
1578
  puts if !series && !printfirst?
1670
1579
  msg = "#{msg} (optional)" unless force
1671
- unless (ret = choice(msg, list, multiple: multiple, force: force, **kwargs)) && !ret.empty?
1672
- exit 1 if force
1673
- return
1580
+ unless (ret = choice(msg, list, multiple: multiple, force: force, **kwargs)) || !force
1581
+ raise_error 'user cancelled'
1582
+ end
1583
+ if ret.nil? || ret.empty?
1584
+ return unless force
1585
+
1586
+ exit 1
1674
1587
  end
1675
1588
  ret = multiple ? ret.map! { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
1676
1589
  if column
1677
- a, b = Array(column)
1678
- ret = Array(ret).map! { |val| val[a, b || 1] }
1590
+ a, b = as_a column
1591
+ ret = as_a(ret).map! { |val| val[a, b || 1] }
1679
1592
  ret = ret.first unless multiple
1680
1593
  end
1681
1594
  if accept
1682
- hint = Array(ret).map { |val| sub_style(val, styles: theme[:inline]) }.join(', ')
1683
- accept = Array(accept).map { |val| Array(val) }
1684
- ret = Array(ret) if accept.any? { |val| val[1] == true }
1595
+ a = as_a(ret).map { |val| sub_style(val, styles: theme[:inline]) }.join(', ')
1596
+ accept = as_a(accept).map { |val| as_a(val) }
1597
+ if accept.any? { |val| val[1] == true }
1598
+ ret = [ret]
1599
+ multiple = -1
1600
+ end
1685
1601
  loop do
1686
- item = accept.first
1687
- d, e = item[2] ? ['Y', '[Y/n]'] : ['N', '[y/N]']
1688
- c = confirm("#{item[0]}#{hint ? " [#{hint}]" : ''} #{e} ", d, timeout: 60)
1689
- if item[1] == true
1602
+ c = confirm("#{accept.first[0]}#{a ? " [#{a}]" : ''} [y/N] ", 'N', timeout: 60)
1603
+ if accept.shift[1] == true
1690
1604
  ret << c
1691
1605
  elsif !c
1692
1606
  break
1693
1607
  end
1694
- hint = nil
1695
- accept.shift
1608
+ a = nil
1696
1609
  break if accept.empty?
1697
1610
  end
1698
1611
  exit 1 unless accept.empty?
1699
1612
  end
1700
1613
  if values
1701
- ret = Array(ret)
1702
- Array(values).each do |val|
1614
+ ret = [ret] unless accept && multiple == -1
1615
+ values.each do |val|
1703
1616
  if val.is_a?(Array)
1704
1617
  val, force = val
1705
1618
  else
@@ -1713,8 +1626,8 @@ module Squared
1713
1626
  ret
1714
1627
  end
1715
1628
 
1716
- def command_args(args, min: 0, force: false, **kwargs)
1717
- return if args.size > min || option('i', 'interactive', **kwargs, equals: '0')
1629
+ def command_args(args, force: false, **kwargs)
1630
+ return unless args.size == 1 && !option('i', 'interactive', **kwargs, equals: '0')
1718
1631
 
1719
1632
  readline('Enter arguments', force: force)
1720
1633
  end
@@ -1723,7 +1636,7 @@ module Squared
1723
1636
  if (ret = instance_eval(&blk)).nil?
1724
1637
  val
1725
1638
  else
1726
- Array(ret)
1639
+ ret.is_a?(Array) ? ret : [ret]
1727
1640
  end
1728
1641
  end
1729
1642
 
@@ -1732,17 +1645,21 @@ module Squared
1732
1645
  end
1733
1646
 
1734
1647
  def command(*args)
1735
- return args.join(' && ') unless workspace.powershell?
1736
-
1737
- "#{shell_bin('powershell.exe')} -Command \"& {#{args.join(' ; ')}}\""
1648
+ if workspace.powershell?
1649
+ "powershell.exe -Command \"& {#{args.join(' ; ')}}\""
1650
+ else
1651
+ args.join(' && ')
1652
+ end
1738
1653
  end
1739
1654
 
1740
1655
  def relativepath(*list, all: false)
1741
1656
  return [] if list.empty?
1742
1657
 
1743
1658
  list.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
1744
- ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
1745
- all && val.to_s.end_with?('/') ? "#{ret}/*" : ret
1659
+ val = val.absolute? ? val.relative_path_from(path).to_s : val.to_s
1660
+ val = val[2..-1] if val.start_with?('./')
1661
+ val = "#{val}*" if all && val.end_with?('/')
1662
+ val
1746
1663
  end
1747
1664
  end
1748
1665
 
@@ -1752,18 +1669,7 @@ module Squared
1752
1669
  raise_error 'pathspec not within worktree' unless pass || files.size == proj.size
1753
1670
  files = proj
1754
1671
  end
1755
- files.map { |val| val == '.' ? '.' : shell_quote(basepath(val)) }
1756
- end
1757
-
1758
- def matchmap(list, prefix = nil)
1759
- list.map do |val|
1760
- if val.is_a?(Regexp)
1761
- val
1762
- else
1763
- val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1764
- Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1765
- end
1766
- end
1672
+ files.map { |val| val == '.' ? '.' : shell_quote(path + val) }
1767
1673
  end
1768
1674
 
1769
1675
  def semver(val)
@@ -1779,53 +1685,23 @@ module Squared
1779
1685
  end
1780
1686
 
1781
1687
  def semscan(val, fill: true)
1782
- ret = val.scan(SEM_VER).first
1783
- fill ? semver(ret) : ret
1784
- end
1785
-
1786
- def semcmp(val, other)
1787
- return 0 if val == other
1788
- return -1 if (b = other.scan(SEM_VER)).empty?
1789
- return 1 if (a = val.scan(SEM_VER)).empty?
1790
-
1791
- a, b = [a.first, b.first].map! do |c|
1792
- begin
1793
- d = Integer(c[5]).to_s
1794
- rescue StandardError
1795
- d = c[5] ? '-1' : '0'
1796
- end
1797
- [c[0], c[2], c[4] || '0', d]
1798
- end
1799
- a.each_with_index do |c, index|
1800
- next if c == (d = b[index])
1801
-
1802
- return c.to_i < d.to_i ? 1 : -1
1803
- end
1804
- 0
1805
- end
1806
-
1807
- def semgte?(val, other)
1808
- semcmp(val, other) != 1
1688
+ val.scan(SEM_VER).first.yield_self { |data| fill ? semver(data) : data }
1809
1689
  end
1810
1690
 
1811
1691
  def indexitem(val)
1812
- [$1.to_i, $2 && $2[1..-1]] if val =~ /\A[=^#{indexchar}](\d+)(:.+)?\z/
1692
+ [$1.to_i, $2 && $2[1..-1]] if val =~ /\A\^(\d+)(:.+)?\z/
1813
1693
  end
1814
1694
 
1815
1695
  def indexerror(val, list = nil)
1816
1696
  raise_error("requested index #{val}", hint: list && "of #{list.size}")
1817
1697
  end
1818
1698
 
1819
- def indexchar
1820
- workspace.windows? ? '=' : '^'
1821
- end
1822
-
1823
1699
  def printsucc
1824
1700
  @@print_order += 1
1825
1701
  end
1826
1702
 
1827
1703
  def color(val)
1828
- (ret = theme[val]) && !ret.empty? ? ret : [val]
1704
+ theme[val].yield_self { |styles| styles && !styles.empty? ? styles : [val] }
1829
1705
  end
1830
1706
 
1831
1707
  def colormap(val)
@@ -1848,9 +1724,9 @@ module Squared
1848
1724
  end
1849
1725
 
1850
1726
  def on(event, from, *args, **kwargs)
1851
- return unless from && @events.key?(event)
1727
+ return unless from && (data = @events[event])
1852
1728
 
1853
- Array(@events[event][from]).each do |obj|
1729
+ data[from]&.each do |obj|
1854
1730
  target, opts = if obj.is_a?(Array) && obj[1].is_a?(Hash)
1855
1731
  [obj[0], kwargs.empty? ? obj[1] : obj[1].merge(kwargs)]
1856
1732
  else
@@ -1867,52 +1743,32 @@ module Squared
1867
1743
  end
1868
1744
  end
1869
1745
 
1870
- def on_error(err, from, exception: self.exception, pass: false, dryrun: false)
1871
- log&.error err
1872
- unless dryrun
1873
- ret = on :error, from, err
1874
- raise err if exception && ret != true
1875
- end
1876
- print_error(err, pass: pass) unless ret
1877
- end
1878
-
1879
- def pwd_set(done = nil, pass: false, from: nil, dryrun: false)
1880
- pwd = Pathname.pwd
1881
- if block_given?
1882
- begin
1883
- unless (path == pwd || pass == true) && (workspace.mri? || !workspace.windows?)
1884
- restore = true
1885
- Dir.chdir(path)
1886
- end
1887
- yield
1888
- rescue StandardError => e
1889
- on_error(e, from, dryrun: dryrun)
1890
- ensure
1891
- Dir.chdir(pwd) if restore
1892
- end
1893
- elsif @pwd == pwd
1894
- @pwd = nil
1895
- pwd unless done
1896
- elsif @pwd
1897
- return unless path == pwd
1898
-
1899
- Dir.chdir(@pwd)
1900
- @pwd = nil
1901
- elsif !done && path != pwd
1902
- @pwd = pwd
1746
+ def pwd_set(pass: false, from: nil)
1747
+ pwd = Dir.pwd
1748
+ if path.to_s == pwd || pass == true || (pass.is_a?(String) && semscan(pass).join <= RUBY_VERSION)
1749
+ ret = yield
1750
+ else
1903
1751
  Dir.chdir(path)
1904
- @pwd
1752
+ ret = yield
1753
+ Dir.chdir(pwd)
1905
1754
  end
1755
+ rescue StandardError => e
1756
+ puts e
1757
+ log&.error e
1758
+ ret = on(:error, from, e)
1759
+ raise if exception && ret != true
1760
+ else
1761
+ ret
1906
1762
  end
1907
1763
 
1908
1764
  def run_set(cmd, val = nil, opts: nil, **)
1909
- noopt = @output[1] == false && !@output[0].nil?
1910
- noenv = @output[2] == false
1765
+ diso = @output[1] == false && !@output[0].nil?
1766
+ dise = @output[2] == false
1911
1767
  parse = lambda do |data|
1912
1768
  ret = []
1913
1769
  if data[:command]
1914
1770
  ret[0] = data[:command]
1915
- ret[1] = data[:opts] unless noopt
1771
+ ret[1] = data[:opts] unless diso
1916
1772
  ret[3] = data[:args]
1917
1773
  elsif data[:script]
1918
1774
  ret[1] = data[:script]
@@ -1921,14 +1777,14 @@ module Squared
1921
1777
  else
1922
1778
  ret[0] = false
1923
1779
  end
1924
- ret[2] = data[:env] unless noenv
1780
+ ret[2] = data[:env] unless dise
1925
1781
  ret
1926
1782
  end
1927
1783
  case cmd
1928
1784
  when Array
1929
- @output = if cmd.all?(Hash)
1930
- noopt = false
1931
- noenv = false
1785
+ @output = if cmd.all? { |data| data.is_a?(Hash) }
1786
+ diso = false
1787
+ dise = false
1932
1788
  cmd.map { |data| parse.call(data) }
1933
1789
  else
1934
1790
  cmd.dup
@@ -1939,14 +1795,14 @@ module Squared
1939
1795
  else
1940
1796
  @output[0] = cmd
1941
1797
  end
1942
- unless noopt
1798
+ unless diso
1943
1799
  if opts == false
1944
1800
  @output[1] = false
1945
1801
  elsif opts && opts != true
1946
1802
  @output[1] = opts
1947
1803
  end
1948
1804
  end
1949
- return if noenv
1805
+ return if dise
1950
1806
 
1951
1807
  if val.is_a?(Hash)
1952
1808
  @output[2] = val
@@ -1975,18 +1831,33 @@ module Squared
1975
1831
  @parent = val if val.is_a?(Project::Base)
1976
1832
  end
1977
1833
 
1834
+ def exception_set(val)
1835
+ @exception = env_bool(val, workspace.exception, strict: true)
1836
+ end
1837
+
1838
+ def pipe_set(val)
1839
+ @pipe = env_pipe(val, workspace.pipe, strict: true)
1840
+ end
1841
+
1842
+ def verbose_set(val)
1843
+ @verbose = case val
1844
+ when NilClass
1845
+ workspace.verbose
1846
+ when String
1847
+ env_bool(val, workspace.verbose, strict: true, index: true)
1848
+ else
1849
+ val
1850
+ end
1851
+ end
1852
+
1978
1853
  def graph_set(val)
1979
1854
  @graph = if val
1980
- Array(val).map { |s| workspace.prefix ? workspace.task_name(s).to_sym : s.to_sym }.freeze
1855
+ as_a(val).map { |s| workspace.prefix ? workspace.task_name(s).to_sym : s.to_sym }.freeze
1981
1856
  end
1982
1857
  end
1983
1858
 
1984
1859
  def pass_set(val)
1985
- @pass = Array(val).freeze
1986
- end
1987
-
1988
- def only_set(val)
1989
- @only = val && as_a(val, :to_s).freeze
1860
+ @pass = (val ? as_a(val, :to_s) : []).freeze
1990
1861
  end
1991
1862
 
1992
1863
  def exclude_set(val)
@@ -2014,12 +1885,14 @@ module Squared
2014
1885
 
2015
1886
  def dependfile_set(list)
2016
1887
  @dependindex = list.index { |file| basepath(file).exist? }.tap do |index|
2017
- @dependfile = basepath(list[index || 0])
1888
+ @dependfile = @path + list[index || 0]
2018
1889
  end
2019
1890
  end
2020
1891
 
2021
- def as_get(val, from)
2022
- (@global && @as[from][val]) || val
1892
+ def as_get(val)
1893
+ return unless val
1894
+
1895
+ @global && (ret = @as && @as[from] && @as[from][val]) ? ret : val
2023
1896
  end
2024
1897
 
2025
1898
  def task_build(keys)
@@ -2032,7 +1905,7 @@ module Squared
2032
1905
  unless @pass.include?(key.to_s) || ws.task_defined?(name, action) || ws.task_exclude?(action, self)
2033
1906
  ws.task_desc(@desc, action)
2034
1907
  task action do
2035
- __send__(key)
1908
+ __send__ key
2036
1909
  end
2037
1910
  end
2038
1911
  next if (items = @children.select { |item| item.task_include?(key) }).empty?
@@ -2043,24 +1916,17 @@ module Squared
2043
1916
  end
2044
1917
  end
2045
1918
 
2046
- def task_pass?(key)
2047
- @only ? !@only.include?(key) : @pass.include?(key)
2048
- end
2049
-
2050
- def matchany?(val, list, empty: true)
2051
- list.empty? ? empty : list.any? { |pat| val.match?(pat) }
2052
- end
2053
-
2054
1919
  def projectpath?(val)
2055
- ret = Pathname.new(val).cleanpath
2056
- ret.absolute? ? ret.to_s.start_with?(File.join(path, '')) : !ret.to_s.start_with?(File.join('..', ''))
1920
+ Pathname.new(val).cleanpath.yield_self do |file|
1921
+ file.absolute? ? file.to_s.start_with?(File.join(path, '')) : !file.to_s.start_with?(File.join('..', ''))
1922
+ end
2057
1923
  end
2058
1924
 
2059
1925
  def checkdir?(val)
2060
1926
  if val.directory? && !val.empty?
2061
1927
  true
2062
1928
  else
2063
- log&.warn "directory \"#{val}\" (#{val.directory? ? 'empty' : 'not found'})"
1929
+ log&.warn "directory \"#{val}\" (#{val.empty? ? 'empty' : 'not found'})"
2064
1930
  false
2065
1931
  end
2066
1932
  end
@@ -2075,7 +1941,7 @@ module Squared
2075
1941
 
2076
1942
  def runnable?(val)
2077
1943
  case val
2078
- when String, Enumerable, Proc, Method, Struct
1944
+ when String, Enumerable, Proc, Method
2079
1945
  true
2080
1946
  else
2081
1947
  false
@@ -2105,22 +1971,17 @@ module Squared
2105
1971
  return false if workspace.series.chain?(val = task_join(name, action))
2106
1972
  return true if task_invoked?(val) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
2107
1973
 
2108
- val = workspace.series.name_get(action)
2109
- val != action && invoked_sync?(val)
1974
+ workspace.series.name_get(action).yield_self { |name| name != action && invoked_sync?(name) }
2110
1975
  end
2111
1976
 
2112
- def success?(ret, display = true)
2113
- ret == true && display && stdout? && banner?
1977
+ def success?(ret)
1978
+ ret == true && stdout? && banner?
2114
1979
  end
2115
1980
 
2116
1981
  def banner?
2117
1982
  ARG[:BANNER] && !env('BANNER', equals: '0')
2118
1983
  end
2119
1984
 
2120
- def pwd?
2121
- path == Pathname.pwd
2122
- end
2123
-
2124
1985
  def stdin?
2125
1986
  pipe == 0
2126
1987
  end
@@ -2134,9 +1995,14 @@ module Squared
2134
1995
  end
2135
1996
 
2136
1997
  def has_value?(data, other)
2137
- return false unless data.is_a?(Enumerable)
2138
-
2139
- other.is_a?(Enumerable) ? other.any? { |obj,| data.include?(obj) } : data.include?(other)
1998
+ case data
1999
+ when Hash
2000
+ other.is_a?(Enumerable) ? other.any? { |obj| data.value?(obj) } : data.value?(other)
2001
+ when Enumerable
2002
+ other.is_a?(Enumerable) ? other.any? { |obj| data.include?(obj) } : data.include?(other)
2003
+ else
2004
+ false
2005
+ end
2140
2006
  end
2141
2007
 
2142
2008
  def variables
@@ -2147,20 +2013,8 @@ module Squared
2147
2013
  BLK_SET
2148
2014
  end
2149
2015
 
2150
- def hashobj
2151
- Workspace::Support.hashobj
2152
- end
2153
-
2154
- def hashlist
2155
- Workspace::Support.hashlist
2156
- end
2157
-
2158
- def hashdup
2159
- Workspace::Support.hashdup
2160
- end
2161
-
2162
2016
  def borderstyle
2163
- ((data = workspace.banner_get(*@ref, group: group)) && data[:border]) || theme[:border]
2017
+ workspace.banner_get(*@ref, group: group)&.border || theme[:border]
2164
2018
  end
2165
2019
 
2166
2020
  def headerstyle
@@ -2173,7 +2027,7 @@ module Squared
2173
2027
  end
2174
2028
 
2175
2029
  Application.implement(Base, base: true)
2176
- Application.attr_banner = Common::SymSet.new(%i[name project path ref group parent])
2030
+ Application.attr_banner = Set.new(%i[name project path ref group parent])
2177
2031
  end
2178
2032
  end
2179
2033
  end