squared 0.4.24 → 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
@@ -396,17 +380,8 @@ module Squared
396
380
  return self
397
381
  elsif !projectpath?(path = basepath(path)) || !checkdir?(path)
398
382
  return self
399
- else
400
- name = case name
401
- when String, Symbol
402
- name.to_s
403
- end
404
- end
405
- if @withargs
406
- data = @withargs.dup
407
- data.merge!(kwargs)
408
- kwargs = data
409
383
  end
384
+ kwargs = @withargs.yield_self { |data| data.dup.update(kwargs) } if @withargs
410
385
  kwargs[:group] = group unless kwargs.key?(:group)
411
386
  kwargs[:ref] = ref unless kwargs.key?(:ref)
412
387
  parent = self
@@ -420,9 +395,9 @@ module Squared
420
395
  workspace.add(path, name, **kwargs) do
421
396
  __send__ :parent_set, parent
422
397
  proj = self
423
- instance_eval(&blk) if block_given?
424
398
  end
425
399
  @children << proj
400
+ proj.instance_eval(&blk) if block_given?
426
401
  self
427
402
  end
428
403
 
@@ -436,7 +411,7 @@ module Squared
436
411
 
437
412
  out = obj.link(self, *args, **kwargs, &blk) if obj.respond_to?(:link)
438
413
  if !out
439
- 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)
440
415
  elsif out.respond_to?(:build)
441
416
  out.build
442
417
  end
@@ -455,49 +430,45 @@ module Squared
455
430
  if args.first.is_a?(Struct)
456
431
  f, blk = args.first.to_a
457
432
  args[0] = instance_eval(&blk) || f
458
- return unless args.first
433
+ return unless args[0]
459
434
  end
460
435
  if args.all? { |val| val.is_a?(Array) }
461
436
  cmd = []
462
437
  var = {}
463
438
  args.each do |val|
464
- 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)
465
440
 
466
441
  a, b, c, d, e = val
467
442
  case b
468
443
  when Hash
469
- b = append_hash(b, target: [], build: true).join(' ')
444
+ b = append_hash(b, build: true).join(' ')
470
445
  when Enumerable
471
446
  b = b.to_a.join(' ')
472
447
  end
473
- d = append_hash(d, target: []).join(' ') if d.is_a?(Hash)
448
+ d = append_hash(d).join(' ') if d.is_a?(Hash)
474
449
  if a
475
450
  cmd << [a, d, b].compact.join(' ')
476
451
  else
477
452
  next unless respond_to?(:compose)
478
453
 
479
- 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))
480
455
  end
481
- var.merge!(c) if c.is_a?(Hash)
456
+ var.update(c) if c.is_a?(Hash)
482
457
  end
483
458
  cmd = cmd.join(' && ')
484
459
  else
485
460
  cmd, opts, var, flags, extra = args
486
461
  end
487
462
  if cmd
488
- return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
489
-
490
- cmd = as_get(cmd, from)
463
+ cmd = as_get cmd
491
464
  opts = compose(opts, script: false) if opts && respond_to?(:compose)
492
- flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
465
+ flags = append_hash(flags).join(' ') if flags.is_a?(Hash)
493
466
  case opts
494
467
  when Hash
495
- cmd = Array(cmd).push(flags)
496
- .concat(append_hash(opts, target: [], build: true))
497
- .compact
498
- .join(' ')
468
+ opts = append_hash(opts, build: true)
469
+ cmd = as_a(cmd).append(flags).concat(opts).compact.join(' ')
499
470
  when Enumerable
500
- cmd = Array(cmd).concat(opts.to_a)
471
+ cmd = as_a(cmd).concat(opts.to_a)
501
472
  cmd.map! { |val| "#{val} #{flags}" } if flags
502
473
  cmd = cmd.join(' && ')
503
474
  else
@@ -506,7 +477,7 @@ module Squared
506
477
  else
507
478
  return unless (opts || extra) && respond_to?(:compose)
508
479
 
509
- 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)
510
481
  from = :script if from == :run && script?
511
482
  end
512
483
  run(cmd, var, sync: sync, from: from, banner: banner)
@@ -526,12 +497,16 @@ module Squared
526
497
  if proj.respond_to?(meth.to_sym)
527
498
  begin
528
499
  proj.__send__(meth, sync: sync)
529
- next
530
500
  rescue StandardError => e
531
- on_error(e, :prereqs, exception: true)
501
+ ret = on(:error, :prereqs, e)
502
+ raise unless ret == true
503
+ else
504
+ next
532
505
  end
533
506
  end
534
- 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
535
510
  end
536
511
  elsif proj.build?
537
512
  proj.build(sync: sync)
@@ -548,7 +523,7 @@ module Squared
548
523
  end
549
524
 
550
525
  def doc(*, sync: invoked_sync?('doc'), **)
551
- 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)
552
527
  end
553
528
 
554
529
  def lint(*, sync: invoked_sync?('lint'), **)
@@ -569,9 +544,9 @@ module Squared
569
544
  on :first, :clean unless pass
570
545
  case @clean
571
546
  when Struct
572
- if (val = instance_eval(&@clean.block) || @clean.run)
547
+ if (any = instance_eval(&@clean.block) || @clean.run)
573
548
  temp = @clean
574
- @clean = val
549
+ @clean = any
575
550
  clean(*args, sync: sync, pass: true, **kwargs)
576
551
  @clean = temp
577
552
  end
@@ -581,15 +556,17 @@ module Squared
581
556
  begin
582
557
  @clean.each { |cmd, opts| build(cmd.to_s, opts, sync: sync) }
583
558
  rescue StandardError => e
584
- on_error e, from
559
+ log&.error e
560
+ ret = on(:error, from, e)
561
+ raise if exception && ret != true
585
562
  end
586
563
  else
587
564
  if @clean.is_a?(Enumerable) && !series?(@clean)
588
565
  @clean.each do |val|
589
- entry = basepath(val = val.to_s)
566
+ entry = path + (val = val.to_s)
590
567
  if entry.directory? && val.match?(%r{[\\/]\z})
591
568
  log&.warn "rm -rf #{entry}"
592
- rm_rf(entry, verbose: verbosetype > 0)
569
+ rm_rf(entry, verbose: verbose)
593
570
  else
594
571
  log&.warn "rm #{entry}"
595
572
  (val.include?('*') ? Dir[entry] : [entry]).each do |file|
@@ -630,7 +607,8 @@ module Squared
630
607
  end
631
608
  ret = graph_branch(self, data, tasks, out, sync: sync, pass: pass)
632
609
  rescue StandardError => e
633
- on_error(e, :graph, exception: true)
610
+ ret = on(:error, :graph, e)
611
+ raise unless ret == true
634
612
  else
635
613
  if out
636
614
  [out, ret]
@@ -639,13 +617,13 @@ module Squared
639
617
  end
640
618
  end
641
619
 
642
- def unpack(target, file = nil, uri: nil, sync: true, digest: nil, ext: nil, force: false, depth: 1, headers: {},
643
- 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'
644
622
  if !target.exist?
645
623
  target.mkpath
646
624
  elsif !target.directory?
647
625
  raise_error('invalid location', hint: target)
648
- elsif !file && !target.empty?
626
+ elsif !target.empty?
649
627
  raise_error('directory not empty', hint: target) unless force || env('UNPACK_FORCE')
650
628
  create = true
651
629
  end
@@ -666,66 +644,58 @@ module Squared
666
644
  when 128, 'sha512'
667
645
  Digest::SHA512
668
646
  else
669
- raise_error "invalid checksum: #{digest}"
647
+ raise_error("invalid checksum: #{digest}", hint: name)
670
648
  end
671
649
  end
672
650
  if (val = env('HEADERS')) && (val = parse_json(val, hint: "HEADERS_#{@envname}"))
673
651
  headers = headers.is_a?(Hash) ? headers.merge(val) : val
674
652
  end
675
- if file
676
- ext ||= File.extname(file)[1..-1]
677
- else
678
- data = nil
679
- (uri = Array(uri)).each_with_index do |url, index|
680
- fetch_uri(url, headers) do |f|
681
- data = f.read
682
- if algo && algo.hexdigest(data) != digest
683
- data = nil
684
- raise_error("checksum failed: #{digest}", hint: url) if index == uri.size - 1
685
- end
686
- next if ext && index == 0
687
-
688
- case f.content_type
689
- when 'application/zip'
690
- ext = 'zip'
691
- when %r{application/(?:x-)?gzip}
692
- ext = 'tgz'
693
- when 'application/x-xz'
694
- ext = 'txz'
695
- 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'
696
670
  end
697
- break uri = url if data
698
- end
699
- unless data && (ext ||= URI.parse(uri).path[/\.(\w+)(?:\?|\z)/, 1])
700
- raise_error("no content#{data ? ' type' : ''}", hint: uri)
701
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)
702
676
  end
703
677
  ext = ext.downcase
704
678
  if (val = env("#{%w[zip 7z gem].include?(ext) ? ext.upcase : 'TAR'}_DEPTH", ignore: false))
705
679
  depth = val.to_i
706
680
  end
707
681
  begin
708
- unless file
709
- if ext == 'gem'
710
- dir = Dir.mktmpdir
711
- file = File.new(File.join(dir, File.basename(uri)), 'w')
712
- else
713
- require 'tempfile'
714
- file = Tempfile.new("#{name}-")
715
- end
716
- file.write(data)
717
- file.close
718
- file = Pathname.new(file)
719
- 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}-")
720
688
  end
689
+ file.write(data)
690
+ file.close
721
691
  if create
722
- print_error('force remove', subject: name, hint: target)
692
+ warn log_message(Logger::WARN, 'force remove', subject: name, hint: target, pass: true)
723
693
  target.rmtree
724
694
  target.mkpath
725
695
  end
726
696
  case ext
727
697
  when 'zip', 'aar'
728
- session 'unzip', shell_quote(file), quote_option('d', target)
698
+ session 'unzip', shell_quote(file.path), quote_option('d', target)
729
699
  when 'tar', 'tgz', 'tar.gz', 'tar.xz', 'gz', 'xz'
730
700
  flags = +(verbose ? 'v' : '')
731
701
  if ext.end_with?('gz')
@@ -733,26 +703,28 @@ module Squared
733
703
  elsif ext.end_with?('xz')
734
704
  flags += 'J'
735
705
  end
736
- 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),
737
707
  quote_option('C', target)
738
708
  depth = 0
739
709
  when '7z'
740
- session '7z', 'x', shell_quote(file), "-o#{shell_quote(target)}"
710
+ session '7z', 'x', shell_quote(file.path), "-o#{shell_quote(target)}"
741
711
  when 'gem'
742
- session 'gem', 'unpack', shell_quote(file), quote_option('target', target)
712
+ session 'gem', 'unpack', shell_quote(file.path), quote_option('target', target)
743
713
  depth = 0 unless val
744
714
  else
745
- raise_error("unsupported format: #{ext}", hint: uri || file)
715
+ raise_error("unsupported format: #{ext}", hint: uri)
746
716
  end
747
- run(sync: sync, banner: verbose, from: from)
717
+ run(sync: sync, from: from)
748
718
  while depth > 0 && target.children.size == 1
749
719
  entry = target.children.first
750
720
  break unless entry.directory?
751
721
 
752
722
  i = 0
753
- 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
754
726
  FileUtils.mv(entry, dest)
755
- dest.children.each { |child| FileUtils.mv(child, target) }
727
+ dest.each_child { |child| FileUtils.mv(child, target) }
756
728
  dest.rmdir
757
729
  target = entry
758
730
  depth -= 1
@@ -760,8 +732,8 @@ module Squared
760
732
  ensure
761
733
  if dir
762
734
  remove_entry dir
763
- elsif delete && file&.exist?
764
- file.unlink
735
+ else
736
+ file&.unlink
765
737
  end
766
738
  end
767
739
  end
@@ -779,7 +751,7 @@ module Squared
779
751
  end
780
752
 
781
753
  def event(name, key, *args, override: false, **kwargs, &blk)
782
- data = @events[name.to_sym]
754
+ data = @events[name.to_sym] ||= {}
783
755
  items = if override
784
756
  data[key.to_sym] = []
785
757
  else
@@ -790,8 +762,9 @@ module Squared
790
762
  end
791
763
 
792
764
  def as(cmd, script, to = nil)
793
- data = @as[cmd.to_sym]
794
- (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 }
795
768
  self
796
769
  end
797
770
 
@@ -808,57 +781,14 @@ module Squared
808
781
  self
809
782
  end
810
783
 
811
- def run(cmd = @session, var = nil, exception: self.exception, sync: true, from: nil, banner: true, chdir: path,
812
- interactive: nil, hint: nil, **)
813
- unless cmd
814
- print_error('no command given', subject: project, hint: from || 'unknown', pass: true)
815
- return
816
- end
817
- cmd = cmd.target if cmd.is_a?(OptionPartition)
818
- if interactive && (!@session || !option('y'))
819
- title, y = case interactive
820
- when Array
821
- interactive
822
- when String
823
- [interactive, 'N']
824
- else
825
- ['Run', 'Y']
826
- end
827
- yn = y == 'Y' ? 'Y/n' : 'y/N'
828
- exit 1 unless confirm("#{title}? [#{sub_style(cmd.to_s, styles: theme[:inline])}] [#{yn}] ", y)
829
- end
830
- cmd = session_done cmd
831
- log&.info cmd
832
- on :first, from
833
- begin
834
- if cmd.match?(/\A[^:]+:[^:]/) && workspace.task_defined?(cmd)
835
- log&.warn "ENV discarded: #{var}" if var
836
- task_invoke(cmd, exception: exception, warning: warning?)
837
- else
838
- print_item format_banner(hint ? "#{cmd} (#{hint})" : cmd, banner: banner) if sync
839
- if var != false && (pre = runenv)
840
- case pre
841
- when Hash
842
- var = var.is_a?(Hash) ? pre.merge(var) : pre
843
- when Enumerable
844
- cmd = command(*pre.to_a, cmd)
845
- else
846
- cmd = command(pre, cmd)
847
- end
848
- end
849
- args = var.is_a?(Hash) ? [var, cmd] : [cmd]
850
- 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
851
789
  end
852
- rescue StandardError => e
853
- on_error(e, from, exception: true)
854
- false
855
- else
856
- on :last, from
857
- ret
790
+ args = block_args args, &blk
858
791
  end
859
- end
860
-
861
- def variable_set(key, *args, **kwargs, &blk)
862
792
  if variables.include?(key) || blocks.include?(key)
863
793
  val = case args.size
864
794
  when 0
@@ -875,14 +805,10 @@ module Squared
875
805
  graph_set val
876
806
  when :pass
877
807
  pass_set val
878
- when :only
879
- only_set val
880
808
  when :exclude
881
809
  exclude_set val
882
810
  when :parent
883
811
  parent_set val
884
- when :archive
885
- archive_set val
886
812
  when :run
887
813
  run_set(*args, **kwargs)
888
814
  when :script
@@ -892,14 +818,7 @@ module Squared
892
818
  when :dependfile
893
819
  @dependfile = basepath(*args)
894
820
  else
895
- if block_given?
896
- if blocks.include?(key)
897
- series key, &blk
898
- return self
899
- end
900
- val = block_args val, &blk
901
- end
902
- instance_variable_set(:"@#{key}", val.first)
821
+ instance_variable_set(:"@#{key}", val)
903
822
  end
904
823
  else
905
824
  log&.warn "variable_set: @#{key} (private)"
@@ -907,8 +826,6 @@ module Squared
907
826
  self
908
827
  end
909
828
 
910
- alias apply variable_set
911
-
912
829
  def enabled?(ref = nil, **)
913
830
  return false if ref && !ref?(ref)
914
831
 
@@ -1001,7 +918,7 @@ module Squared
1001
918
  def log
1002
919
  return @log unless @log.is_a?(Array)
1003
920
 
1004
- @log = Logger.new(enabled? ? @log.first : nil, **@log.last)
921
+ @log = Logger.new(enabled? ? @log[0] : nil, **@log[1])
1005
922
  end
1006
923
 
1007
924
  def allref
@@ -1019,7 +936,7 @@ module Squared
1019
936
  path.parent.ascend.each do |dir|
1020
937
  target = dir.join(*args)
1021
938
  return target if target.exist?
1022
- 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
1023
940
  end
1024
941
  ret
1025
942
  end
@@ -1042,8 +959,51 @@ module Squared
1042
959
 
1043
960
  private
1044
961
 
1045
- def puts(*args, **kwargs)
1046
- 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
1047
1007
  end
1048
1008
 
1049
1009
  def run_s(*cmd, env: nil, sync: true, from: nil, banner: verbose != false, **kwargs)
@@ -1051,7 +1011,7 @@ module Squared
1051
1011
  begin
1052
1012
  cmd.flatten.each { |val| run(val, env, sync: sync, banner: banner, **kwargs) }
1053
1013
  rescue StandardError => e
1054
- ret = on :error, from, e
1014
+ ret = on(:error, from, e)
1055
1015
  raise unless ret == true
1056
1016
  end
1057
1017
  on :last, from
@@ -1073,7 +1033,7 @@ module Squared
1073
1033
  elsif obj.is_a?(Array) && obj.any? { |val| !val.is_a?(String) }
1074
1034
  build(*obj, **kwargs)
1075
1035
  elsif obj
1076
- run_s(*Array(obj), **kwargs)
1036
+ run_s(obj.is_a?(Enumerable) ? obj.to_a : obj, **kwargs)
1077
1037
  end
1078
1038
  end
1079
1039
  end
@@ -1120,7 +1080,7 @@ module Squared
1120
1080
 
1121
1081
  t = dedupe.call(proj.name)
1122
1082
  j = if out
1123
- 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?
1124
1084
  true
1125
1085
  elsif !t.empty? && depth > 0
1126
1086
  post.reject { |pr| t.include?(pr) }.empty?
@@ -1132,9 +1092,9 @@ module Squared
1132
1092
  end
1133
1093
  if !out
1134
1094
  if !tasks && (script = workspace.script_get(:graph, group: proj.group, ref: proj.allref))
1135
- tasks = script[:graph]
1095
+ group = script[:graph]
1136
1096
  end
1137
- (tasks || (dev? ? ['build', 'copy'] : ['depend', 'build'])).each do |meth|
1097
+ (tasks || group || (dev? ? ['build', 'copy'] : ['depend', 'build'])).each do |meth|
1138
1098
  next if pass.include?(meth)
1139
1099
 
1140
1100
  if workspace.task_defined?(cmd = task_join(proj.name, meth))
@@ -1145,7 +1105,7 @@ module Squared
1145
1105
  end
1146
1106
  run(cmd, sync: false, banner: false)
1147
1107
  ENV.delete(key) if key
1148
- elsif proj.has?(meth, tasks ? nil : workspace.baseref)
1108
+ elsif proj.has?(meth, tasks || group ? nil : workspace.baseref)
1149
1109
  proj.__send__(meth.to_sym, sync: sync)
1150
1110
  end
1151
1111
  end
@@ -1170,7 +1130,7 @@ module Squared
1170
1130
  done
1171
1131
  end
1172
1132
 
1173
- def graph_collect(target, start = [], data: {}, pass: [], root: [])
1133
+ def graph_collect(target, start = [], data: {}, pass: [])
1174
1134
  deps = []
1175
1135
  (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
1176
1136
  next if pass.include?(val)
@@ -1182,13 +1142,12 @@ module Squared
1182
1142
  else
1183
1143
  items = workspace.find(group: val, ref: val.to_sym)
1184
1144
  end
1145
+
1185
1146
  items.each do |proj|
1186
- next if pass.include?(name = proj.name)
1147
+ next if pass.include?(proj.name)
1187
1148
 
1188
- if proj.graph? && !data.key?(name) && !root.include?(name)
1189
- graph_collect(proj, data: data, pass: pass, root: root + [name, target.name])
1190
- end
1191
- 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)
1192
1151
 
1193
1152
  deps << proj
1194
1153
  deps.concat(objs)
@@ -1228,23 +1187,31 @@ module Squared
1228
1187
  end
1229
1188
  return ret == equals.to_s unless equals.nil?
1230
1189
 
1231
- 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
1232
1191
  end
1233
1192
 
1234
1193
  def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
1235
- prefix = stripext prefix.to_s
1236
- 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])
1237
1196
  cmd[0] = shell_quote(val, force: false)
1238
1197
  end
1239
1198
  ret = JoinSet.new(cmd.flatten(1))
1240
- if options && (val = env("#{prefix.upcase}_OPTIONS"))
1199
+ if options && (val = env("#{prefix}_OPTIONS"))
1241
1200
  split_escape(val).each { |opt| ret.last(fill_option(opt), /\A(--?[^ =]+)[ =].+\z/m) }
1242
1201
  end
1243
1202
  main ? @session = ret : ret
1244
1203
  end
1245
1204
 
1246
1205
  def session_delete(*args, target: @session)
1247
- 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
1248
1215
  end
1249
1216
 
1250
1217
  def session_output(*cmd, **kwargs)
@@ -1254,7 +1221,7 @@ module Squared
1254
1221
  def session_done(cmd)
1255
1222
  return cmd unless cmd.respond_to?(:done)
1256
1223
 
1257
- 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
1258
1225
  @session = nil if cmd == @session
1259
1226
  cmd.done
1260
1227
  end
@@ -1266,20 +1233,17 @@ module Squared
1266
1233
  end
1267
1234
 
1268
1235
  def option(*args, target: @session, prefix: target&.first, **kwargs)
1269
- 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?
1270
1240
 
1271
- args.each do |val|
1272
- ret = env(env_key(stripext(prefix), val), **kwargs)
1273
- return ret if ret
1241
+ return yield ret
1242
+ end
1274
1243
  end
1275
1244
  nil
1276
1245
  end
1277
1246
 
1278
- def option_sanitize(opts, list, target: @session, **kwargs)
1279
- op = OptionPartition.new(opts, list, target, project: self, **kwargs)
1280
- [op.extras, op.values]
1281
- end
1282
-
1283
1247
  def option_clear(opts, target: @session, **kwargs)
1284
1248
  return unless target
1285
1249
 
@@ -1290,17 +1254,13 @@ module Squared
1290
1254
  puts 'Success'
1291
1255
  end
1292
1256
 
1293
- def print_error(*args, loglevel: Logger::WARN, **kwargs)
1294
- warn log_message(loglevel, *args, **kwargs) if warning?
1295
- end
1296
-
1297
1257
  def print_item(*val)
1298
1258
  puts unless printfirst?
1299
1259
  printsucc
1300
1260
  puts val unless val.empty? || (val.size == 1 && val.first.nil?)
1301
1261
  end
1302
1262
 
1303
- def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle, **)
1263
+ def print_banner(*lines, styles: theme[:banner], border: borderstyle, client: false)
1304
1264
  pad = 0
1305
1265
  if styles
1306
1266
  if styles.any? { |s| s.to_s.end_with?('!') }
@@ -1309,7 +1269,7 @@ module Squared
1309
1269
  styles = [:bold] + styles
1310
1270
  end
1311
1271
  end
1312
- n = Project.max_width(lines)
1272
+ n = line_width lines
1313
1273
  ch = ' ' * pad
1314
1274
  index = -1
1315
1275
  lines.map! do |val|
@@ -1324,64 +1284,45 @@ module Squared
1324
1284
  (lines << sub_style(ARG[:BORDER][1] * n, styles: border)).join("\n")
1325
1285
  end
1326
1286
 
1327
- def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle, **)
1328
- n = Project.max_width(lines)
1287
+ def print_footer(*lines, sub: nil, reverse: false, right: false, **kwargs)
1288
+ n = line_width lines
1329
1289
  lines.map! do |val|
1330
1290
  s = right ? val.rjust(n) : val.ljust(n)
1331
1291
  sub&.each { |h| s = sub_style(s, **h) }
1332
1292
  s
1333
1293
  end
1334
- 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]
1335
1295
  ret.reverse! if reverse
1336
1296
  ret.join("\n")
1337
1297
  end
1338
1298
 
1339
- def print_status(*args, from: nil, **kwargs)
1340
- return if stdin?
1341
-
1342
- case from
1343
- when :outdated
1344
- out = print_footer("major #{args[0]} / minor #{args[1]} / patch #{args[2]}", right: true).split("\n")
1345
- out[1] = sub_style(out[1], pat: /^( +major )(\d+)(.+)$/, styles: theme[:major], index: 2)
1346
- out[1] = sub_style(out[1], pat: /^(.+)(minor )(\d+)(.+)$/, styles: theme[:active], index: 3)
1347
- puts out
1348
- when :completed
1349
- if verbose && kwargs[:start]
1350
- msg = sub_style('completed', styles: theme[:active])
1351
- puts log_message(Logger::INFO, *args, msg, subject: kwargs[:subject],
1352
- hint: time_format(epochtime - kwargs[:start]))
1353
- end
1354
- end
1355
- end
1356
-
1357
1299
  def format_desc(action, flag, opts = nil, **kwargs)
1358
1300
  return unless TASK_METADATA
1359
1301
 
1360
- ret = [@desc, action]
1361
- ret << flag if flag
1362
- workspace.format_desc(ret, opts, **kwargs)
1302
+ workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
1363
1303
  end
1364
1304
 
1365
1305
  def format_banner(cmd, banner: true)
1366
1306
  return unless banner && banner?
1367
1307
 
1368
1308
  if (data = workspace.banner_get(*@ref, group: group))
1369
- return if data.empty?
1309
+ return if !data.command && data.order.empty?
1370
1310
 
1371
1311
  client = true
1372
1312
  else
1373
- data = { command: true, order: [:path], styles: theme[:banner], border: theme[:border] }
1313
+ data = Workspace::Support::BannerData.new(true, [:path], theme[:banner], theme[:border])
1374
1314
  end
1375
1315
  if verbose
1376
1316
  out = []
1377
- if data[:command]
1317
+ if data.command
1378
1318
  if cmd =~ /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+))( |\z)/
1379
1319
  path = $3 || $2 || $1
1380
- cmd = cmd.sub(path, stripext(path).upcase)
1320
+ name = stripext path
1321
+ cmd = cmd.sub(path, data.command == 0 ? name : name.upcase)
1381
1322
  end
1382
1323
  out << cmd
1383
1324
  end
1384
- data[:order].each do |val|
1325
+ data.order.each do |val|
1385
1326
  if val.is_a?(Array)
1386
1327
  s = ' '
1387
1328
  found = false
@@ -1391,7 +1332,7 @@ module Squared
1391
1332
  meth
1392
1333
  elsif respond_to?(meth)
1393
1334
  found = true
1394
- __send__(meth)
1335
+ __send__ meth
1395
1336
  end
1396
1337
  end
1397
1338
  val = val.compact.join(s)
@@ -1401,9 +1342,9 @@ module Squared
1401
1342
  end
1402
1343
  out << val.to_s
1403
1344
  end
1404
- print_banner(*out, styles: data[:styles], border: data[:border], client: client)
1345
+ print_banner(*out, styles: data.styles, border: data.border, client: client)
1405
1346
  elsif workspace.series.multiple?
1406
- "## #{__send__(data[:order].first || :path)} ##"
1347
+ "## #{__send__(data.order.first || :path)} ##"
1407
1348
  end
1408
1349
  end
1409
1350
 
@@ -1413,9 +1354,9 @@ module Squared
1413
1354
  unless items.empty?
1414
1355
  pad = items.size.to_s.size
1415
1356
  items.each_with_index do |val, i|
1416
- next unless matchany?(val.first, reg)
1357
+ next unless reg.empty? || reg.any? { |pat| val[0].match?(pat) }
1417
1358
 
1418
- 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]}"
1419
1360
  end
1420
1361
  end
1421
1362
  sub = [headerstyle]
@@ -1427,7 +1368,7 @@ module Squared
1427
1368
  out << ''
1428
1369
  end
1429
1370
  if from
1430
- out << (from = from.to_s)
1371
+ out << from
1431
1372
  pat = /\A(#{Regexp.escape(from)})(.*)\z/
1432
1373
  end
1433
1374
  else
@@ -1449,7 +1390,7 @@ module Squared
1449
1390
  opts.each { |val| target << shell_option(flag, val) }
1450
1391
  end
1451
1392
 
1452
- def append_hash(data, target: @session || [], build: false)
1393
+ def append_hash(data, target: @session, build: false)
1453
1394
  if build && (type = env('BUILD', suffix: 'TYPE') || ENV['BUILD_TYPE'])
1454
1395
  type = "__#{type}__"
1455
1396
  if (extra = data[type] || data[type.to_sym]).is_a?(Hash)
@@ -1462,7 +1403,7 @@ module Squared
1462
1403
  next if (key = key.to_s).start_with?('__')
1463
1404
 
1464
1405
  if val.nil? || extra || session_arg?(key, target: target)
1465
- OptionPartition.delete_key(target, key)
1406
+ session_delete(key, target: target)
1466
1407
  next if val.nil?
1467
1408
  end
1468
1409
  case val
@@ -1480,14 +1421,15 @@ module Squared
1480
1421
  end
1481
1422
 
1482
1423
  def append_any(val, target: @session, build: false, delim: false)
1424
+ return unless val
1425
+
1483
1426
  if delim && !target.include?('--')
1484
1427
  target << '--'
1485
1428
  else
1486
1429
  delim = false
1487
1430
  end
1431
+ val = shell_split(val) if val.is_a?(String)
1488
1432
  case val
1489
- when String
1490
- target << val
1491
1433
  when Hash
1492
1434
  append_hash(val, target: target, build: build)
1493
1435
  when Enumerable
@@ -1518,7 +1460,7 @@ module Squared
1518
1460
  return target << (if flag
1519
1461
  shell_option(opt, equals ? val : nil, quote: quote, escape: escape, force: force)
1520
1462
  else
1521
- shell_quote(val)
1463
+ shell_quote val
1522
1464
  end)
1523
1465
  end
1524
1466
  nil
@@ -1528,17 +1470,18 @@ module Squared
1528
1470
  **kwargs)
1529
1471
  return if list.empty?
1530
1472
 
1531
- ret = []
1532
- list.flatten.each do |flag|
1533
- 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))
1534
1476
 
1535
- if val == '0' && no
1536
- flag = "no-#{flag}"
1537
- 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)
1538
1482
  end
1539
- ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1483
+ ret.each { |val| target << val } unless ret.empty?
1540
1484
  end
1541
- ret.each { |val| target << val } unless ret.empty?
1542
1485
  end
1543
1486
 
1544
1487
  def append_nocolor(target: @session)
@@ -1552,7 +1495,7 @@ module Squared
1552
1495
  when String
1553
1496
  "#{base} #{data}"
1554
1497
  when Hash
1555
- "#{append_hash(base, target: []).join(' ')} #{data}"
1498
+ "#{append_hash(base).join(' ')} #{data}"
1556
1499
  when Enumerable
1557
1500
  "#{base.to_a.join(' ')} #{data}"
1558
1501
  else
@@ -1561,11 +1504,11 @@ module Squared
1561
1504
  when Hash
1562
1505
  case base
1563
1506
  when String
1564
- "#{base} #{append_hash(data, target: []).join(' ')}"
1507
+ "#{base} #{append_hash(data).join(' ')}"
1565
1508
  when Hash
1566
1509
  base.merge(data)
1567
1510
  when Enumerable
1568
- Set.new(base.to_a + append_hash(data, target: [])).to_a
1511
+ Set.new(base.to_a + append_hash(data)).to_a
1569
1512
  else
1570
1513
  data
1571
1514
  end
@@ -1574,7 +1517,7 @@ module Squared
1574
1517
  when String
1575
1518
  "#{base} #{data.to_a.join(' ')}"
1576
1519
  when Hash
1577
- "#{append_hash(base, target: []).join(' ')} #{data.to_a.join(' ')}"
1520
+ "#{append_hash(base).join(' ')} #{data.to_a.join(' ')}"
1578
1521
  when Enumerable
1579
1522
  Set.new(base.to_a + data.to_a).to_a
1580
1523
  else
@@ -1586,9 +1529,9 @@ module Squared
1586
1529
  end
1587
1530
 
1588
1531
  def collect_hash(data, pass: [])
1589
- ret = []
1590
- data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1591
- ret
1532
+ [].tap do |ret|
1533
+ data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1534
+ end
1592
1535
  end
1593
1536
 
1594
1537
  def parse_json(val, kind: Hash, hint: nil)
@@ -1596,20 +1539,11 @@ module Squared
1596
1539
  raise_error("invalid JSON #{kind.name}", val, hint: hint) if kind && !ret.is_a?(kind)
1597
1540
  rescue StandardError => e
1598
1541
  log&.warn e
1599
- print_error(e, subject: name)
1542
+ warn log_message(Logger::WARN, e, subject: name, pass: true) if warning?
1600
1543
  else
1601
1544
  ret
1602
1545
  end
1603
1546
 
1604
- def fetch_uri(*args, **kwargs, &blk)
1605
- require 'open-uri'
1606
- if RUBY_VERSION < '2.5'
1607
- open(*args, **kwargs, &blk)
1608
- else
1609
- URI.open(*args, **kwargs, &blk)
1610
- end
1611
- end
1612
-
1613
1547
  def param_guard(action, flag, args:, key: nil, pat: nil, values: nil)
1614
1548
  if args && key
1615
1549
  val = args.fetch(key, nil)
@@ -1624,7 +1558,7 @@ module Squared
1624
1558
  args
1625
1559
  end
1626
1560
 
1627
- def confirm_outdated(pkg, ver, rev, lock: false)
1561
+ def confirm_outdated(pkg, ver, rev, cur = nil, lock: false, col1: 0)
1628
1562
  a = sub_style(case rev
1629
1563
  when 1
1630
1564
  'MAJOR'
@@ -1632,48 +1566,52 @@ module Squared
1632
1566
  'MINOR'
1633
1567
  else
1634
1568
  'PATCH'
1635
- end, styles: theme[:header])
1636
- 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])
1637
1571
  c, d = rev == 1 || lock ? ['y/N', 'N'] : ['Y/n', 'Y']
1638
- e = lock ? " #{sub_style('(locked)', styles: color(:red))}" : ''
1639
- 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
1640
1574
  end
1641
1575
 
1642
- def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil, multiple: false,
1643
- 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)
1644
1578
  puts if !series && !printfirst?
1645
1579
  msg = "#{msg} (optional)" unless force
1646
- unless (ret = choice(msg, list, multiple: multiple, force: force, **kwargs)) && !ret.empty?
1647
- exit 1 if force
1648
- 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
1649
1587
  end
1650
1588
  ret = multiple ? ret.map! { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
1651
1589
  if column
1652
- a, b = Array(column)
1653
- 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] }
1654
1592
  ret = ret.first unless multiple
1655
1593
  end
1656
1594
  if accept
1657
- hint = Array(ret).map { |val| sub_style(val, styles: theme[:inline]) }.join(', ')
1658
- accept = Array(accept).map { |val| Array(val) }
1659
- 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
1660
1601
  loop do
1661
- item = accept.first
1662
- d, e = item[2] ? ['Y', '[Y/n]'] : ['N', '[y/N]']
1663
- c = confirm("#{item[0]}#{hint ? " [#{hint}]" : ''} #{e} ", d, timeout: 60)
1664
- if item[1] == true
1602
+ c = confirm("#{accept.first[0]}#{a ? " [#{a}]" : ''} [y/N] ", 'N', timeout: 60)
1603
+ if accept.shift[1] == true
1665
1604
  ret << c
1666
1605
  elsif !c
1667
1606
  break
1668
1607
  end
1669
- hint = nil
1670
- accept.shift
1608
+ a = nil
1671
1609
  break if accept.empty?
1672
1610
  end
1673
1611
  exit 1 unless accept.empty?
1674
1612
  end
1675
1613
  if values
1676
- ret = Array(ret)
1614
+ ret = [ret] unless accept && multiple == -1
1677
1615
  values.each do |val|
1678
1616
  if val.is_a?(Array)
1679
1617
  val, force = val
@@ -1688,8 +1626,8 @@ module Squared
1688
1626
  ret
1689
1627
  end
1690
1628
 
1691
- def command_args(args, min: 0, force: false, **kwargs)
1692
- 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')
1693
1631
 
1694
1632
  readline('Enter arguments', force: force)
1695
1633
  end
@@ -1698,7 +1636,7 @@ module Squared
1698
1636
  if (ret = instance_eval(&blk)).nil?
1699
1637
  val
1700
1638
  else
1701
- Array(ret)
1639
+ ret.is_a?(Array) ? ret : [ret]
1702
1640
  end
1703
1641
  end
1704
1642
 
@@ -1707,17 +1645,21 @@ module Squared
1707
1645
  end
1708
1646
 
1709
1647
  def command(*args)
1710
- return args.join(' && ') unless workspace.powershell?
1711
-
1712
- "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
1713
1653
  end
1714
1654
 
1715
1655
  def relativepath(*list, all: false)
1716
1656
  return [] if list.empty?
1717
1657
 
1718
1658
  list.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
1719
- ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
1720
- 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
1721
1663
  end
1722
1664
  end
1723
1665
 
@@ -1727,18 +1669,7 @@ module Squared
1727
1669
  raise_error 'pathspec not within worktree' unless pass || files.size == proj.size
1728
1670
  files = proj
1729
1671
  end
1730
- files.map { |val| val == '.' ? '.' : shell_quote(basepath(val)) }
1731
- end
1732
-
1733
- def matchmap(list, prefix = nil)
1734
- list.map do |val|
1735
- if val.is_a?(Regexp)
1736
- val
1737
- else
1738
- val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
1739
- Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
1740
- end
1741
- end
1672
+ files.map { |val| val == '.' ? '.' : shell_quote(path + val) }
1742
1673
  end
1743
1674
 
1744
1675
  def semver(val)
@@ -1754,55 +1685,23 @@ module Squared
1754
1685
  end
1755
1686
 
1756
1687
  def semscan(val, fill: true)
1757
- ret = val.scan(SEM_VER).first
1758
- fill ? semver(ret) : ret
1759
- end
1760
-
1761
- def semcmp(val, other)
1762
- return 0 if val == other
1763
-
1764
- a, b = [val, other].map! { |ver| ver.scan(SEM_VER) }
1765
- return -1 if b.empty?
1766
- return 1 if a.empty?
1767
-
1768
- a, b = [a.first, b.first].map! do |c|
1769
- begin
1770
- d = Integer(c[5]).to_s
1771
- rescue StandardError
1772
- d = c[5] ? '-1' : '0'
1773
- end
1774
- [c[0], c[2], c[4] || '0', d]
1775
- end
1776
- a.each_with_index do |c, index|
1777
- next if c == (d = b[index])
1778
-
1779
- return c.to_i < d.to_i ? 1 : -1
1780
- end
1781
- 0
1782
- end
1783
-
1784
- def semgte?(val, other)
1785
- semcmp(val, other) != 1
1688
+ val.scan(SEM_VER).first.yield_self { |data| fill ? semver(data) : data }
1786
1689
  end
1787
1690
 
1788
1691
  def indexitem(val)
1789
- [$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/
1790
1693
  end
1791
1694
 
1792
1695
  def indexerror(val, list = nil)
1793
1696
  raise_error("requested index #{val}", hint: list && "of #{list.size}")
1794
1697
  end
1795
1698
 
1796
- def indexchar
1797
- workspace.windows? ? '=' : '^'
1798
- end
1799
-
1800
1699
  def printsucc
1801
1700
  @@print_order += 1
1802
1701
  end
1803
1702
 
1804
1703
  def color(val)
1805
- (ret = theme[val]) && !ret.empty? ? ret : [val]
1704
+ theme[val].yield_self { |styles| styles && !styles.empty? ? styles : [val] }
1806
1705
  end
1807
1706
 
1808
1707
  def colormap(val)
@@ -1825,9 +1724,9 @@ module Squared
1825
1724
  end
1826
1725
 
1827
1726
  def on(event, from, *args, **kwargs)
1828
- return unless from && @events.key?(event)
1727
+ return unless from && (data = @events[event])
1829
1728
 
1830
- Array(@events[event][from]).each do |obj|
1729
+ data[from]&.each do |obj|
1831
1730
  target, opts = if obj.is_a?(Array) && obj[1].is_a?(Hash)
1832
1731
  [obj[0], kwargs.empty? ? obj[1] : obj[1].merge(kwargs)]
1833
1732
  else
@@ -1844,49 +1743,27 @@ module Squared
1844
1743
  end
1845
1744
  end
1846
1745
 
1847
- def on_error(err, from, exception: self.exception, pass: false, dryrun: false)
1848
- log&.error err
1849
- unless dryrun
1850
- ret = on :error, from, err
1851
- raise err if exception && ret != true
1852
- end
1853
- print_error(err, pass: pass) unless ret
1854
- end
1855
-
1856
- def pwd_set(done = nil, pass: false, from: nil, dryrun: false)
1857
- pwd = Pathname.pwd
1858
- if block_given?
1859
- begin
1860
- if (path == pwd || pass == true) && (workspace.mri? || !workspace.windows?)
1861
- ret = yield
1862
- else
1863
- Dir.chdir path
1864
- ret = yield
1865
- Dir.chdir pwd
1866
- end
1867
- rescue StandardError => e
1868
- on_error(e, from, dryrun: dryrun)
1869
- else
1870
- ret
1871
- end
1872
- elsif @pwd == pwd
1873
- @pwd = nil
1874
- pwd unless done
1875
- elsif @pwd
1876
- return unless path == pwd
1877
-
1878
- Dir.chdir(@pwd)
1879
- @pwd = nil
1880
- elsif !done && path != pwd
1881
- @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
1882
1751
  Dir.chdir(path)
1883
- @pwd
1752
+ ret = yield
1753
+ Dir.chdir(pwd)
1884
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
1885
1762
  end
1886
1763
 
1887
1764
  def run_set(cmd, val = nil, opts: nil, **)
1888
- noopt = @output[1] == false && !@output[0].nil?
1889
- noenv = @output[2] == false
1765
+ diso = @output[1] == false && !@output[0].nil?
1766
+ dise = @output[2] == false
1890
1767
  parse = lambda do |data|
1891
1768
  ret = []
1892
1769
  if data[:command]
@@ -1906,8 +1783,8 @@ module Squared
1906
1783
  case cmd
1907
1784
  when Array
1908
1785
  @output = if cmd.all? { |data| data.is_a?(Hash) }
1909
- noopt = false
1910
- noenv = false
1786
+ diso = false
1787
+ dise = false
1911
1788
  cmd.map { |data| parse.call(data) }
1912
1789
  else
1913
1790
  cmd.dup
@@ -1918,14 +1795,14 @@ module Squared
1918
1795
  else
1919
1796
  @output[0] = cmd
1920
1797
  end
1921
- unless noopt
1798
+ unless diso
1922
1799
  if opts == false
1923
1800
  @output[1] = false
1924
1801
  elsif opts && opts != true
1925
1802
  @output[1] = opts
1926
1803
  end
1927
1804
  end
1928
- return if noenv
1805
+ return if dise
1929
1806
 
1930
1807
  if val.is_a?(Hash)
1931
1808
  @output[2] = val
@@ -1954,18 +1831,33 @@ module Squared
1954
1831
  @parent = val if val.is_a?(Project::Base)
1955
1832
  end
1956
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
+
1957
1853
  def graph_set(val)
1958
1854
  @graph = if val
1959
- 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
1960
1856
  end
1961
1857
  end
1962
1858
 
1963
1859
  def pass_set(val)
1964
- @pass = Array(val).freeze
1965
- end
1966
-
1967
- def only_set(val)
1968
- @only = val && as_a(val, :to_s).freeze
1860
+ @pass = (val ? as_a(val, :to_s) : []).freeze
1969
1861
  end
1970
1862
 
1971
1863
  def exclude_set(val)
@@ -1993,12 +1885,14 @@ module Squared
1993
1885
 
1994
1886
  def dependfile_set(list)
1995
1887
  @dependindex = list.index { |file| basepath(file).exist? }.tap do |index|
1996
- @dependfile = basepath(list[index || 0])
1888
+ @dependfile = @path + list[index || 0]
1997
1889
  end
1998
1890
  end
1999
1891
 
2000
- def as_get(val, from)
2001
- (@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
2002
1896
  end
2003
1897
 
2004
1898
  def task_build(keys)
@@ -2011,7 +1905,7 @@ module Squared
2011
1905
  unless @pass.include?(key.to_s) || ws.task_defined?(name, action) || ws.task_exclude?(action, self)
2012
1906
  ws.task_desc(@desc, action)
2013
1907
  task action do
2014
- __send__(key)
1908
+ __send__ key
2015
1909
  end
2016
1910
  end
2017
1911
  next if (items = @children.select { |item| item.task_include?(key) }).empty?
@@ -2022,17 +1916,10 @@ module Squared
2022
1916
  end
2023
1917
  end
2024
1918
 
2025
- def task_pass?(key)
2026
- @only ? !@only.include?(key) : @pass.include?(key)
2027
- end
2028
-
2029
- def matchany?(val, list, empty: true)
2030
- list.empty? ? empty : list.any? { |pat| val.match?(pat) }
2031
- end
2032
-
2033
1919
  def projectpath?(val)
2034
- ret = Pathname.new(val).cleanpath
2035
- 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
2036
1923
  end
2037
1924
 
2038
1925
  def checkdir?(val)
@@ -2084,22 +1971,17 @@ module Squared
2084
1971
  return false if workspace.series.chain?(val = task_join(name, action))
2085
1972
  return true if task_invoked?(val) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
2086
1973
 
2087
- val = workspace.series.name_get(action)
2088
- val != action && invoked_sync?(val)
1974
+ workspace.series.name_get(action).yield_self { |name| name != action && invoked_sync?(name) }
2089
1975
  end
2090
1976
 
2091
- def success?(ret, display = true)
2092
- ret == true && display && stdout? && banner?
1977
+ def success?(ret)
1978
+ ret == true && stdout? && banner?
2093
1979
  end
2094
1980
 
2095
1981
  def banner?
2096
1982
  ARG[:BANNER] && !env('BANNER', equals: '0')
2097
1983
  end
2098
1984
 
2099
- def pwd?
2100
- path == Pathname.pwd
2101
- end
2102
-
2103
1985
  def stdin?
2104
1986
  pipe == 0
2105
1987
  end
@@ -2131,16 +2013,8 @@ module Squared
2131
2013
  BLK_SET
2132
2014
  end
2133
2015
 
2134
- def hashobj
2135
- Workspace::Support.hashobj
2136
- end
2137
-
2138
- def hashlist
2139
- Workspace::Support.hashlist
2140
- end
2141
-
2142
2016
  def borderstyle
2143
- ((data = workspace.banner_get(*@ref, group: group)) && data[:border]) || theme[:border]
2017
+ workspace.banner_get(*@ref, group: group)&.border || theme[:border]
2144
2018
  end
2145
2019
 
2146
2020
  def headerstyle
@@ -2153,7 +2027,7 @@ module Squared
2153
2027
  end
2154
2028
 
2155
2029
  Application.implement(Base, base: true)
2156
- 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])
2157
2031
  end
2158
2032
  end
2159
2033
  end