squared 0.4.25 → 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,8 +73,8 @@ module Squared
72
73
  'unpack' => %i[zip tar gem ext].freeze
73
74
  })
74
75
 
75
- attr_reader :name, :project, :workspace, :path, :theme, :group, :parent, :dependfile,
76
- :exception, :pipe, :verbose
76
+ attr_reader :name, :project, :workspace, :path, :theme, :exception, :pipe, :verbose,
77
+ :group, :parent, :dependfile
77
78
 
78
79
  def initialize(workspace, path, name, *, group: nil, first: {}, last: {}, error: {}, common: ARG[:COMMON],
79
80
  **kwargs)
@@ -82,31 +83,38 @@ module Squared
82
83
  @name = name.to_s.freeze
83
84
  @project = @path.basename.to_s.freeze
84
85
  @group = group&.to_s.freeze
85
- @envname = env_key(@name).freeze
86
86
  @depend = kwargs[:depend]
87
87
  @doc = kwargs[:doc]
88
88
  @lint = kwargs[:lint]
89
89
  @test = kwargs[:test]
90
90
  @copy = kwargs[:copy]
91
91
  @clean = kwargs[:clean]
92
+ @version = kwargs[:version]
92
93
  @release = kwargs[:release]
93
- self.version = kwargs[:version]
94
- self.exception = kwargs[:exception]
95
- self.pipe = kwargs[:pipe]
96
- self.verbose = kwargs[:verbose]
97
94
  @output = []
98
95
  @ref = []
99
96
  @children = []
100
- @events = hashobj.update({ first: first, last: last, error: error })
101
- @as = hashobj
97
+ @events = {
98
+ first: first,
99
+ last: last,
100
+ error: error
101
+ }
102
+ @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
102
103
  @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
103
104
  @parent = nil
104
105
  @global = false
106
+ @log = nil
107
+ @dev = nil
108
+ @prod = nil
109
+ @withargs = nil
110
+ @session = nil
105
111
  @index = -1
106
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]
107
116
  graph_set kwargs[:graph]
108
117
  pass_set kwargs[:pass]
109
- only_set kwargs[:only]
110
118
  exclude_set kwargs[:exclude]
111
119
  archive_set kwargs[:archive]
112
120
  theme_set common
@@ -168,7 +176,9 @@ module Squared
168
176
  def initialize_events(ref, **)
169
177
  return unless (events = @workspace.events_get(group: @group, ref: ref))
170
178
 
171
- 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
172
182
  end
173
183
 
174
184
  def initialize_logger(log: nil, **)
@@ -195,7 +205,7 @@ module Squared
195
205
  file &&= @workspace.home.join(env('LOG_DIR', ''), file).realdirpath
196
206
  rescue StandardError => e
197
207
  file = nil
198
- print_error e
208
+ warn log_message(Logger::WARN, e, pass: true) if warning?
199
209
  end
200
210
  log[:progname] ||= @name
201
211
  if (val = env('LOG_LEVEL', ignore: false))
@@ -208,7 +218,7 @@ module Squared
208
218
  def initialize_env(dev: nil, prod: nil, **)
209
219
  @dev = env_match('BUILD', dev, suffix: 'DEV', strict: true)
210
220
  @prod = env_match('BUILD', prod, suffix: 'PROD', strict: true)
211
- if (val = env('BUILD', suffix: 'ENV')) && @output[2] != false
221
+ if @output[2] != false && (val = env('BUILD', suffix: 'ENV'))
212
222
  @output[2] = parse_json(val, hint: "BUILD_#{@envname}_ENV") || @output[2]
213
223
  end
214
224
  unless @output[0] == false || @output[0].is_a?(Array)
@@ -233,26 +243,21 @@ module Squared
233
243
  end
234
244
  end
235
245
 
236
- def ==(other)
237
- equal?(other)
238
- end
239
-
240
246
  def <=>(other)
241
- return unless workspace == other.workspace
242
- return 0 if equal?(other)
247
+ return 0 unless workspace == other.workspace
243
248
 
244
249
  a, b = graph_deps
245
250
  return 1 if a.include?(other)
246
251
 
247
252
  c, d = graph_deps other
248
- e = b - d
249
- f = d - b
253
+ e = b.reject { |val| d.include?(val) }
254
+ f = d.reject { |val| b.include?(val) }
250
255
  if parent == other.parent
251
256
  g = []
252
257
  h = []
253
258
  else
254
- g = a - c
255
- h = c - a
259
+ g = a.reject { |val| c.include?(val) }
260
+ h = c.reject { |val| a.include?(val) }
256
261
  end
257
262
  g << self
258
263
  h << other
@@ -262,39 +267,18 @@ module Squared
262
267
  -1
263
268
  elsif h.any? { |val| e.include?(val) }
264
269
  1
265
- elsif e.any? { |val| f.include?(val) } # rubocop:disable Lint/DuplicateBranch
270
+ elsif e.any? { |val| f.include?(val) }
266
271
  -1
267
- elsif f.any? { |val| e.include?(val) } # rubocop:disable Lint/DuplicateBranch
272
+ elsif f.any? { |val| e.include?(val) }
268
273
  1
269
274
  elsif @index >= 0 && (i = other.instance_variable_get(:@index)) >= 0
270
- @index <=> i
275
+ @index <= i ? -1 : 1
276
+ else
277
+ 0
271
278
  end
272
279
  rescue StandardError => e
273
280
  log&.debug e
274
- nil
275
- end
276
-
277
- def version=(val)
278
- @version = val&.to_s
279
- end
280
-
281
- def exception=(val)
282
- @exception = env_bool(val, workspace.exception, strict: true)
283
- end
284
-
285
- def pipe=(val)
286
- @pipe = env_pipe(val, workspace.pipe, strict: true)
287
- end
288
-
289
- def verbose=(val)
290
- @verbose = case (val = env('VERBOSE', val))
291
- when NilClass
292
- workspace.verbose
293
- when String
294
- env_bool(val, workspace.verbose, strict: true, index: true)
295
- else
296
- val
297
- end
281
+ 0
298
282
  end
299
283
 
300
284
  def ref
@@ -307,41 +291,41 @@ module Squared
307
291
 
308
292
  namespace name do
309
293
  Base.subtasks do |action, flags|
310
- next if task_pass?(action)
294
+ next if @pass.include?(action)
311
295
 
312
296
  namespace action do
313
297
  flags.each do |flag|
314
298
  case action
315
299
  when 'graph'
316
- break unless graph?
300
+ next unless graph?
317
301
 
318
302
  format_desc action, flag, '(-)project*'
319
303
  task flag do |_, args|
320
- args = args.to_a.reject { |val| name == val.to_s }
304
+ args = args.to_a.reject { |val| name == val }
321
305
  if flag == :run
322
306
  graph args
323
307
  else
324
308
  out, done = graph(args, out: [])
325
309
  out.map! do |val|
326
310
  done.each_with_index do |proj, i|
327
- next unless val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/)
328
-
329
- val += " (#{i.succ})"
330
- break
311
+ if val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/)
312
+ val += " (#{i.succ})"
313
+ break
314
+ end
331
315
  end
332
316
  val
333
317
  end
334
318
  emphasize(out, title: path, right: true, border: borderstyle, sub: [
335
319
  { pat: /\A(#{Regexp.escape(path.to_s)})(.*)\z/, styles: theme[:header] },
336
320
  { pat: /\A(#{Regexp.escape(name)})(.*)\z/, styles: theme[:active] },
337
- { pat: /\A(.+ )(\()(\d+)(\))(.*)\z/, styles: theme[:inline], index: 3 }
321
+ { pat: /\A((?~ \() \()(\d+)(\).*)\z/, styles: theme[:inline], index: 2 }
338
322
  ])
339
323
  end
340
324
  end
341
325
  when 'unpack'
342
326
  format_desc(action, flag, 'tag/url,dir,digest?,f|force?', before: flag == :ext ? 'ext' : nil)
343
327
  params = %i[tag dir digest force]
344
- params.unshift(:ext) if flag == :ext
328
+ params.prepend(:ext) if flag == :ext
345
329
  task flag, params do |_, args|
346
330
  ext = flag == :ext ? param_guard(action, flag, args: args, key: :ext) : flag.to_s
347
331
  tag = param_guard(action, flag, args: args, key: :tag)
@@ -362,7 +346,7 @@ module Squared
362
346
  else
363
347
  force = args.fetch(:force, false)
364
348
  end
365
- unpack(basepath(dir), uri: tag, digest: digest, ext: ext, force: force)
349
+ unpack(path + dir, uri: tag, digest: digest, ext: ext, force: force)
366
350
  end
367
351
  end
368
352
  end
@@ -397,8 +381,8 @@ module Squared
397
381
  elsif !projectpath?(path = basepath(path)) || !checkdir?(path)
398
382
  return self
399
383
  end
400
- kwargs = hashdup(@withargs).update(kwargs) if @withargs
401
- kwargs[:group] = group if group && !kwargs.key?(:group)
384
+ kwargs = @withargs.yield_self { |data| data.dup.update(kwargs) } if @withargs
385
+ kwargs[:group] = group unless kwargs.key?(:group)
402
386
  kwargs[:ref] = ref unless kwargs.key?(:ref)
403
387
  parent = self
404
388
  proj = nil
@@ -411,9 +395,9 @@ module Squared
411
395
  workspace.add(path, name, **kwargs) do
412
396
  __send__ :parent_set, parent
413
397
  proj = self
414
- instance_eval(&blk) if block_given?
415
398
  end
416
399
  @children << proj
400
+ proj.instance_eval(&blk) if block_given?
417
401
  self
418
402
  end
419
403
 
@@ -427,7 +411,7 @@ module Squared
427
411
 
428
412
  out = obj.link(self, *args, **kwargs, &blk) if obj.respond_to?(:link)
429
413
  if !out
430
- print_error('link not compatible', subject: obj, hint: name)
414
+ warn log_message(Logger::WARN, 'link not compatible', subject: obj.to_s, hint: name, pass: true)
431
415
  elsif out.respond_to?(:build)
432
416
  out.build
433
417
  end
@@ -446,49 +430,45 @@ module Squared
446
430
  if args.first.is_a?(Struct)
447
431
  f, blk = args.first.to_a
448
432
  args[0] = instance_eval(&blk) || f
449
- return unless args.first
433
+ return unless args[0]
450
434
  end
451
435
  if args.all? { |val| val.is_a?(Array) }
452
436
  cmd = []
453
437
  var = {}
454
438
  args.each do |val|
455
- next instance_exec(*val[1..-1], &val.first) if val.first.is_a?(Proc)
439
+ next instance_exec(*val[1..-1], &val[0]) if val.first.is_a?(Proc)
456
440
 
457
441
  a, b, c, d, e = val
458
442
  case b
459
443
  when Hash
460
- b = append_hash(b, target: [], build: true).join(' ')
444
+ b = append_hash(b, build: true).join(' ')
461
445
  when Enumerable
462
446
  b = b.to_a.join(' ')
463
447
  end
464
- d = append_hash(d, target: []).join(' ') if d.is_a?(Hash)
448
+ d = append_hash(d).join(' ') if d.is_a?(Hash)
465
449
  if a
466
450
  cmd << [a, d, b].compact.join(' ')
467
451
  else
468
452
  next unless respond_to?(:compose)
469
453
 
470
- 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))
471
455
  end
472
- var.merge!(c) if c.is_a?(Hash)
456
+ var.update(c) if c.is_a?(Hash)
473
457
  end
474
458
  cmd = cmd.join(' && ')
475
459
  else
476
460
  cmd, opts, var, flags, extra = args
477
461
  end
478
462
  if cmd
479
- return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
480
-
481
- cmd = as_get(cmd, from)
463
+ cmd = as_get cmd
482
464
  opts = compose(opts, script: false) if opts && respond_to?(:compose)
483
- flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
465
+ flags = append_hash(flags).join(' ') if flags.is_a?(Hash)
484
466
  case opts
485
467
  when Hash
486
- cmd = Array(cmd).push(flags)
487
- .concat(append_hash(opts, target: [], build: true))
488
- .compact
489
- .join(' ')
468
+ opts = append_hash(opts, build: true)
469
+ cmd = as_a(cmd).append(flags).concat(opts).compact.join(' ')
490
470
  when Enumerable
491
- cmd = Array(cmd).concat(opts.to_a)
471
+ cmd = as_a(cmd).concat(opts.to_a)
492
472
  cmd.map! { |val| "#{val} #{flags}" } if flags
493
473
  cmd = cmd.join(' && ')
494
474
  else
@@ -497,7 +477,7 @@ module Squared
497
477
  else
498
478
  return unless (opts || extra) && respond_to?(:compose)
499
479
 
500
- cmd = compose(as_get(opts, from), flags, script: true, args: extra, from: from)
480
+ cmd = compose(as_get(opts), flags, script: true, args: extra, from: from)
501
481
  from = :script if from == :run && script?
502
482
  end
503
483
  run(cmd, var, sync: sync, from: from, banner: banner)
@@ -517,12 +497,16 @@ module Squared
517
497
  if proj.respond_to?(meth.to_sym)
518
498
  begin
519
499
  proj.__send__(meth, sync: sync)
520
- next
521
500
  rescue StandardError => e
522
- on_error(e, :prereqs, exception: true)
501
+ ret = on(:error, :prereqs, e)
502
+ raise unless ret == true
503
+ else
504
+ next
523
505
  end
524
506
  end
525
- print_error(name, 'method not found', subject: 'prereqs', hint: meth)
507
+ if warning?
508
+ warn log_message(Logger::WARN, name, 'method not found', subject: 'prereqs', hint: meth, pass: true)
509
+ end
526
510
  end
527
511
  elsif proj.build?
528
512
  proj.build(sync: sync)
@@ -539,7 +523,7 @@ module Squared
539
523
  end
540
524
 
541
525
  def doc(*, sync: invoked_sync?('doc'), **)
542
- 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)
543
527
  end
544
528
 
545
529
  def lint(*, sync: invoked_sync?('lint'), **)
@@ -560,9 +544,9 @@ module Squared
560
544
  on :first, :clean unless pass
561
545
  case @clean
562
546
  when Struct
563
- if (val = instance_eval(&@clean.block) || @clean.run)
547
+ if (any = instance_eval(&@clean.block) || @clean.run)
564
548
  temp = @clean
565
- @clean = val
549
+ @clean = any
566
550
  clean(*args, sync: sync, pass: true, **kwargs)
567
551
  @clean = temp
568
552
  end
@@ -572,15 +556,17 @@ module Squared
572
556
  begin
573
557
  @clean.each { |cmd, opts| build(cmd.to_s, opts, sync: sync) }
574
558
  rescue StandardError => e
575
- on_error e, from
559
+ log&.error e
560
+ ret = on(:error, from, e)
561
+ raise if exception && ret != true
576
562
  end
577
563
  else
578
564
  if @clean.is_a?(Enumerable) && !series?(@clean)
579
565
  @clean.each do |val|
580
- entry = basepath(val = val.to_s)
566
+ entry = path + (val = val.to_s)
581
567
  if entry.directory? && val.match?(%r{[\\/]\z})
582
568
  log&.warn "rm -rf #{entry}"
583
- rm_rf(entry, verbose: verbosetype > 0)
569
+ rm_rf(entry, verbose: verbose)
584
570
  else
585
571
  log&.warn "rm #{entry}"
586
572
  (val.include?('*') ? Dir[entry] : [entry]).each do |file|
@@ -621,7 +607,8 @@ module Squared
621
607
  end
622
608
  ret = graph_branch(self, data, tasks, out, sync: sync, pass: pass)
623
609
  rescue StandardError => e
624
- on_error(e, :graph, exception: true)
610
+ ret = on(:error, :graph, e)
611
+ raise unless ret == true
625
612
  else
626
613
  if out
627
614
  [out, ret]
@@ -630,13 +617,13 @@ module Squared
630
617
  end
631
618
  end
632
619
 
633
- def unpack(target, file = nil, uri: nil, sync: true, digest: nil, ext: nil, force: false, depth: 1, headers: {},
634
- 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'
635
622
  if !target.exist?
636
623
  target.mkpath
637
624
  elsif !target.directory?
638
625
  raise_error('invalid location', hint: target)
639
- elsif !file && !target.empty?
626
+ elsif !target.empty?
640
627
  raise_error('directory not empty', hint: target) unless force || env('UNPACK_FORCE')
641
628
  create = true
642
629
  end
@@ -657,66 +644,58 @@ module Squared
657
644
  when 128, 'sha512'
658
645
  Digest::SHA512
659
646
  else
660
- raise_error "invalid checksum: #{digest}"
647
+ raise_error("invalid checksum: #{digest}", hint: name)
661
648
  end
662
649
  end
663
650
  if (val = env('HEADERS')) && (val = parse_json(val, hint: "HEADERS_#{@envname}"))
664
651
  headers = headers.is_a?(Hash) ? headers.merge(val) : val
665
652
  end
666
- if file
667
- ext ||= File.extname(file)[1..-1]
668
- else
669
- data = nil
670
- (uri = Array(uri)).each_with_index do |url, index|
671
- fetch_uri(url, headers) do |f|
672
- data = f.read
673
- if algo && algo.hexdigest(data) != digest
674
- data = nil
675
- raise_error("checksum failed: #{digest}", hint: url) if index == uri.size - 1
676
- end
677
- next if ext && index == 0
678
-
679
- case f.content_type
680
- when 'application/zip'
681
- ext = 'zip'
682
- when %r{application/(?:x-)?gzip}
683
- ext = 'tgz'
684
- when 'application/x-xz'
685
- ext = 'txz'
686
- 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'
687
670
  end
688
- break uri = url if data
689
- end
690
- unless data && (ext ||= URI.parse(uri).path[/\.(\w+)(?:\?|\z)/, 1])
691
- raise_error("no content#{data ? ' type' : ''}", hint: uri)
692
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)
693
676
  end
694
677
  ext = ext.downcase
695
678
  if (val = env("#{%w[zip 7z gem].include?(ext) ? ext.upcase : 'TAR'}_DEPTH", ignore: false))
696
679
  depth = val.to_i
697
680
  end
698
681
  begin
699
- unless file
700
- if ext == 'gem'
701
- dir = Dir.mktmpdir
702
- file = File.new(File.join(dir, File.basename(uri)), 'w')
703
- else
704
- require 'tempfile'
705
- file = Tempfile.new("#{name}-")
706
- end
707
- file.write(data)
708
- file.close
709
- file = Pathname.new(file)
710
- 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}-")
711
688
  end
689
+ file.write(data)
690
+ file.close
712
691
  if create
713
- print_error('force remove', subject: name, hint: target)
692
+ warn log_message(Logger::WARN, 'force remove', subject: name, hint: target, pass: true)
714
693
  target.rmtree
715
694
  target.mkpath
716
695
  end
717
696
  case ext
718
697
  when 'zip', 'aar'
719
- session 'unzip', shell_quote(file), quote_option('d', target)
698
+ session 'unzip', shell_quote(file.path), quote_option('d', target)
720
699
  when 'tar', 'tgz', 'tar.gz', 'tar.xz', 'gz', 'xz'
721
700
  flags = +(verbose ? 'v' : '')
722
701
  if ext.end_with?('gz')
@@ -724,26 +703,28 @@ module Squared
724
703
  elsif ext.end_with?('xz')
725
704
  flags += 'J'
726
705
  end
727
- 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),
728
707
  quote_option('C', target)
729
708
  depth = 0
730
709
  when '7z'
731
- session '7z', 'x', shell_quote(file), "-o#{shell_quote(target)}"
710
+ session '7z', 'x', shell_quote(file.path), "-o#{shell_quote(target)}"
732
711
  when 'gem'
733
- session 'gem', 'unpack', shell_quote(file), quote_option('target', target)
712
+ session 'gem', 'unpack', shell_quote(file.path), quote_option('target', target)
734
713
  depth = 0 unless val
735
714
  else
736
- raise_error("unsupported format: #{ext}", hint: uri || file)
715
+ raise_error("unsupported format: #{ext}", hint: uri)
737
716
  end
738
- run(sync: sync, banner: verbose, from: from)
717
+ run(sync: sync, from: from)
739
718
  while depth > 0 && target.children.size == 1
740
719
  entry = target.children.first
741
720
  break unless entry.directory?
742
721
 
743
722
  i = 0
744
- 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
745
726
  FileUtils.mv(entry, dest)
746
- dest.children.each { |child| FileUtils.mv(child, target) }
727
+ dest.each_child { |child| FileUtils.mv(child, target) }
747
728
  dest.rmdir
748
729
  target = entry
749
730
  depth -= 1
@@ -751,8 +732,8 @@ module Squared
751
732
  ensure
752
733
  if dir
753
734
  remove_entry dir
754
- elsif delete && file&.exist?
755
- file.unlink
735
+ else
736
+ file&.unlink
756
737
  end
757
738
  end
758
739
  end
@@ -770,7 +751,7 @@ module Squared
770
751
  end
771
752
 
772
753
  def event(name, key, *args, override: false, **kwargs, &blk)
773
- data = @events[name.to_sym]
754
+ data = @events[name.to_sym] ||= {}
774
755
  items = if override
775
756
  data[key.to_sym] = []
776
757
  else
@@ -781,8 +762,9 @@ module Squared
781
762
  end
782
763
 
783
764
  def as(cmd, script, to = nil)
784
- data = @as[cmd.to_sym]
785
- (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 }
786
768
  self
787
769
  end
788
770
 
@@ -799,57 +781,14 @@ module Squared
799
781
  self
800
782
  end
801
783
 
802
- def run(cmd = @session, var = nil, exception: self.exception, sync: true, from: nil, banner: true, chdir: path,
803
- interactive: nil, hint: nil, **)
804
- unless cmd
805
- print_error('no command given', subject: project, hint: from || 'unknown', pass: true)
806
- return
807
- end
808
- cmd = cmd.target if cmd.is_a?(OptionPartition)
809
- if interactive && (!@session || !option('y'))
810
- title, y = case interactive
811
- when Array
812
- interactive
813
- when String
814
- [interactive, 'N']
815
- else
816
- ['Run', 'Y']
817
- end
818
- yn = y == 'Y' ? 'Y/n' : 'y/N'
819
- exit 1 unless confirm("#{title}? [#{sub_style(cmd.to_s, styles: theme[:inline])}] [#{yn}] ", y)
820
- end
821
- cmd = session_done cmd
822
- log&.info cmd
823
- on :first, from
824
- begin
825
- if cmd.match?(/\A[^:]+:[^:]/) && workspace.task_defined?(cmd)
826
- log&.warn "ENV discarded: #{var}" if var
827
- task_invoke(cmd, exception: exception, warning: warning?)
828
- else
829
- print_item format_banner(hint ? "#{cmd} (#{hint})" : cmd, banner: banner) if sync
830
- if var != false && (pre = runenv)
831
- case pre
832
- when Hash
833
- var = var.is_a?(Hash) ? pre.merge(var) : pre
834
- when Enumerable
835
- cmd = command(*pre.to_a, cmd)
836
- else
837
- cmd = command(pre, cmd)
838
- end
839
- end
840
- args = var.is_a?(Hash) ? [var, cmd] : [cmd]
841
- 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
842
789
  end
843
- rescue StandardError => e
844
- on_error(e, from, exception: true)
845
- false
846
- else
847
- on :last, from
848
- ret
790
+ args = block_args args, &blk
849
791
  end
850
- end
851
-
852
- def variable_set(key, *args, **kwargs, &blk)
853
792
  if variables.include?(key) || blocks.include?(key)
854
793
  val = case args.size
855
794
  when 0
@@ -866,14 +805,10 @@ module Squared
866
805
  graph_set val
867
806
  when :pass
868
807
  pass_set val
869
- when :only
870
- only_set val
871
808
  when :exclude
872
809
  exclude_set val
873
810
  when :parent
874
811
  parent_set val
875
- when :archive
876
- archive_set val
877
812
  when :run
878
813
  run_set(*args, **kwargs)
879
814
  when :script
@@ -883,14 +818,7 @@ module Squared
883
818
  when :dependfile
884
819
  @dependfile = basepath(*args)
885
820
  else
886
- if block_given?
887
- if blocks.include?(key)
888
- series key, &blk
889
- return self
890
- end
891
- val = block_args val, &blk
892
- end
893
- instance_variable_set(:"@#{key}", val.first)
821
+ instance_variable_set(:"@#{key}", val)
894
822
  end
895
823
  else
896
824
  log&.warn "variable_set: @#{key} (private)"
@@ -898,8 +826,6 @@ module Squared
898
826
  self
899
827
  end
900
828
 
901
- alias apply variable_set
902
-
903
829
  def enabled?(ref = nil, **)
904
830
  return false if ref && !ref?(ref)
905
831
 
@@ -992,7 +918,7 @@ module Squared
992
918
  def log
993
919
  return @log unless @log.is_a?(Array)
994
920
 
995
- @log = Logger.new(enabled? ? @log.first : nil, **@log.last)
921
+ @log = Logger.new(enabled? ? @log[0] : nil, **@log[1])
996
922
  end
997
923
 
998
924
  def allref
@@ -1010,7 +936,7 @@ module Squared
1010
936
  path.parent.ascend.each do |dir|
1011
937
  target = dir.join(*args)
1012
938
  return target if target.exist?
1013
- break if (ascend && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
939
+ break if (ascend.is_a?(String) && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
1014
940
  end
1015
941
  ret
1016
942
  end
@@ -1033,8 +959,51 @@ module Squared
1033
959
 
1034
960
  private
1035
961
 
1036
- def puts(*args, **kwargs)
1037
- 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
1038
1007
  end
1039
1008
 
1040
1009
  def run_s(*cmd, env: nil, sync: true, from: nil, banner: verbose != false, **kwargs)
@@ -1042,7 +1011,7 @@ module Squared
1042
1011
  begin
1043
1012
  cmd.flatten.each { |val| run(val, env, sync: sync, banner: banner, **kwargs) }
1044
1013
  rescue StandardError => e
1045
- ret = on :error, from, e
1014
+ ret = on(:error, from, e)
1046
1015
  raise unless ret == true
1047
1016
  end
1048
1017
  on :last, from
@@ -1064,7 +1033,7 @@ module Squared
1064
1033
  elsif obj.is_a?(Array) && obj.any? { |val| !val.is_a?(String) }
1065
1034
  build(*obj, **kwargs)
1066
1035
  elsif obj
1067
- run_s(*Array(obj), **kwargs)
1036
+ run_s(obj.is_a?(Enumerable) ? obj.to_a : obj, **kwargs)
1068
1037
  end
1069
1038
  end
1070
1039
  end
@@ -1111,7 +1080,7 @@ module Squared
1111
1080
 
1112
1081
  t = dedupe.call(proj.name)
1113
1082
  j = if out
1114
- 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?
1115
1084
  true
1116
1085
  elsif !t.empty? && depth > 0
1117
1086
  post.reject { |pr| t.include?(pr) }.empty?
@@ -1123,9 +1092,9 @@ module Squared
1123
1092
  end
1124
1093
  if !out
1125
1094
  if !tasks && (script = workspace.script_get(:graph, group: proj.group, ref: proj.allref))
1126
- tasks = script[:graph]
1095
+ group = script[:graph]
1127
1096
  end
1128
- (tasks || (dev? ? ['build', 'copy'] : ['depend', 'build'])).each do |meth|
1097
+ (tasks || group || (dev? ? ['build', 'copy'] : ['depend', 'build'])).each do |meth|
1129
1098
  next if pass.include?(meth)
1130
1099
 
1131
1100
  if workspace.task_defined?(cmd = task_join(proj.name, meth))
@@ -1136,7 +1105,7 @@ module Squared
1136
1105
  end
1137
1106
  run(cmd, sync: false, banner: false)
1138
1107
  ENV.delete(key) if key
1139
- elsif proj.has?(meth, tasks ? nil : workspace.baseref)
1108
+ elsif proj.has?(meth, tasks || group ? nil : workspace.baseref)
1140
1109
  proj.__send__(meth.to_sym, sync: sync)
1141
1110
  end
1142
1111
  end
@@ -1161,7 +1130,7 @@ module Squared
1161
1130
  done
1162
1131
  end
1163
1132
 
1164
- def graph_collect(target, start = [], data: {}, pass: [], root: [])
1133
+ def graph_collect(target, start = [], data: {}, pass: [])
1165
1134
  deps = []
1166
1135
  (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
1167
1136
  next if pass.include?(val)
@@ -1173,13 +1142,12 @@ module Squared
1173
1142
  else
1174
1143
  items = workspace.find(group: val, ref: val.to_sym)
1175
1144
  end
1145
+
1176
1146
  items.each do |proj|
1177
- next if pass.include?(name = proj.name)
1147
+ next if pass.include?(proj.name)
1178
1148
 
1179
- if proj.graph? && !data.key?(name) && !root.include?(name)
1180
- graph_collect(proj, data: data, pass: pass, root: root + [name, target.name])
1181
- end
1182
- 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)
1183
1151
 
1184
1152
  deps << proj
1185
1153
  deps.concat(objs)
@@ -1219,23 +1187,31 @@ module Squared
1219
1187
  end
1220
1188
  return ret == equals.to_s unless equals.nil?
1221
1189
 
1222
- 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
1223
1191
  end
1224
1192
 
1225
1193
  def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
1226
- prefix = stripext prefix.to_s
1227
- 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])
1228
1196
  cmd[0] = shell_quote(val, force: false)
1229
1197
  end
1230
1198
  ret = JoinSet.new(cmd.flatten(1))
1231
- if options && (val = env("#{prefix.upcase}_OPTIONS"))
1199
+ if options && (val = env("#{prefix}_OPTIONS"))
1232
1200
  split_escape(val).each { |opt| ret.last(fill_option(opt), /\A(--?[^ =]+)[ =].+\z/m) }
1233
1201
  end
1234
1202
  main ? @session = ret : ret
1235
1203
  end
1236
1204
 
1237
1205
  def session_delete(*args, target: @session)
1238
- 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
1239
1215
  end
1240
1216
 
1241
1217
  def session_output(*cmd, **kwargs)
@@ -1245,7 +1221,7 @@ module Squared
1245
1221
  def session_done(cmd)
1246
1222
  return cmd unless cmd.respond_to?(:done)
1247
1223
 
1248
- 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
1249
1225
  @session = nil if cmd == @session
1250
1226
  cmd.done
1251
1227
  end
@@ -1257,20 +1233,17 @@ module Squared
1257
1233
  end
1258
1234
 
1259
1235
  def option(*args, target: @session, prefix: target&.first, **kwargs)
1260
- 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?
1261
1240
 
1262
- args.each do |val|
1263
- ret = env(env_key(stripext(prefix), val), **kwargs)
1264
- return ret if ret
1241
+ return yield ret
1242
+ end
1265
1243
  end
1266
1244
  nil
1267
1245
  end
1268
1246
 
1269
- def option_sanitize(opts, list, target: @session, **kwargs)
1270
- op = OptionPartition.new(opts, list, target, project: self, **kwargs)
1271
- [op.extras, op.values]
1272
- end
1273
-
1274
1247
  def option_clear(opts, target: @session, **kwargs)
1275
1248
  return unless target
1276
1249
 
@@ -1281,17 +1254,13 @@ module Squared
1281
1254
  puts 'Success'
1282
1255
  end
1283
1256
 
1284
- def print_error(*args, loglevel: Logger::WARN, **kwargs)
1285
- warn log_message(loglevel, *args, **kwargs) if warning?
1286
- end
1287
-
1288
1257
  def print_item(*val)
1289
1258
  puts unless printfirst?
1290
1259
  printsucc
1291
1260
  puts val unless val.empty? || (val.size == 1 && val.first.nil?)
1292
1261
  end
1293
1262
 
1294
- def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle, **)
1263
+ def print_banner(*lines, styles: theme[:banner], border: borderstyle, client: false)
1295
1264
  pad = 0
1296
1265
  if styles
1297
1266
  if styles.any? { |s| s.to_s.end_with?('!') }
@@ -1300,7 +1269,7 @@ module Squared
1300
1269
  styles = [:bold] + styles
1301
1270
  end
1302
1271
  end
1303
- n = Project.max_width(lines)
1272
+ n = line_width lines
1304
1273
  ch = ' ' * pad
1305
1274
  index = -1
1306
1275
  lines.map! do |val|
@@ -1315,64 +1284,45 @@ module Squared
1315
1284
  (lines << sub_style(ARG[:BORDER][1] * n, styles: border)).join("\n")
1316
1285
  end
1317
1286
 
1318
- def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle, **)
1319
- n = Project.max_width(lines)
1287
+ def print_footer(*lines, sub: nil, reverse: false, right: false, **kwargs)
1288
+ n = line_width lines
1320
1289
  lines.map! do |val|
1321
1290
  s = right ? val.rjust(n) : val.ljust(n)
1322
1291
  sub&.each { |h| s = sub_style(s, **h) }
1323
1292
  s
1324
1293
  end
1325
- ret = [sub_style(ARG[:BORDER][1] * n, styles: border), *lines]
1294
+ ret = [sub_style(ARG[:BORDER][1] * n, styles: kwargs.key?(:border) ? kwargs[:border] : borderstyle), *lines]
1326
1295
  ret.reverse! if reverse
1327
1296
  ret.join("\n")
1328
1297
  end
1329
1298
 
1330
- def print_status(*args, from: nil, **kwargs)
1331
- return if stdin?
1332
-
1333
- case from
1334
- when :outdated
1335
- out = print_footer("major #{args[0]} / minor #{args[1]} / patch #{args[2]}", right: true).split("\n")
1336
- out[1] = sub_style(out[1], pat: /^( +major )(\d+)(.+)$/, styles: theme[:major], index: 2)
1337
- out[1] = sub_style(out[1], pat: /^(.+)(minor )(\d+)(.+)$/, styles: theme[:active], index: 3)
1338
- puts out
1339
- when :completed
1340
- if verbose && kwargs[:start]
1341
- msg = sub_style('completed', styles: theme[:active])
1342
- puts log_message(Logger::INFO, *args, msg, subject: kwargs[:subject],
1343
- hint: time_format(epochtime - kwargs[:start]))
1344
- end
1345
- end
1346
- end
1347
-
1348
1299
  def format_desc(action, flag, opts = nil, **kwargs)
1349
1300
  return unless TASK_METADATA
1350
1301
 
1351
- ret = [@desc, action]
1352
- ret << flag if flag
1353
- workspace.format_desc(ret, opts, **kwargs)
1302
+ workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
1354
1303
  end
1355
1304
 
1356
1305
  def format_banner(cmd, banner: true)
1357
1306
  return unless banner && banner?
1358
1307
 
1359
1308
  if (data = workspace.banner_get(*@ref, group: group))
1360
- return if data.empty?
1309
+ return if !data.command && data.order.empty?
1361
1310
 
1362
1311
  client = true
1363
1312
  else
1364
- data = { command: true, order: [:path], styles: theme[:banner], border: theme[:border] }
1313
+ data = Workspace::Support::BannerData.new(true, [:path], theme[:banner], theme[:border])
1365
1314
  end
1366
1315
  if verbose
1367
1316
  out = []
1368
- if data[:command]
1317
+ if data.command
1369
1318
  if cmd =~ /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+))( |\z)/
1370
1319
  path = $3 || $2 || $1
1371
- cmd = cmd.sub(path, stripext(path).upcase)
1320
+ name = stripext path
1321
+ cmd = cmd.sub(path, data.command == 0 ? name : name.upcase)
1372
1322
  end
1373
1323
  out << cmd
1374
1324
  end
1375
- data[:order].each do |val|
1325
+ data.order.each do |val|
1376
1326
  if val.is_a?(Array)
1377
1327
  s = ' '
1378
1328
  found = false
@@ -1382,7 +1332,7 @@ module Squared
1382
1332
  meth
1383
1333
  elsif respond_to?(meth)
1384
1334
  found = true
1385
- __send__(meth)
1335
+ __send__ meth
1386
1336
  end
1387
1337
  end
1388
1338
  val = val.compact.join(s)
@@ -1392,9 +1342,9 @@ module Squared
1392
1342
  end
1393
1343
  out << val.to_s
1394
1344
  end
1395
- print_banner(*out, styles: data[:styles], border: data[:border], client: client)
1345
+ print_banner(*out, styles: data.styles, border: data.border, client: client)
1396
1346
  elsif workspace.series.multiple?
1397
- "## #{__send__(data[:order].first || :path)} ##"
1347
+ "## #{__send__(data.order.first || :path)} ##"
1398
1348
  end
1399
1349
  end
1400
1350
 
@@ -1404,9 +1354,9 @@ module Squared
1404
1354
  unless items.empty?
1405
1355
  pad = items.size.to_s.size
1406
1356
  items.each_with_index do |val, i|
1407
- next unless matchany?(val.first, reg)
1357
+ next unless reg.empty? || reg.any? { |pat| val[0].match?(pat) }
1408
1358
 
1409
- 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]}"
1410
1360
  end
1411
1361
  end
1412
1362
  sub = [headerstyle]
@@ -1418,7 +1368,7 @@ module Squared
1418
1368
  out << ''
1419
1369
  end
1420
1370
  if from
1421
- out << (from = from.to_s)
1371
+ out << from
1422
1372
  pat = /\A(#{Regexp.escape(from)})(.*)\z/
1423
1373
  end
1424
1374
  else
@@ -1440,7 +1390,7 @@ module Squared
1440
1390
  opts.each { |val| target << shell_option(flag, val) }
1441
1391
  end
1442
1392
 
1443
- def append_hash(data, target: @session || [], build: false)
1393
+ def append_hash(data, target: @session, build: false)
1444
1394
  if build && (type = env('BUILD', suffix: 'TYPE') || ENV['BUILD_TYPE'])
1445
1395
  type = "__#{type}__"
1446
1396
  if (extra = data[type] || data[type.to_sym]).is_a?(Hash)
@@ -1453,7 +1403,7 @@ module Squared
1453
1403
  next if (key = key.to_s).start_with?('__')
1454
1404
 
1455
1405
  if val.nil? || extra || session_arg?(key, target: target)
1456
- OptionPartition.delete_key(target, key)
1406
+ session_delete(key, target: target)
1457
1407
  next if val.nil?
1458
1408
  end
1459
1409
  case val
@@ -1471,14 +1421,15 @@ module Squared
1471
1421
  end
1472
1422
 
1473
1423
  def append_any(val, target: @session, build: false, delim: false)
1424
+ return unless val
1425
+
1474
1426
  if delim && !target.include?('--')
1475
1427
  target << '--'
1476
1428
  else
1477
1429
  delim = false
1478
1430
  end
1431
+ val = shell_split(val) if val.is_a?(String)
1479
1432
  case val
1480
- when String
1481
- target << val
1482
1433
  when Hash
1483
1434
  append_hash(val, target: target, build: build)
1484
1435
  when Enumerable
@@ -1509,7 +1460,7 @@ module Squared
1509
1460
  return target << (if flag
1510
1461
  shell_option(opt, equals ? val : nil, quote: quote, escape: escape, force: force)
1511
1462
  else
1512
- shell_quote(val)
1463
+ shell_quote val
1513
1464
  end)
1514
1465
  end
1515
1466
  nil
@@ -1519,17 +1470,18 @@ module Squared
1519
1470
  **kwargs)
1520
1471
  return if list.empty?
1521
1472
 
1522
- ret = []
1523
- list.flatten.each do |flag|
1524
- 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))
1525
1476
 
1526
- if val == '0' && no
1527
- flag = "no-#{flag}"
1528
- 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)
1529
1482
  end
1530
- ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1483
+ ret.each { |val| target << val } unless ret.empty?
1531
1484
  end
1532
- ret.each { |val| target << val } unless ret.empty?
1533
1485
  end
1534
1486
 
1535
1487
  def append_nocolor(target: @session)
@@ -1543,7 +1495,7 @@ module Squared
1543
1495
  when String
1544
1496
  "#{base} #{data}"
1545
1497
  when Hash
1546
- "#{append_hash(base, target: []).join(' ')} #{data}"
1498
+ "#{append_hash(base).join(' ')} #{data}"
1547
1499
  when Enumerable
1548
1500
  "#{base.to_a.join(' ')} #{data}"
1549
1501
  else
@@ -1552,11 +1504,11 @@ module Squared
1552
1504
  when Hash
1553
1505
  case base
1554
1506
  when String
1555
- "#{base} #{append_hash(data, target: []).join(' ')}"
1507
+ "#{base} #{append_hash(data).join(' ')}"
1556
1508
  when Hash
1557
1509
  base.merge(data)
1558
1510
  when Enumerable
1559
- Set.new(base.to_a + append_hash(data, target: [])).to_a
1511
+ Set.new(base.to_a + append_hash(data)).to_a
1560
1512
  else
1561
1513
  data
1562
1514
  end
@@ -1565,7 +1517,7 @@ module Squared
1565
1517
  when String
1566
1518
  "#{base} #{data.to_a.join(' ')}"
1567
1519
  when Hash
1568
- "#{append_hash(base, target: []).join(' ')} #{data.to_a.join(' ')}"
1520
+ "#{append_hash(base).join(' ')} #{data.to_a.join(' ')}"
1569
1521
  when Enumerable
1570
1522
  Set.new(base.to_a + data.to_a).to_a
1571
1523
  else
@@ -1577,9 +1529,9 @@ module Squared
1577
1529
  end
1578
1530
 
1579
1531
  def collect_hash(data, pass: [])
1580
- ret = []
1581
- data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1582
- ret
1532
+ [].tap do |ret|
1533
+ data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1534
+ end
1583
1535
  end
1584
1536
 
1585
1537
  def parse_json(val, kind: Hash, hint: nil)
@@ -1587,20 +1539,11 @@ module Squared
1587
1539
  raise_error("invalid JSON #{kind.name}", val, hint: hint) if kind && !ret.is_a?(kind)
1588
1540
  rescue StandardError => e
1589
1541
  log&.warn e
1590
- print_error(e, subject: name)
1542
+ warn log_message(Logger::WARN, e, subject: name, pass: true) if warning?
1591
1543
  else
1592
1544
  ret
1593
1545
  end
1594
1546
 
1595
- def fetch_uri(*args, **kwargs, &blk)
1596
- require 'open-uri'
1597
- if RUBY_VERSION < '2.5'
1598
- open(*args, **kwargs, &blk)
1599
- else
1600
- URI.open(*args, **kwargs, &blk)
1601
- end
1602
- end
1603
-
1604
1547
  def param_guard(action, flag, args:, key: nil, pat: nil, values: nil)
1605
1548
  if args && key
1606
1549
  val = args.fetch(key, nil)
@@ -1615,7 +1558,7 @@ module Squared
1615
1558
  args
1616
1559
  end
1617
1560
 
1618
- def confirm_outdated(pkg, ver, rev, lock: false)
1561
+ def confirm_outdated(pkg, ver, rev, cur = nil, lock: false, col1: 0)
1619
1562
  a = sub_style(case rev
1620
1563
  when 1
1621
1564
  'MAJOR'
@@ -1623,48 +1566,52 @@ module Squared
1623
1566
  'MINOR'
1624
1567
  else
1625
1568
  'PATCH'
1626
- end, styles: theme[:header])
1627
- 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])
1628
1571
  c, d = rev == 1 || lock ? ['y/N', 'N'] : ['Y/n', 'Y']
1629
- e = lock ? " #{sub_style('(locked)', styles: color(:red))}" : ''
1630
- 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
1631
1574
  end
1632
1575
 
1633
- def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil, multiple: false,
1634
- 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)
1635
1578
  puts if !series && !printfirst?
1636
1579
  msg = "#{msg} (optional)" unless force
1637
- unless (ret = choice(msg, list, multiple: multiple, force: force, **kwargs)) && !ret.empty?
1638
- exit 1 if force
1639
- 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
1640
1587
  end
1641
1588
  ret = multiple ? ret.map! { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
1642
1589
  if column
1643
- a, b = Array(column)
1644
- 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] }
1645
1592
  ret = ret.first unless multiple
1646
1593
  end
1647
1594
  if accept
1648
- hint = Array(ret).map { |val| sub_style(val, styles: theme[:inline]) }.join(', ')
1649
- accept = Array(accept).map { |val| Array(val) }
1650
- 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
1651
1601
  loop do
1652
- item = accept.first
1653
- d, e = item[2] ? ['Y', '[Y/n]'] : ['N', '[y/N]']
1654
- c = confirm("#{item[0]}#{hint ? " [#{hint}]" : ''} #{e} ", d, timeout: 60)
1655
- if item[1] == true
1602
+ c = confirm("#{accept.first[0]}#{a ? " [#{a}]" : ''} [y/N] ", 'N', timeout: 60)
1603
+ if accept.shift[1] == true
1656
1604
  ret << c
1657
1605
  elsif !c
1658
1606
  break
1659
1607
  end
1660
- hint = nil
1661
- accept.shift
1608
+ a = nil
1662
1609
  break if accept.empty?
1663
1610
  end
1664
1611
  exit 1 unless accept.empty?
1665
1612
  end
1666
1613
  if values
1667
- ret = Array(ret)
1614
+ ret = [ret] unless accept && multiple == -1
1668
1615
  values.each do |val|
1669
1616
  if val.is_a?(Array)
1670
1617
  val, force = val
@@ -1679,8 +1626,8 @@ module Squared
1679
1626
  ret
1680
1627
  end
1681
1628
 
1682
- def command_args(args, min: 0, force: false, **kwargs)
1683
- 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')
1684
1631
 
1685
1632
  readline('Enter arguments', force: force)
1686
1633
  end
@@ -1689,7 +1636,7 @@ module Squared
1689
1636
  if (ret = instance_eval(&blk)).nil?
1690
1637
  val
1691
1638
  else
1692
- Array(ret)
1639
+ ret.is_a?(Array) ? ret : [ret]
1693
1640
  end
1694
1641
  end
1695
1642
 
@@ -1698,17 +1645,21 @@ module Squared
1698
1645
  end
1699
1646
 
1700
1647
  def command(*args)
1701
- return args.join(' && ') unless workspace.powershell?
1702
-
1703
- "powershell.exe -Command #{shell_quote("& {#{args.join(' ; ')}}", option: false, double: true)}"
1648
+ if workspace.powershell?
1649
+ "powershell.exe -Command \"& {#{args.join(' ; ')}}\""
1650
+ else
1651
+ args.join(' && ')
1652
+ end
1704
1653
  end
1705
1654
 
1706
1655
  def relativepath(*list, all: false)
1707
1656
  return [] if list.empty?
1708
1657
 
1709
1658
  list.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
1710
- ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
1711
- 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
1712
1663
  end
1713
1664
  end
1714
1665
 
@@ -1718,18 +1669,7 @@ module Squared
1718
1669
  raise_error 'pathspec not within worktree' unless pass || files.size == proj.size
1719
1670
  files = proj
1720
1671
  end
1721
- files.map { |val| val == '.' ? '.' : shell_quote(basepath(val)) }
1722
- end
1723
-
1724
- def matchmap(list, prefix = nil)
1725
- list.map do |val|
1726
- if val.is_a?(Regexp)
1727
- val
1728
- else
1729
- val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1730
- Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1731
- end
1732
- end
1672
+ files.map { |val| val == '.' ? '.' : shell_quote(path + val) }
1733
1673
  end
1734
1674
 
1735
1675
  def semver(val)
@@ -1745,55 +1685,23 @@ module Squared
1745
1685
  end
1746
1686
 
1747
1687
  def semscan(val, fill: true)
1748
- ret = val.scan(SEM_VER).first
1749
- fill ? semver(ret) : ret
1750
- end
1751
-
1752
- def semcmp(val, other)
1753
- return 0 if val == other
1754
-
1755
- a, b = [val, other].map! { |ver| ver.scan(SEM_VER) }
1756
- return -1 if b.empty?
1757
- return 1 if a.empty?
1758
-
1759
- a, b = [a.first, b.first].map! do |c|
1760
- begin
1761
- d = Integer(c[5]).to_s
1762
- rescue StandardError
1763
- d = c[5] ? '-1' : '0'
1764
- end
1765
- [c[0], c[2], c[4] || '0', d]
1766
- end
1767
- a.each_with_index do |c, index|
1768
- next if c == (d = b[index])
1769
-
1770
- return c.to_i < d.to_i ? 1 : -1
1771
- end
1772
- 0
1773
- end
1774
-
1775
- def semgte?(val, other)
1776
- semcmp(val, other) != 1
1688
+ val.scan(SEM_VER).first.yield_self { |data| fill ? semver(data) : data }
1777
1689
  end
1778
1690
 
1779
1691
  def indexitem(val)
1780
- [$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/
1781
1693
  end
1782
1694
 
1783
1695
  def indexerror(val, list = nil)
1784
1696
  raise_error("requested index #{val}", hint: list && "of #{list.size}")
1785
1697
  end
1786
1698
 
1787
- def indexchar
1788
- workspace.windows? ? '=' : '^'
1789
- end
1790
-
1791
1699
  def printsucc
1792
1700
  @@print_order += 1
1793
1701
  end
1794
1702
 
1795
1703
  def color(val)
1796
- (ret = theme[val]) && !ret.empty? ? ret : [val]
1704
+ theme[val].yield_self { |styles| styles && !styles.empty? ? styles : [val] }
1797
1705
  end
1798
1706
 
1799
1707
  def colormap(val)
@@ -1816,9 +1724,9 @@ module Squared
1816
1724
  end
1817
1725
 
1818
1726
  def on(event, from, *args, **kwargs)
1819
- return unless from && @events.key?(event)
1727
+ return unless from && (data = @events[event])
1820
1728
 
1821
- Array(@events[event][from]).each do |obj|
1729
+ data[from]&.each do |obj|
1822
1730
  target, opts = if obj.is_a?(Array) && obj[1].is_a?(Hash)
1823
1731
  [obj[0], kwargs.empty? ? obj[1] : obj[1].merge(kwargs)]
1824
1732
  else
@@ -1835,49 +1743,27 @@ module Squared
1835
1743
  end
1836
1744
  end
1837
1745
 
1838
- def on_error(err, from, exception: self.exception, pass: false, dryrun: false)
1839
- log&.error err
1840
- unless dryrun
1841
- ret = on :error, from, err
1842
- raise err if exception && ret != true
1843
- end
1844
- print_error(err, pass: pass) unless ret
1845
- end
1846
-
1847
- def pwd_set(done = nil, pass: false, from: nil, dryrun: false)
1848
- pwd = Pathname.pwd
1849
- if block_given?
1850
- begin
1851
- if (path == pwd || pass == true) && (workspace.mri? || !workspace.windows?)
1852
- ret = yield
1853
- else
1854
- Dir.chdir path
1855
- ret = yield
1856
- Dir.chdir pwd
1857
- end
1858
- rescue StandardError => e
1859
- on_error(e, from, dryrun: dryrun)
1860
- else
1861
- ret
1862
- end
1863
- elsif @pwd == pwd
1864
- @pwd = nil
1865
- pwd unless done
1866
- elsif @pwd
1867
- return unless path == pwd
1868
-
1869
- Dir.chdir(@pwd)
1870
- @pwd = nil
1871
- elsif !done && path != pwd
1872
- @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
1873
1751
  Dir.chdir(path)
1874
- @pwd
1752
+ ret = yield
1753
+ Dir.chdir(pwd)
1875
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
1876
1762
  end
1877
1763
 
1878
1764
  def run_set(cmd, val = nil, opts: nil, **)
1879
- noopt = @output[1] == false && !@output[0].nil?
1880
- noenv = @output[2] == false
1765
+ diso = @output[1] == false && !@output[0].nil?
1766
+ dise = @output[2] == false
1881
1767
  parse = lambda do |data|
1882
1768
  ret = []
1883
1769
  if data[:command]
@@ -1897,8 +1783,8 @@ module Squared
1897
1783
  case cmd
1898
1784
  when Array
1899
1785
  @output = if cmd.all? { |data| data.is_a?(Hash) }
1900
- noopt = false
1901
- noenv = false
1786
+ diso = false
1787
+ dise = false
1902
1788
  cmd.map { |data| parse.call(data) }
1903
1789
  else
1904
1790
  cmd.dup
@@ -1909,14 +1795,14 @@ module Squared
1909
1795
  else
1910
1796
  @output[0] = cmd
1911
1797
  end
1912
- unless noopt
1798
+ unless diso
1913
1799
  if opts == false
1914
1800
  @output[1] = false
1915
1801
  elsif opts && opts != true
1916
1802
  @output[1] = opts
1917
1803
  end
1918
1804
  end
1919
- return if noenv
1805
+ return if dise
1920
1806
 
1921
1807
  if val.is_a?(Hash)
1922
1808
  @output[2] = val
@@ -1945,18 +1831,33 @@ module Squared
1945
1831
  @parent = val if val.is_a?(Project::Base)
1946
1832
  end
1947
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
+
1948
1853
  def graph_set(val)
1949
1854
  @graph = if val
1950
- 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
1951
1856
  end
1952
1857
  end
1953
1858
 
1954
1859
  def pass_set(val)
1955
- @pass = Array(val).freeze
1956
- end
1957
-
1958
- def only_set(val)
1959
- @only = val && as_a(val, :to_s).freeze
1860
+ @pass = (val ? as_a(val, :to_s) : []).freeze
1960
1861
  end
1961
1862
 
1962
1863
  def exclude_set(val)
@@ -1984,12 +1885,14 @@ module Squared
1984
1885
 
1985
1886
  def dependfile_set(list)
1986
1887
  @dependindex = list.index { |file| basepath(file).exist? }.tap do |index|
1987
- @dependfile = basepath(list[index || 0])
1888
+ @dependfile = @path + list[index || 0]
1988
1889
  end
1989
1890
  end
1990
1891
 
1991
- def as_get(val, from)
1992
- (@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
1993
1896
  end
1994
1897
 
1995
1898
  def task_build(keys)
@@ -2002,7 +1905,7 @@ module Squared
2002
1905
  unless @pass.include?(key.to_s) || ws.task_defined?(name, action) || ws.task_exclude?(action, self)
2003
1906
  ws.task_desc(@desc, action)
2004
1907
  task action do
2005
- __send__(key)
1908
+ __send__ key
2006
1909
  end
2007
1910
  end
2008
1911
  next if (items = @children.select { |item| item.task_include?(key) }).empty?
@@ -2013,17 +1916,10 @@ module Squared
2013
1916
  end
2014
1917
  end
2015
1918
 
2016
- def task_pass?(key)
2017
- @only ? !@only.include?(key) : @pass.include?(key)
2018
- end
2019
-
2020
- def matchany?(val, list, empty: true)
2021
- list.empty? ? empty : list.any? { |pat| val.match?(pat) }
2022
- end
2023
-
2024
1919
  def projectpath?(val)
2025
- ret = Pathname.new(val).cleanpath
2026
- 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
2027
1923
  end
2028
1924
 
2029
1925
  def checkdir?(val)
@@ -2075,22 +1971,17 @@ module Squared
2075
1971
  return false if workspace.series.chain?(val = task_join(name, action))
2076
1972
  return true if task_invoked?(val) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
2077
1973
 
2078
- val = workspace.series.name_get(action)
2079
- val != action && invoked_sync?(val)
1974
+ workspace.series.name_get(action).yield_self { |name| name != action && invoked_sync?(name) }
2080
1975
  end
2081
1976
 
2082
- def success?(ret, display = true)
2083
- ret == true && display && stdout? && banner?
1977
+ def success?(ret)
1978
+ ret == true && stdout? && banner?
2084
1979
  end
2085
1980
 
2086
1981
  def banner?
2087
1982
  ARG[:BANNER] && !env('BANNER', equals: '0')
2088
1983
  end
2089
1984
 
2090
- def pwd?
2091
- path == Pathname.pwd
2092
- end
2093
-
2094
1985
  def stdin?
2095
1986
  pipe == 0
2096
1987
  end
@@ -2122,20 +2013,8 @@ module Squared
2122
2013
  BLK_SET
2123
2014
  end
2124
2015
 
2125
- def hashobj
2126
- Workspace::Support.hashobj
2127
- end
2128
-
2129
- def hashlist
2130
- Workspace::Support.hashlist
2131
- end
2132
-
2133
- def hashdup
2134
- Workspace::Support.hashdup
2135
- end
2136
-
2137
2016
  def borderstyle
2138
- ((data = workspace.banner_get(*@ref, group: group)) && data[:border]) || theme[:border]
2017
+ workspace.banner_get(*@ref, group: group)&.border || theme[:border]
2139
2018
  end
2140
2019
 
2141
2020
  def headerstyle
@@ -2148,7 +2027,7 @@ module Squared
2148
2027
  end
2149
2028
 
2150
2029
  Application.implement(Base, base: true)
2151
- 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])
2152
2031
  end
2153
2032
  end
2154
2033
  end