squared 0.6.8 → 0.7.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.
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'logger'
5
5
 
6
6
  require_relative 'support/class'
7
+ require_relative 'support/utils'
7
8
 
8
9
  module Squared
9
10
  module Workspace
@@ -16,12 +17,13 @@ module Squared
16
17
  include Prompt
17
18
  include Utils
18
19
  include Support
20
+ include Support::Utils
19
21
  include Workspace::Support::Variables
20
22
  include Rake::DSL
21
23
 
22
24
  OPTIONS = Workspace::Support.hashobj
23
25
  VAR_SET = %i[parent global script index envname desc dependfile dependname dependindex theme archive env graph
24
- dev prod pass only exclude asdf].freeze
26
+ dev prod timeout pass only exclude asdf].freeze
25
27
  BLK_SET = %i[run depend doc lint test copy clean].freeze
26
28
  SEM_VER = /\b(\d+)(?:(\.)(\d+))?(?:(\.)(\d+))?[-.]?(\S+)?\b/.freeze
27
29
  URI_SCHEME = %r{\A([a-z][a-z\d+-.]*)://[^@:\[\]\\^<>|\s]}i.freeze
@@ -99,33 +101,35 @@ module Squared
99
101
 
100
102
  @@tasks = {}
101
103
  @@graph = { _: [] }
102
- @@asdf = Pathname.new("#{Dir.home}/.asdf").yield_self do |path|
104
+ @@asdf = Pathname.new(ENV['ASDF_DIR'] || "#{Dir.home}/.asdf").yield_self do |path|
103
105
  version = if path.join('asdf.sh').exist?
104
106
  15
105
107
  elsif ENV['ASDF_DATA_DIR'] && (path = Pathname.new(ENV['ASDF_DATA_DIR'])).exist?
106
108
  16
107
109
  end
108
- Struct.new(:path, :version).new(path, version) if version
110
+ if version
111
+ config = ENV['ASDF_CONFIG_FILE'] || File.join(Dir.home, '.asdfrc')
112
+ Struct.new(:path, :version, :config).new(path, version, config)
113
+ end
109
114
  end
110
115
  @@print_order = 0
111
116
 
112
117
  subtasks({
113
118
  'graph' => %i[run print].freeze,
114
119
  'unpack' => %i[zip gz tar ext],
115
- 'asdf' => %i[set exec current update latest where reshim]
120
+ 'asdf' => %i[set exec env current update latest where reshim]
116
121
  })
117
122
 
118
123
  attr_reader :name, :workspace, :path, :theme, :group, :parent, :children, :dependfile,
119
- :exception, :pipe, :verbose, :global
124
+ :exception, :pipe, :verbose
120
125
  attr_accessor :project
121
126
 
122
- def initialize(workspace, path, name, *, group: nil, first: {}, last: {}, error: {}, common: ARG[:COMMON],
123
- **kwargs)
127
+ def initialize(workspace, path, name, *, first: {}, last: {}, error: {}, common: ARG[:COMMON], **kwargs)
124
128
  @path = path
125
129
  @workspace = workspace
126
130
  @name = name.to_s.freeze
127
131
  @project = @path.basename.to_s.freeze
128
- @group = group&.to_s.freeze
132
+ @group = kwargs[:group]&.to_s.freeze
129
133
  @envname = env_key(@name).freeze
130
134
  @depend = kwargs[:depend]
131
135
  @doc = kwargs[:doc]
@@ -143,22 +147,28 @@ module Squared
143
147
  else
144
148
  val.nil? ? workspace.verbose : val
145
149
  end
146
- self.global = false
150
+ @global = 0
147
151
  @output = []
148
152
  @ref = []
149
153
  @children = []
150
- @events = hashobj.update({ first: first, last: last, error: error })
151
154
  @as = hashobj
152
155
  @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
156
+ @on = {
157
+ event: hashobj.update({ first: first, last: last, error: error }),
158
+ from: []
159
+ }
160
+ @timeout = { _: [{}.compare_by_identity, nil, 0] }
153
161
  @log = nil
154
162
  @dev = nil
155
163
  @prod = nil
156
164
  @withargs = nil
157
165
  @session = nil
158
166
  @index = -1
167
+ @initargs = kwargs.freeze
159
168
  parent_set kwargs[:parent]
160
169
  run_set(kwargs[:run], kwargs[:env], opts: kwargs.fetch(:opts, true))
161
170
  graph_set kwargs[:graph]
171
+ timeout_set kwargs[:timeout]
162
172
  pass_set kwargs[:pass]
163
173
  only_set kwargs[:only]
164
174
  exclude_set kwargs[:exclude]
@@ -168,117 +178,6 @@ module Squared
168
178
  initialize_ref Base.ref
169
179
  end
170
180
 
171
- def initialize_ref(ref)
172
- @ref << ref unless @exclude.include?(ref)
173
- end
174
-
175
- def initialize_build(ref, **kwargs)
176
- initialize_ref ref
177
- if (@script = @workspace.script_get(group: @group, ref: ref))
178
- if @script[:log] && !kwargs.key?(:log)
179
- kwargs[:log] = @script[:log]
180
- @log = nil
181
- end
182
- @depend = @script[:depend] if @depend.nil?
183
- @doc = @script[:doc] if @doc.nil?
184
- @lint = @script[:lint] if @lint.nil?
185
- @test = @script[:test] if @test.nil?
186
- @clean = @script[:clean] if @clean.nil?
187
- @exclude = @script[:exclude] if @exclude.empty? && @script.key?(:exclude)
188
- end
189
- initialize_events(ref, **kwargs)
190
- initialize_logger(**kwargs)
191
- return if @output[0] == false
192
-
193
- data = @workspace.script_find(*@ref, @group)
194
- if @output[0].nil?
195
- if data[:script]
196
- unless kwargs[:script] == false
197
- script_set(data[:script], args: data.fetch(:args, kwargs[:args]), prod: kwargs[:prod], global: true)
198
- end
199
- elsif data[:run]
200
- run_set(data[:run], global: true)
201
- end
202
- if kwargs[:script]
203
- script_set(kwargs[:script], args: kwargs[:args]) unless data[:env][:script]
204
- elsif @script
205
- if @script[:script]
206
- script_set(@script[:script], args: @script.fetch(:args, kwargs[:args])) unless data[:global][:script]
207
- elsif @script[:run] && !data[:global][:run]
208
- run_set @script[:run]
209
- end
210
- end
211
- elsif data[:run] && data[:env][:run]
212
- run_set(data[:run], global: true)
213
- end
214
- end
215
-
216
- def initialize_events(ref, **)
217
- return unless (events = @workspace.events_get(group: @group, ref: ref))
218
-
219
- events.each { |task, data| data.each { |ev, blk| @events[ev][task] ||= [blk] } }
220
- end
221
-
222
- def initialize_logger(log: nil, **)
223
- return if @log
224
-
225
- log = log.is_a?(Hash) ? log.dup : { file: log }
226
- file = if (val = env('LOG_FILE'))
227
- Time.now.strftime(val)
228
- elsif (val = env('LOG_AUTO'))
229
- require 'date'
230
- "#{@name}-%s.log" % [case val
231
- when 'y', 'year'
232
- Date.today.year
233
- when 'm', 'month'
234
- Date.today.strftime('%Y-%m')
235
- when 'd', 'day', '1'
236
- Date.today
237
- else
238
- val.include?('%') ? Time.now.strftime(val) : Time.now.strftime('%FT%T%:z')
239
- end]
240
- elsif (val = log[:file])
241
- if val.is_a?(String)
242
- Time.now.strftime(val)
243
- else
244
- require 'date'
245
- "#{@name}-#{Date.today}.log"
246
- end
247
- end
248
- .yield_self do |dir|
249
- @workspace.home.join(env('LOG_DIR', ''), dir).realdirpath if dir
250
- rescue StandardError => e
251
- print_error e
252
- end
253
- log[:progname] ||= @name
254
- env('LOG_LEVEL', ignore: false) { |val| log[:level] = val.start_with?(/\d/) ? log_sym(val.to_i) : val }
255
- log.delete(:file)
256
- @log = [file, log]
257
- end
258
-
259
- def initialize_env(dev: nil, prod: nil, **)
260
- @dev = env_match('BUILD', dev, suffix: 'DEV', strict: true)
261
- @prod = env_match('BUILD', prod, suffix: 'PROD', strict: true)
262
- env('BUILD', suffix: 'ENV') { |val| @output[2] = val if (val = parse_json(val)) } unless @output[0] == false
263
- unless @output[0] == false || @output[0].is_a?(Array)
264
- env('BUILD', suffix: 'OPTS') do |val|
265
- n = @output[0] ? 1 : 3
266
- @output[n] = merge_opts(@output[n], shell_split(val))
267
- end
268
- env(ref.to_s.upcase, suffix: 'OPTS') { |val| @output[4] = merge_opts(@output[4], shell_split(val)) }
269
- end
270
- env('BUILD', suffix: 'VERSION') { |val| self.version = val }
271
- env('BUILD', strict: true) do |val|
272
- if val == '0'
273
- @output = [false]
274
- elsif script?
275
- script_set val
276
- else
277
- run_set val
278
- end
279
- end
280
- end
281
-
282
181
  def ==(other)
283
182
  equal?(other)
284
183
  end
@@ -312,10 +211,10 @@ module Squared
312
211
  -1
313
212
  elsif f.any? { |val| e.include?(val) } # rubocop:disable Lint/DuplicateBranch
314
213
  1
315
- elsif @index >= 0 && (i = other.instance_variable_get(:@index)) >= 0
214
+ elsif @index >= 0 && (i = other.index_get) >= 0
316
215
  @index <=> i
317
216
  end
318
- rescue StandardError => e
217
+ rescue => e
319
218
  log&.debug e
320
219
  nil
321
220
  end
@@ -326,7 +225,7 @@ module Squared
326
225
 
327
226
  def exception=(val)
328
227
  @exception = case val
329
- when Numeric, TrueClass, FalseClass
228
+ when Numeric, true, false
330
229
  val
331
230
  else
332
231
  workspace.exception
@@ -344,7 +243,7 @@ module Squared
344
243
 
345
244
  def verbose=(val)
346
245
  @verbose = case val
347
- when Numeric, TrueClass, FalseClass
246
+ when Numeric, true, false
348
247
  val
349
248
  else
350
249
  workspace.verbose
@@ -352,7 +251,17 @@ module Squared
352
251
  end
353
252
 
354
253
  def global=(val)
355
- @global = val unless val.nil?
254
+ return if @global == -1
255
+
256
+ @global = if val == 0
257
+ 0
258
+ elsif val.nil?
259
+ -1
260
+ elsif val < 0
261
+ @global ^ val.abs
262
+ else
263
+ @global | val
264
+ end
356
265
  end
357
266
 
358
267
  def ref
@@ -375,7 +284,7 @@ module Squared
375
284
 
376
285
  format_desc action, flag, '(-)project*'
377
286
  task flag do |_, args|
378
- args = args.to_a.reject { |val| name == val }
287
+ args = args.to_a.reject { |val| val == name }
379
288
  if flag == :run
380
289
  graph args
381
290
  else
@@ -434,10 +343,12 @@ module Squared
434
343
  asdf(flag, args, version: version)
435
344
  end
436
345
  else
437
- format_desc(action, flag, ('command' if flag == :exec))
346
+ format_desc(action, flag, case flag when :exec, :env then 'command' end)
438
347
  task flag do |_, args|
439
348
  args = args.to_a
440
- args << readline('Enter command', force: true) if args.empty? && flag == :exec
349
+ if args.empty? && (flag == :exec || flag == :env)
350
+ args << readline('Enter command', force: flag == :exec)
351
+ end
441
352
  asdf flag, args
442
353
  end
443
354
  end
@@ -453,7 +364,7 @@ module Squared
453
364
  end
454
365
 
455
366
  def with(**kwargs, &blk)
456
- @withargs = (kwargs unless kwargs.empty?)
367
+ @withargs = kwargs.empty? ? nil : kwargs
457
368
  if block_given?
458
369
  instance_eval(&blk)
459
370
  @withargs = nil
@@ -474,18 +385,17 @@ module Squared
474
385
  kwargs = hashdup(@withargs).update(kwargs) if @withargs
475
386
  kwargs[:group] = group if group && !kwargs.key?(:group)
476
387
  kwargs[:ref] = ref unless kwargs.key?(:ref)
477
- proj = nil
478
388
  name = case name
479
389
  when String, Symbol
480
390
  name.to_s
481
391
  else
482
392
  path.basename
483
393
  end
394
+ target = children
484
395
  workspace.add(path, name, parent: self, **kwargs) do
485
- proj = self
396
+ target << self
486
397
  instance_eval(&blk) if block_given?
487
398
  end
488
- children << proj
489
399
  end
490
400
  self
491
401
  end
@@ -502,13 +412,13 @@ module Squared
502
412
  out.build if out.respond_to?(:build)
503
413
  end
504
414
  self
505
- rescue StandardError => e
506
- print_error(e, subject: obj, hint: name)
415
+ rescue => e
416
+ print_error(e, subject: name, hint: obj)
507
417
  self
508
418
  end
509
419
 
510
420
  def build(*args, sync: invoked_sync?('build'), from: :run, **)
511
- banner = verbose
421
+ banner = !silent?
512
422
  if args.empty?
513
423
  return unless from == :run
514
424
 
@@ -522,9 +432,8 @@ module Squared
522
432
  return unless args.first
523
433
  end
524
434
  if args.all? { |val| val.is_a?(Array) }
525
- cmd = []
526
435
  var = {}
527
- args.each do |val|
436
+ cmd = args.each_with_object([]) do |val, out|
528
437
  case val.first
529
438
  when Proc
530
439
  instance_exec(*val[1..-1], &val.first)
@@ -542,21 +451,20 @@ module Squared
542
451
  end
543
452
  d = append_hash(d, target: []).join(' ') if d.is_a?(Hash)
544
453
  if a
545
- cmd << [replace_bin(a), d, b].compact.join(' ')
454
+ out << [replace_bin(as_get(a, from, 1)), d, b].compact.join(' ')
546
455
  else
547
456
  next unless respond_to?(:compose)
548
457
 
549
- cmd << a if (a = compose(as_get(b, from), d, script: true, args: e, from: from))
458
+ out << a if (a = compose(as_get(b, from, 2), d, script: true, args: e, from: from))
550
459
  end
551
460
  var.update(c) if c.is_a?(Hash)
552
- end
553
- cmd = cmd.join(' && ')
461
+ end.join(' && ')
554
462
  else
555
463
  cmd, opts, var, flags, extra = args
556
464
  if cmd
557
465
  return run_b(cmd, sync: sync, from: from) if cmd.is_a?(Proc) || cmd.is_a?(Method)
558
466
 
559
- cmd = replace_bin as_get(cmd, from)
467
+ cmd = replace_bin as_get(cmd, from, 1)
560
468
  opts = compose(opts, script: false) if opts && respond_to?(:compose)
561
469
  flags = append_hash(flags, target: []).join(' ') if flags.is_a?(Hash)
562
470
  cmd = case opts
@@ -574,7 +482,7 @@ module Squared
574
482
  else
575
483
  return unless (opts || extra) && respond_to?(:compose)
576
484
 
577
- cmd = compose(as_get(opts, from), flags, script: true, args: extra, from: from)
485
+ cmd = compose(as_get(opts, from, 2), flags, script: true, args: extra, from: from)
578
486
  from = :script if from == :run && script?
579
487
  end
580
488
  end
@@ -590,16 +498,16 @@ module Squared
590
498
  graph_deps.flatten(1).sort.each do |proj|
591
499
  next if @@graph[:_].include?(proj)
592
500
 
593
- if (val = ENV["PREREQS_#{proj.instance_variable_get(:@envname)}"] || ENV["PREREQS_#{proj.ref.upcase}"])
501
+ if (val = ENV["PREREQS_#{proj.envname_get}"] || ENV["PREREQS_#{proj.ref.upcase}"])
594
502
  split_escape(val) do |meth|
595
- if proj.respond_to?(meth.to_sym)
503
+ if proj.has?(meth)
596
504
  begin
597
505
  proj.__send__(meth, sync: sync)
598
- rescue StandardError => e
599
- on_error(e, :prereqs, exception: true)
506
+ rescue => e
507
+ on_error(e, exception: true)
600
508
  end
601
509
  else
602
- print_error(name, "method: #{meth}", subject: 'prereqs', hint: 'undefined')
510
+ print_error('prereqs', "method: #{meth}", subject: name, hint: 'undefined')
603
511
  end
604
512
  end
605
513
  elsif proj.build?
@@ -649,24 +557,24 @@ module Squared
649
557
  when Hash
650
558
  begin
651
559
  @clean.each { |cmd, opts| build(cmd.to_s, opts, sync: sync) }
652
- rescue StandardError => e
653
- on_error e, :clean
560
+ rescue => e
561
+ on_error(e, fatal: prod?)
654
562
  end
655
563
  else
656
564
  if @clean.is_a?(Enumerable) && !series?(@clean)
657
565
  @clean.each do |val|
658
- entry = basepath(val = val.to_s)
659
- if entry.directory? && val.match?(%r{[\\/]\z})
660
- log&.warn "rm -rf #{entry}"
661
- rm_rf(entry, verbose: !silent?)
566
+ src = basepath(val = val.to_s)
567
+ if src.directory? && val.match?(%r{[\\/]\z})
568
+ log&.warn "rm -rf #{src}"
569
+ rm_rf(src, verbose: !silent?)
662
570
  else
663
- log&.warn "rm #{entry}"
664
- (val.include?('*') ? Dir[entry] : [entry]).each do |file|
571
+ log&.warn "rm #{src}"
572
+ (val.include?('*') ? Dir[src] : [src]).each do |file|
665
573
  next unless File.file?(file)
666
574
 
667
575
  File.delete(file)
668
- rescue StandardError => e
669
- log&.error e
576
+ rescue => e
577
+ print_error(Logger::ERROR, e, subject: name)
670
578
  end
671
579
  end
672
580
  end
@@ -690,22 +598,20 @@ module Squared
690
598
  end
691
599
  env('GRAPH', suffix: 'PASS') { |val| pass.concat(split_escape(val)) }
692
600
  start, neg = start.partition { |name| !name.start_with?('-') }
693
- data = graph_collect(self, start, pass: neg.map! { |name| name[1..-1] })
601
+ data = graph_collect(self, start, pass: neg.map { |name| name[1..-1] })
694
602
  unless out
695
603
  data[name] << self
696
604
  on :first, :graph
697
605
  end
698
606
  ret = graph_branch(self, data, tasks, out, sync: sync, pass: pass, order: order)
699
- rescue StandardError => e
700
- on_error(e, :graph, exception: true)
607
+ rescue => e
608
+ on_error(e, exception: true, dryrun: !out.nil?)
701
609
  else
702
610
  if out
703
611
  if order
704
- out.map! do |val|
705
- name = ret.find { |proj| val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/) }&.name
706
- next val unless (n = name && order[name])
707
-
708
- val.subhint(n.succ)
612
+ out.map do |val|
613
+ name = ret.find { |proj| val.match?(/ #{Regexp.escape(proj.name)}(@\d|\z)/) }&.name
614
+ (n = name && order[name]) ? val.subhint(n.succ) : val
709
615
  end
710
616
  else
711
617
  [out, ret]
@@ -716,7 +622,7 @@ module Squared
716
622
  end
717
623
 
718
624
  def unpack(target, file = nil, uri: nil, sync: true, digest: nil, ext: nil, force: false, depth: 1, headers: {},
719
- verbose: self.verbose, from: :unpack)
625
+ verbose: !silent?, from: :unpack)
720
626
  if !target.exist?
721
627
  target.mkpath
722
628
  elsif !target.directory?
@@ -866,8 +772,8 @@ module Squared
866
772
  '--home'
867
773
  end
868
774
  cmd << name << version
869
- when :exec
870
- cmd << name unless opts.first.start_with?(/#{name}\b/)
775
+ when :env, :exec
776
+ cmd << name if flag == :env || !opts.first.start_with?(/#{name}\b/)
871
777
  cmd.merge(opts)
872
778
  when :current
873
779
  cmd << '--no-header' unless legacy
@@ -876,7 +782,7 @@ module Squared
876
782
  cmd << name
877
783
  banner = false if flag == :latest || flag == :where
878
784
  end
879
- success?(run(banner: banner, from: :"asdf:#{flag}"), flag == :set || flag == :reshim)
785
+ success?(run(banner: banner, from: symjoin('asdf', flag)), flag == :set || flag == :reshim)
880
786
  end
881
787
 
882
788
  def first(key, *args, **kwargs, &blk)
@@ -893,14 +799,18 @@ module Squared
893
799
 
894
800
  def event(name, key, *args, override: false, **kwargs, &blk)
895
801
  args.unshift(blk) if block_given?
896
- ev = @events[name.to_sym]
802
+ ev = @on[:event][name.to_sym]
897
803
  (override ? ev[key.to_sym] = [] : ev[key.to_sym] ||= []) << [args, kwargs]
898
804
  self
899
805
  end
900
806
 
901
807
  def as(cmd, script, to = nil)
902
808
  data = @as[cmd.to_sym]
903
- (to ? [[script, to]] : script).each { |key, val| data[key.to_s] = val }
809
+ if script.is_a?(Enumerable)
810
+ script.each { |key, val| data[key.to_s] = val }
811
+ elsif to
812
+ data[script.to_s] = to
813
+ end
904
814
  self
905
815
  end
906
816
 
@@ -917,8 +827,8 @@ module Squared
917
827
  self
918
828
  end
919
829
 
920
- def run(cmd = @session, var = nil, exception: self.exception, sync: true, banner: true, from: nil, chdir: path,
921
- interactive: nil, hint: nil, series: false, **)
830
+ def run(cmd = @session, var = nil, exception: exception?, sync: true, banner: true, from: nil, chdir: path,
831
+ interactive: nil, hint: nil, series: false, timeout: nil, **)
922
832
  unless cmd
923
833
  print_error('no command session started', subject: project, hint: from, pass: true)
924
834
  return
@@ -936,6 +846,7 @@ module Squared
936
846
  msg = "#{msg} #{sub_style(h, theme[:active])}" if h
937
847
  exit 1 unless confirm_basic("#{msg}?", cmd, y)
938
848
  end
849
+ timeout = session_timeout cmd if timeout.nil?
939
850
  cmd = session_done cmd
940
851
  log&.info cmd
941
852
  on :first, from
@@ -956,10 +867,13 @@ module Squared
956
867
  end
957
868
  end
958
869
  args = var.is_a?(Hash) ? [var, cmd] : [cmd]
959
- ret = shell(*args, chdir: chdir, exception: exception)
870
+ ret = shell_t(*args, chdir: chdir, exception: exception, timeout: timeout || 0)
960
871
  end
961
- rescue StandardError => e
962
- on_error(e, from, exception: true)
872
+ rescue Timeout::Error => e
873
+ print_error(Logger::ERROR, cmd, subject: name, hint: e)
874
+ exit 1 unless exception?(Logger::DEBUG, Logger::INFO)
875
+ rescue => e
876
+ on_error(e, exception: true)
963
877
  false
964
878
  else
965
879
  on :last, from
@@ -973,6 +887,51 @@ module Squared
973
887
  end
974
888
  end
975
889
 
890
+ def global(*args)
891
+ @global > 0 && @global.anybits?(case args.size
892
+ when 1
893
+ args.first
894
+ when 0
895
+ 1 | 2
896
+ else
897
+ args.reduce(0) { |a, b| a | b }
898
+ end)
899
+ end
900
+
901
+ def global_set(name = nil, **kwargs)
902
+ return if (n = @global) == -1
903
+
904
+ @global = 0
905
+ data = scriptdata(name: name)
906
+ if n.anybits?(1 << 31)
907
+ @global = (n & (1 | 2)) | (1 << 31)
908
+ elsif @output[0] != false
909
+ kwargs.update(@initargs) if name
910
+ if @output[0].nil?
911
+ if data[:script]
912
+ unless kwargs[:script] == false
913
+ script_set(data[:script], args: data.fetch(:args, kwargs[:args]), prod: kwargs[:prod], global: true)
914
+ end
915
+ elsif data[:run]
916
+ run_set(data[:run], global: true)
917
+ end
918
+ if kwargs[:script]
919
+ script_set(kwargs[:script], args: kwargs[:args]) unless data[:env][:script]
920
+ elsif @script
921
+ if @script[:script]
922
+ script_set(@script[:script], args: @script.fetch(:args, kwargs[:args])) unless data[:global][:script]
923
+ elsif @script[:run] && !data[:global][:run]
924
+ run_set @script[:run]
925
+ end
926
+ end
927
+ elsif data[:run] && data[:env][:run]
928
+ run_set(data[:run], global: true)
929
+ end
930
+ end
931
+ self.global = 4 if data[:global][:doc] && doc?
932
+ self.global = 8 if data[:global][:test] && test?
933
+ end
934
+
976
935
  def variable_set(key, *args, **kwargs, &blk)
977
936
  if block_given?
978
937
  if blocks.include?(key)
@@ -988,6 +947,8 @@ module Squared
988
947
  index_set val
989
948
  when :graph
990
949
  graph_set val
950
+ when :timeout
951
+ timeout_set val
991
952
  when :pass
992
953
  pass_set val
993
954
  when :only
@@ -1021,25 +982,26 @@ module Squared
1021
982
  alias apply variable_set
1022
983
 
1023
984
  def enabled?(ref = nil, **)
1024
- return false if ref && !ref?(ref)
985
+ return false if ref && Array(ref).none? { |val| ref?(val) }
1025
986
 
1026
987
  (path.directory? && !path.empty?) || archive?
1027
988
  end
1028
989
 
1029
- def has?(meth, ref = nil)
990
+ def has?(meth, ref = nil, all: false, missing: false)
1030
991
  return false if ref && !ref?(ref)
1031
992
 
1032
- respond_to?(meth = :"#{meth}?") && __send__(meth)
993
+ pred = :"#{meth}?"
994
+ respond_to?(pred, all) ? __send__(pred) : missing
1033
995
  end
1034
996
 
1035
997
  def ref?(val)
1036
998
  @ref.include?(val)
1037
999
  end
1038
1000
 
1039
- def exist?(*args)
1040
- return false if (args = args.compact).empty?
1001
+ def exist?(*args, type: nil)
1002
+ return false if args.first.nil?
1041
1003
 
1042
- basepath(*args).exist?
1004
+ type ? !basepath!(*args, type: type).nil? : basepath(*args).exist?
1043
1005
  end
1044
1006
 
1045
1007
  def build?
@@ -1064,10 +1026,9 @@ module Squared
1064
1026
 
1065
1027
  def prereqs?
1066
1028
  target = self
1067
- loop do
1029
+ begin
1068
1030
  return true if target.graph?
1069
- break unless (target = target.parent)
1070
- end
1031
+ end while (target = target.parent)
1071
1032
  false
1072
1033
  end
1073
1034
 
@@ -1104,7 +1065,7 @@ module Squared
1104
1065
  end
1105
1066
 
1106
1067
  def exclude?(*refs)
1107
- !@exclude.empty? && has_value?(@exclude, refs.flatten)
1068
+ !@exclude.empty? && has_value?(@exclude, *refs)
1108
1069
  end
1109
1070
 
1110
1071
  def task_include?(key, ref = nil)
@@ -1126,7 +1087,7 @@ module Squared
1126
1087
  def log
1127
1088
  return @log unless @log.is_a?(Array)
1128
1089
 
1129
- @log = Logger.new((@log.first if enabled?), **@log.last)
1090
+ @log = Logger.new(enabled? ? @log.first : nil, **@log.last)
1130
1091
  end
1131
1092
 
1132
1093
  def allref(&blk)
@@ -1183,7 +1144,12 @@ module Squared
1183
1144
  def scriptname(from: :run)
1184
1145
  return unless (name = @output[1]) && respond_to?(:compose)
1185
1146
 
1186
- as_get name, from
1147
+ as_get name, from, 2
1148
+ end
1149
+
1150
+ def scriptdata(*keys, name: nil)
1151
+ ret = @workspace.script_find(*@ref, @group, name: name)
1152
+ keys.empty? ? ret : ret.dig(*keys)
1187
1153
  end
1188
1154
 
1189
1155
  def inspect
@@ -1202,21 +1168,123 @@ module Squared
1202
1168
 
1203
1169
  def script_get(*args, key: nil)
1204
1170
  ret = workspace.script_get(*args, group: group, ref: allref)
1205
- return ret unless ret && key
1171
+ ret && key ? ret.fetch(key, nil) : ret
1172
+ end
1206
1173
 
1207
- ret.fetch(key, nil)
1174
+ def index_get
1175
+ @index
1176
+ end
1177
+
1178
+ def envname_get
1179
+ @envname
1180
+ end
1181
+
1182
+ def graph_get
1183
+ @graph
1208
1184
  end
1209
1185
 
1210
1186
  private
1211
1187
 
1188
+ def initialize_ref(ref)
1189
+ @ref << ref unless @exclude.include?(ref)
1190
+ end
1191
+
1192
+ def initialize_build(ref, **kwargs)
1193
+ initialize_ref ref
1194
+ if (@script = @workspace.script_get(group: @group, ref: ref))
1195
+ if @script[:log] && !kwargs.key?(:log)
1196
+ kwargs[:log] = @script[:log]
1197
+ @log = nil
1198
+ end
1199
+ @depend = @script[:depend] if @depend.nil?
1200
+ @doc = @script[:doc] if @doc.nil?
1201
+ @lint = @script[:lint] if @lint.nil?
1202
+ @test = @script[:test] if @test.nil?
1203
+ @clean = @script[:clean] if @clean.nil?
1204
+ @exclude = @script[:exclude] if @exclude.empty? && @script.key?(:exclude)
1205
+ end
1206
+ @workspace.events_get(group: @group, ref: ref)&.each do |task, data|
1207
+ data.each { |ev, blk| @on[:event][ev][task] ||= [blk] }
1208
+ end
1209
+ initialize_logger kwargs[:log]
1210
+ global_set(script: kwargs[:script], args: kwargs[:args], prod: kwargs[:prod])
1211
+ end
1212
+
1213
+ def initialize_logger(log)
1214
+ return if @log
1215
+
1216
+ log = log.is_a?(Hash) ? log.dup : { file: log }
1217
+ file = if (val = env('LOG_FILE'))
1218
+ Time.now.strftime(val)
1219
+ elsif (val = env('LOG_AUTO'))
1220
+ require 'date'
1221
+ "#{@name}-%s.log" % [case val
1222
+ when 'y', 'year'
1223
+ Date.today.year
1224
+ when 'm', 'month'
1225
+ Date.today.strftime('%Y-%m')
1226
+ when 'd', 'day', '1'
1227
+ Date.today
1228
+ else
1229
+ val.include?('%') ? Time.now.strftime(val) : Time.now.strftime('%FT%T%:z')
1230
+ end]
1231
+ elsif (val = log[:file])
1232
+ if val.is_a?(String)
1233
+ Time.now.strftime(val)
1234
+ else
1235
+ require 'date'
1236
+ "#{@name}-#{Date.today}.log"
1237
+ end
1238
+ end
1239
+ .yield_self do |path|
1240
+ next unless path
1241
+
1242
+ @workspace.home.join(env('LOG_DIR', ''), path).realdirpath
1243
+ rescue => e
1244
+ print_error e
1245
+ end
1246
+ log[:progname] ||= @name
1247
+ env('LOG_LEVEL', ignore: false) { |val| log[:level] = val.start_with?(/\d/) ? log_sym(val.to_i) : val }
1248
+ log.delete(:file)
1249
+ @log = [file, log]
1250
+ end
1251
+
1252
+ def initialize_env(dev: nil, prod: nil, **)
1253
+ @dev = env_match('BUILD', dev, suffix: 'DEV', strict: true)
1254
+ @prod = env_match('BUILD', prod, suffix: 'PROD', strict: true)
1255
+ unless @output[0] == false
1256
+ env('BUILD', suffix: 'ENV') do |val|
1257
+ next unless (val = parse_json(val, kind: nil)).is_a?(Hash)
1258
+
1259
+ @output[2] = val
1260
+ end
1261
+ end
1262
+ unless @output[0] == false || @output[0].is_a?(Array)
1263
+ env('BUILD', suffix: 'OPTS') do |val|
1264
+ n = @output[0] ? 1 : 3
1265
+ @output[n] = merge_opts @output[n], shell_split(val)
1266
+ end
1267
+ env(ref.to_s.upcase, suffix: 'OPTS') { |val| @output[4] = merge_opts @output[4], shell_split(val) }
1268
+ end
1269
+ env('BUILD', suffix: 'VERSION') { |val| self.version = val }
1270
+ env('BUILD', strict: true) do |val|
1271
+ if val == '0'
1272
+ @output = [false]
1273
+ elsif script?
1274
+ script_set(val, global: 1 << 31)
1275
+ else
1276
+ run_set(val, global: 1 << 31)
1277
+ end
1278
+ end
1279
+ end
1280
+
1212
1281
  def puts(*args, **kwargs)
1213
1282
  log_console(*args, pipe: kwargs[:pipe] || pipe)
1214
1283
  end
1215
1284
 
1216
1285
  def run_s(*cmd, sync: true, banner: !silent?, from: nil, **kwargs)
1217
- cmd.flatten!
1218
1286
  case cmd.last
1219
- when Hash, TrueClass, FalseClass
1287
+ when Hash, true, false
1220
1288
  var = cmd.pop
1221
1289
  end
1222
1290
  on :first, from
@@ -1225,9 +1293,8 @@ module Squared
1225
1293
  print_run val, banner
1226
1294
  run(val, var, sync: sync, banner: banner, **kwargs)
1227
1295
  end
1228
- rescue StandardError => e
1229
- ret = on :error, from, e
1230
- raise unless ret == true
1296
+ rescue => e
1297
+ on_error(e, exception: kwargs.fetch(:exception, exception?))
1231
1298
  end
1232
1299
  on :last, from
1233
1300
  end
@@ -1241,11 +1308,8 @@ module Squared
1241
1308
  when Proc
1242
1309
  instance_eval(&obj)
1243
1310
  when Method
1244
- args = if (n = obj.arity.abs) > 0
1245
- Array.new(n).tap { |data| data[0] = self }
1246
- else
1247
- []
1248
- end
1311
+ n = obj.arity.abs
1312
+ args = n > 0 ? Array.new(n).tap { |data| data[0] = self } : []
1249
1313
  obj.call(*args)
1250
1314
  else
1251
1315
  if series?(obj)
@@ -1258,8 +1322,25 @@ module Squared
1258
1322
  end
1259
1323
  end
1260
1324
 
1261
- def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], order: nil, depth: 0,
1262
- single: false, last: false, context: nil)
1325
+ def run_e(cmd, stderr: false, banner: true, **kwargs)
1326
+ require 'open3'
1327
+ if stderr
1328
+ Open3.popen3(cmd) do |_, out, err|
1329
+ n = write_lines(out, banner: banner, pass: true, **kwargs)
1330
+ if n == 0
1331
+ n = write_lines(err, banner: banner)
1332
+ success?(n == 0, n == 0 && !banner.nil?)
1333
+ else
1334
+ write_lines(err, loglevel: Logger::DEBUG)
1335
+ end
1336
+ end
1337
+ else
1338
+ Open3.popen2e(cmd) { |_, out| write_lines(out, banner: banner, **kwargs) }
1339
+ end
1340
+ end
1341
+
1342
+ def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], depth: 0, single: false,
1343
+ last: false, order: nil, **kwargs)
1263
1344
  tag = ->(proj) { "#{proj.name}#{"@#{proj.version}" if SEM_VER.match?(proj.version)}" }
1264
1345
  uniq = lambda do |name|
1265
1346
  return [] unless (ret = data[name])
@@ -1277,6 +1358,8 @@ module Squared
1277
1358
  else
1278
1359
  items = data[target.name] - done
1279
1360
  end
1361
+ return done if items.empty?
1362
+
1280
1363
  if out
1281
1364
  a, b, c, d, e = ARG[:GRAPH]
1282
1365
  f = tag.call(target)
@@ -1293,6 +1376,7 @@ module Squared
1293
1376
  "#{single ? ' ' : a}#{' ' * depth.pred}#{last ? d : c}#{b * 3}#{items.empty? ? b : e} #{f}"
1294
1377
  end
1295
1378
  end
1379
+ ctx = { sync: sync, pass: pass, done: done, order: order, single: single, depth: depth.succ, context: target }
1296
1380
  items.each_with_index do |proj, i|
1297
1381
  next if done.include?(proj)
1298
1382
 
@@ -1305,8 +1389,7 @@ module Squared
1305
1389
  end
1306
1390
  end
1307
1391
  unless target.name == name || (none = (t - done).empty?)
1308
- graph_branch(proj, data, tasks, out, sync: sync, pass: pass, done: done, order: order, depth: depth.succ,
1309
- single: single, last: j == true, context: target)
1392
+ graph_branch(proj, data, tasks, out, last: j == true, **ctx)
1310
1393
  end
1311
1394
  if out
1312
1395
  if none
@@ -1319,7 +1402,7 @@ module Squared
1319
1402
  final = data.keys.last
1320
1403
  while k < depth
1321
1404
  indent = k > 0 ? ((last && !j) || (j && k == depth.pred) || single) : j && last && depth == 1
1322
- s += "#{indent || (last && data[final].last == context) ? ' ' : a} "
1405
+ s += "#{indent || (last && data[final].last == kwargs[:context]) ? ' ' : a} "
1323
1406
  k += 1
1324
1407
  end
1325
1408
  s += "#{j ? d : c}#{b * 3} #{tag.call(proj)}"
@@ -1345,14 +1428,14 @@ module Squared
1345
1428
  next if pass.include?(meth)
1346
1429
 
1347
1430
  if workspace.task_defined?(cmd = task_join(name, meth))
1348
- if ENV.key?(key = "BANNER_#{name.upcase}")
1431
+ if ENV.key?(key = "BANNER_#{envname_get}")
1349
1432
  key = nil
1350
1433
  else
1351
1434
  ENV[key] = '0'
1352
1435
  end
1353
1436
  run(cmd, sync: false, banner: false)
1354
1437
  ENV.delete(key) if key
1355
- elsif proj.has?(meth, (workspace.baseref unless tasks || graph))
1438
+ elsif proj.has?(meth, tasks || graph ? nil : workspace.baseref)
1356
1439
  proj.__send__(meth.to_sym, sync: sync)
1357
1440
  end
1358
1441
  end
@@ -1363,15 +1446,14 @@ module Squared
1363
1446
  end
1364
1447
 
1365
1448
  def graph_collect(target, start = [], data: {}, pass: [], root: [])
1366
- deps = []
1367
- (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
1449
+ deps = Array(start.empty? ? target.graph_get : start).each_with_object([]) do |val, out|
1368
1450
  next if pass.include?(val)
1369
1451
 
1370
1452
  if (obj = workspace.find(name: val))
1371
1453
  obj.enabled? ? [obj] : []
1372
1454
  else
1373
- workspace.find(group: val, ref: val.to_sym)
1374
- end.sort.each do |proj|
1455
+ workspace.find(group: val, ref: val.to_sym).sort
1456
+ end.each do |proj|
1375
1457
  next if pass.include?(name = proj.name)
1376
1458
 
1377
1459
  if proj.graph? && !data.key?(name) && !root.include?(name)
@@ -1379,8 +1461,8 @@ module Squared
1379
1461
  end
1380
1462
  next if (objs = data.fetch(name, [])).include?(target)
1381
1463
 
1382
- deps << proj
1383
- deps.concat(objs)
1464
+ out << proj
1465
+ out.concat(objs)
1384
1466
  end
1385
1467
  end
1386
1468
  deps.uniq!
@@ -1391,22 +1473,22 @@ module Squared
1391
1473
 
1392
1474
  def graph_deps(target = self)
1393
1475
  key = target.name
1394
- return @@graph[key] if @@graph.key?(key)
1395
-
1396
- base = []
1397
- deps = []
1398
- loop do
1399
- deps.concat(graph_branch(target, graph_collect(target), []))
1400
- break unless (target = target.parent)
1476
+ @@graph[key] ||= begin
1477
+ base = []
1478
+ deps = []
1479
+ loop do
1480
+ deps.concat(graph_branch(target, graph_collect(target), []))
1481
+ break unless (target = target.parent)
1401
1482
 
1402
- base << target
1483
+ base << target
1484
+ end
1485
+ deps.uniq!
1486
+ [base, deps]
1403
1487
  end
1404
- deps.uniq!
1405
- @@graph[key] = [base, deps]
1406
1488
  end
1407
1489
 
1408
- def env(key, default = nil, suffix: nil, equals: nil, ignore: nil, strict: false)
1409
- name = "#{key}_#{@envname}"
1490
+ def env(key, default = nil, suffix: nil, strict: false, ignore: nil, **kwargs, &blk)
1491
+ name = "#{key}_#{envname_get}"
1410
1492
  ret = if suffix
1411
1493
  ENV.fetch("#{name}_#{suffix}", '')
1412
1494
  elsif strict
@@ -1415,15 +1497,7 @@ module Squared
1415
1497
  ignore = ['0'].freeze if ignore.nil?
1416
1498
  ENV[name] || ENV.fetch(key, '')
1417
1499
  end
1418
- if equals.nil?
1419
- ret = default if ret.empty? || (ignore && Array(ignore).any? { |val| ret == val.to_s })
1420
- return ret if ret.nil?
1421
- else
1422
- ret = Array(equals).any? { |val| ret == val.to_s }
1423
- end
1424
- return yield ret if block_given?
1425
-
1426
- ret
1500
+ env_yield(ret, default, ignore: ignore, **kwargs, &blk)
1427
1501
  end
1428
1502
 
1429
1503
  def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
@@ -1431,7 +1505,7 @@ module Squared
1431
1505
  if path && (val = shell_bin(prefix))
1432
1506
  cmd[0] = shell_quote(val, force: false)
1433
1507
  end
1434
- ret = JoinSet.new(cmd.flatten(1))
1508
+ ret = JoinSet.new(cmd)
1435
1509
  if options
1436
1510
  env("#{prefix.upcase}_OPTIONS") do |val|
1437
1511
  if val.start_with?('-')
@@ -1441,9 +1515,17 @@ module Squared
1441
1515
  end
1442
1516
  end
1443
1517
  end
1444
- return ret unless main
1518
+ cache = @timeout[:_]
1519
+ cache[1] ||= workspace.timeout_get(*@ref, group: group)
1520
+ cache[0][ret] = if cmd[1..-1].find { |val| val =~ /\A([A-Za-z][\w-]*)(?<![-_])(?: |\z)/ }
1521
+ command = :"#{prefix}_#{$1}"
1522
+ cache[1][command] || @timeout[command]
1523
+ end || cache[1][prefix.to_sym] || @timeout[prefix.to_sym]
1524
+ main ? @session = ret : ret
1525
+ end
1445
1526
 
1446
- @session = ret
1527
+ def session_timeout(cmd)
1528
+ @timeout[:_][0][cmd] || @timeout[:_][2]
1447
1529
  end
1448
1530
 
1449
1531
  def session_output(*cmd, **kwargs)
@@ -1467,18 +1549,18 @@ module Squared
1467
1549
  end
1468
1550
 
1469
1551
  def session_apply(val, args: nil, kwargs: nil, pass: [], keys: [:opts], exclude: [])
1470
- a = []
1471
1552
  b = {}
1472
- Array(val).each do |c|
1553
+ a = Array(val).each_with_object([]) do |c, out|
1473
1554
  d, e, f = session_get c
1474
- unless (f -= exclude).empty?
1555
+ f -= exclude
1556
+ unless f.empty?
1475
1557
  h = []
1476
1558
  i = {}
1477
- session_apply(f.map!(&:to_s), args: h, kwargs: i, pass: pass, keys: keys, exclude: exclude.concat(f))
1478
- a.concat(h)
1559
+ session_apply(f.map(&:to_s), args: h, kwargs: i, pass: pass, keys: keys, exclude: exclude.concat(f))
1560
+ out.concat(h)
1479
1561
  append_keys(b, i, *keys)
1480
1562
  end
1481
- a.concat(d)
1563
+ out.concat(d)
1482
1564
  append_keys(b, e, *keys)
1483
1565
  end
1484
1566
  if args
@@ -1511,23 +1593,48 @@ module Squared
1511
1593
  OptionPartition.arg?(target, *args, **kwargs)
1512
1594
  end
1513
1595
 
1514
- def option(*args, target: @session, prefix: target&.first, **kwargs)
1596
+ def option(*args, target: @session, prefix: target&.first, path: false, escape: false, quote: false, pat: nil,
1597
+ **kwargs)
1515
1598
  return unless prefix
1516
1599
 
1517
- args.each do |val|
1518
- next unless (ret = env(env_key(prefix.to_s.stripext, val), **kwargs))
1600
+ args.each do |key|
1601
+ next unless (ret = env(env_key(prefix.to_s.stripext, key), **kwargs)) && (!pat || ret.match?(pat))
1519
1602
 
1603
+ if path
1604
+ target << quote_option(key, basepath(ret))
1605
+ elsif quote
1606
+ target << quote_option(key, ret)
1607
+ elsif escape
1608
+ target << shell_option(key, ret)
1609
+ end
1520
1610
  return block_given? ? yield(ret) : ret
1521
1611
  end
1522
1612
  nil
1523
1613
  end
1524
1614
 
1525
- def option_clear(opts, empty = true, target: @session, **kwargs)
1526
- return unless target
1615
+ def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1616
+ grep = grep.empty? ? nil : matchmap(grep, prefix)
1617
+ sub = stdin? ? nil : as_a(sub)
1618
+ ret = 0
1619
+ lines = data.each_with_object([]) do |line, out|
1620
+ next if grep&.none? { |pat| pat.match?(line) }
1621
+ next if block_given? && !(line = yield(line, ret))
1527
1622
 
1528
- OptionPartition.clear(target, opts, styles: theme[:inline], **kwargs)
1529
- opts.clear if empty
1530
- nil
1623
+ if loglevel
1624
+ log&.add loglevel, line
1625
+ else
1626
+ sub&.each { |h| sub_style!(line, **h) }
1627
+ if banner
1628
+ out << line
1629
+ else
1630
+ puts line
1631
+ end
1632
+ end
1633
+ ret += 1
1634
+ break out if first
1635
+ end
1636
+ print_item banner, lines if banner && (ret > 0 || (!pass && !first))
1637
+ ret
1531
1638
  end
1532
1639
 
1533
1640
  def print_success
@@ -1537,11 +1644,22 @@ module Squared
1537
1644
  def print_error(*args, loglevel: Logger::WARN, **kwargs)
1538
1645
  return unless warning?
1539
1646
 
1540
- warn log_message(loglevel, *args, **kwargs)
1647
+ loglevel = args.shift if args.first.is_a?(Numeric) && args.first.between?(Logger::DEBUG, Logger::UNKNOWN)
1648
+ if loglevel >= Logger::WARN && exception?(Logger::ERROR, Logger::FATAL)
1649
+ $stderr.puts log_message([loglevel, Logger::Error].max, *args, **kwargs)
1650
+ if exception == Logger::ERROR
1651
+ ex = [args.first, kwargs[:subject], kwargs[:hint]].compact
1652
+ raise(ex.find { |val| val.class < Exception } || ex.first)
1653
+ else
1654
+ exit 1
1655
+ end
1656
+ else
1657
+ warn log_message(loglevel, *args, **kwargs)
1658
+ end
1541
1659
  end
1542
1660
 
1543
1661
  def print_run(cmd, banner = true, verbose: nil, **)
1544
- return if banner || !stdout? || verbose == false
1662
+ return if banner || !stdout? || verbose == false || env('BANNER', equals: '0')
1545
1663
 
1546
1664
  puts "\n> #{cmd}"
1547
1665
  printsucc
@@ -1553,19 +1671,19 @@ module Squared
1553
1671
  puts val unless val.empty? || (val.size == 1 && !val.first)
1554
1672
  end
1555
1673
 
1556
- def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle, **)
1674
+ def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle)
1557
1675
  pad = 0
1558
1676
  if styles
1559
1677
  if styles.any? { |s| s.to_s.end_with?('!') }
1560
1678
  pad = 1
1561
1679
  elsif !client && styles.size <= 1
1562
- styles = [:bold] + styles
1680
+ styles += [:bold]
1563
1681
  end
1564
1682
  end
1565
1683
  n = line_width lines
1566
1684
  ch = ' ' * pad
1567
1685
  index = -1
1568
- lines.map! do |val|
1686
+ ret = lines.map do |val|
1569
1687
  index += 1
1570
1688
  val = ch + val.ljust(n - (pad * 2)) + ch
1571
1689
  if styles && (pad == 1 || index == 0)
@@ -1574,23 +1692,24 @@ module Squared
1574
1692
  val
1575
1693
  end
1576
1694
  end
1577
- (lines << sub_style(ARG[:BORDER][1] * n, border)).join("\n")
1695
+ ret << sub_style(ARG[:BORDER][1] * n, border)
1696
+ ret.join("\n")
1578
1697
  end
1579
1698
 
1580
- def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle, **)
1699
+ def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle)
1581
1700
  n = line_width lines
1582
1701
  sub = as_a sub
1583
- lines.map! do |val|
1702
+ ret = lines.map do |val|
1584
1703
  s = right ? val.rjust(n) : val.ljust(n)
1585
1704
  sub.each { |h| sub_style!(s, **h) }
1586
1705
  s
1587
1706
  end
1588
- [sub_style(ARG[:BORDER][1] * n, border)].concat(lines)
1589
- .tap { |ret| ret.reverse! if reverse }
1590
- .join("\n")
1707
+ ret.unshift(sub_style(ARG[:BORDER][1] * n, border))
1708
+ ret.reverse! if reverse
1709
+ ret.join("\n")
1591
1710
  end
1592
1711
 
1593
- def print_status(*args, from: nil, **kwargs)
1712
+ def print_status(*args, from: :completed, **kwargs)
1594
1713
  return if stdin? || silent?
1595
1714
 
1596
1715
  case from
@@ -1616,7 +1735,7 @@ module Squared
1616
1735
  workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
1617
1736
  end
1618
1737
 
1619
- def format_banner(cmd, banner: true, hint: nil, strip: nil)
1738
+ def format_banner(cmd, banner: true, hint: nil, strip: nil, quote: false)
1620
1739
  return unless banner && banner?
1621
1740
 
1622
1741
  if (data = workspace.banner_get(*@ref, group: group))
@@ -1624,7 +1743,7 @@ module Squared
1624
1743
 
1625
1744
  client = true
1626
1745
  else
1627
- data = Struct::BannerData.new(true, [:path], theme[:banner], theme[:border])
1746
+ data = Struct::BannerData.new(true, theme[:banner], theme[:border], [:path])
1628
1747
  end
1629
1748
  if verbose
1630
1749
  out = []
@@ -1637,6 +1756,7 @@ module Squared
1637
1756
  cmd = cmd.gsub(/(?:#{s = Regexp.escape(File.join(path, ''))}?(?=["'])|#{s})/, '')
1638
1757
  .gsub(/(?: -[^ ])? (?:""|'')/, '')
1639
1758
  end
1759
+ cmd.gsub!(/(?<= )(["'])([\w.-]+)\1(?= |\z)/, '\2') unless quote
1640
1760
  out << cmd.subhint(hint)
1641
1761
  end
1642
1762
  data.order.each do |val|
@@ -1671,7 +1791,7 @@ module Squared
1671
1791
  unless items.empty?
1672
1792
  pad = items.size.to_s.size
1673
1793
  items.each_with_index do |val, i|
1674
- next unless matchany?(val.first, reg)
1794
+ next unless matchany?(reg, val.first)
1675
1795
 
1676
1796
  out << ('%*d. %s' % [pad, i.succ, block_given? ? yield(val) : val.first])
1677
1797
  end
@@ -1685,7 +1805,7 @@ module Squared
1685
1805
  out << ''
1686
1806
  end
1687
1807
  if from
1688
- out << (from = from.to_s)
1808
+ out << from
1689
1809
  /\A(#{Regexp.escape(from)})(.*)\z/
1690
1810
  end
1691
1811
  else
@@ -1718,18 +1838,21 @@ module Squared
1718
1838
  end
1719
1839
  end
1720
1840
  data.each do |key, val|
1721
- next if (key = key.to_s).start_with?('__')
1841
+ key = key.to_s
1842
+ next if key.start_with?('__')
1722
1843
 
1723
1844
  if val.nil? || extra || session_arg?(key, target: target)
1724
1845
  OptionPartition.delete_key(target, key)
1725
1846
  next if val.nil?
1726
1847
  end
1727
1848
  case val
1728
- when Array
1849
+ when Hash
1850
+ append_hash(val, target: target, build: build)
1851
+ when Enumerable
1729
1852
  append_repeat(key, val, target: target)
1730
1853
  when Numeric
1731
1854
  target << basic_option(key, val)
1732
- when FalseClass
1855
+ when false
1733
1856
  target << shell_option(key).sub(/^--(?!no-)/, '--no-')
1734
1857
  when Pathname
1735
1858
  target << shell_option(key, val, escape: false)
@@ -1766,9 +1889,7 @@ module Squared
1766
1889
 
1767
1890
  def append_first(*list, target: @session, flag: true, equals: false, escape: true, quote: true, force: true,
1768
1891
  **kwargs)
1769
- return if list.empty?
1770
-
1771
- list.flatten.each do |opt|
1892
+ list.each do |opt|
1772
1893
  next unless (val = option(opt, **kwargs))
1773
1894
 
1774
1895
  return target << if flag
@@ -1782,43 +1903,38 @@ module Squared
1782
1903
 
1783
1904
  def append_option(*list, target: @session, no: false, equals: false, escape: true, quote: true, force: true,
1784
1905
  **kwargs)
1785
- return if list.empty?
1786
-
1787
1906
  kwargs[:ignore] = false if no && !kwargs.key?(:ignore)
1788
- ret = []
1789
- list.flatten.each do |flag|
1907
+ ret = list.each_with_object([]) do |flag, out|
1790
1908
  next unless (val = option(flag, target: target, **kwargs))
1791
1909
 
1792
1910
  if no && val == '0'
1793
1911
  flag = "no-#{flag}"
1794
1912
  val = nil
1795
1913
  end
1796
- ret << shell_option(flag, (val if equals), escape: escape, quote: quote, force: force)
1914
+ out << shell_option(flag, (val if equals), escape: escape, quote: quote, force: force)
1797
1915
  end
1798
1916
  merge_list target, ret unless ret.empty?
1799
- ret
1800
1917
  end
1801
1918
 
1802
- def append_nocolor(target: @session)
1803
- target << '--no-color' if !ARG[:COLOR] || stdin? || option('color', target: target, equals: '0')
1919
+ def append_nocolor(flag = '--no-color', target: @session, **kwargs)
1920
+ target << flag if !ARG[:COLOR] || stdin? || option('color', target: target, equals: '0', **kwargs)
1804
1921
  end
1805
1922
 
1806
1923
  def append_keys(base, data, *keys)
1807
- out = {}
1808
- keys.each do |key|
1924
+ ret = keys.each_with_object({}) do |key, out|
1809
1925
  next unless data.key?(key)
1810
1926
 
1811
1927
  out[key] = case (val = data[key])
1812
- when Array
1813
- base.fetch(key, []) + val
1814
1928
  when Hash
1815
1929
  base.fetch(key, {}).update(val)
1930
+ when Enumerable
1931
+ Array(base.fetch(key, [])) + val.to_a
1816
1932
  else
1817
1933
  val
1818
1934
  end
1819
1935
  end
1820
1936
  base.update(data)
1821
- .update(out)
1937
+ .update(ret)
1822
1938
  end
1823
1939
 
1824
1940
  def merge_opts(base, data)
@@ -1861,22 +1977,9 @@ module Squared
1861
1977
  end
1862
1978
  end
1863
1979
 
1864
- def merge_list(base, data)
1865
- data = Array(data)
1866
- case base
1867
- when Array
1868
- base.concat(data)
1869
- when Set
1870
- base.merge(data)
1871
- else
1872
- Array(base).concat(data)
1873
- end
1874
- end
1875
-
1876
1980
  def collect_hash(data, pass: [])
1877
- ret = []
1878
- data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1879
- ret
1981
+ data = data.reject { |key,| pass.include?(key) } unless pass.empty?
1982
+ data.values.flatten
1880
1983
  end
1881
1984
 
1882
1985
  def replace_bin(val)
@@ -1890,14 +1993,13 @@ module Squared
1890
1993
  ret = JSON.parse(val)
1891
1994
  raise_error 'invalid JSON'.subhint(kind.name), val, hint: hint if kind && !ret.is_a?(kind)
1892
1995
  ret
1893
- rescue StandardError => e
1894
- log&.warn e
1895
- print_error(e, subject: name)
1996
+ rescue => e
1997
+ print_error(kind ? Logger::ERROR : Logger::INFO, e, subject: name)
1896
1998
  end
1897
1999
 
1898
2000
  def parse_env(key)
1899
2001
  env(key) do |val|
1900
- val.start_with?('-') ? shell_parse(val) : split_escape(val).map! { |opt| fill_option(opt) }
2002
+ val.start_with?('-') ? shell_parse(val) : split_escape(val).map { |opt| fill_option(opt) }
1901
2003
  end || []
1902
2004
  end
1903
2005
 
@@ -1919,65 +2021,46 @@ module Squared
1919
2021
  confirm("#{msg} [#{sub_style(target.to_s, style.is_a?(Symbol) ? theme[style] : style)}]", default, **kwargs)
1920
2022
  end
1921
2023
 
1922
- def confirm_outdated(pkg, ver, type, cur = nil, lock: false, col0: 0, col1: 0, col2: nil, col3: 0, col4: 0,
1923
- **kwargs)
1924
- h = sub_style(semrev(type).upcase, (type == 1 && theme[:major]) || theme[:header])
1925
- case col0
1926
- when 0
1927
- col0 = "#{h}: "
1928
- when Numeric
1929
- puts h
1930
- col0 = ' ' * col0
1931
- else
1932
- puts h
1933
- end
1934
- b = sub_style pkg.ljust(col1), theme[:inline]
1935
- cur ||= 'locked' if lock
1936
- c = if cur
1937
- cur = cur.ljust(col2 || cur.size.succ)
1938
- lock ? sub_style(cur, color(:red)) : cur
1939
- end
1940
- d = type == 1 || lock ? 'N' : 'Y'
1941
- e = "#{col0}#{b}#{c}#{sub_style(col1 > 0 ? ver.ljust(col3) : ver.rjust(ver.size.succ), theme[:inline])}"
1942
- confirm("#{e}#{col4 > 0 ? ' ' * [col4 - e.stripstyle.size - 1, 2].max : ' '}", d, **kwargs)
1943
- end
1944
-
1945
2024
  def confirm_semver(msg, type, style: (type == 1 && theme[:major]) || :inline, timeout: 0, **kwargs)
1946
- confirm_basic(msg, semrev(type), type == 1 ? 'N' : 'Y', style: style, timeout: timeout, **kwargs)
2025
+ rev = case type
2026
+ when 1
2027
+ 'major'
2028
+ when 2
2029
+ 'minor'
2030
+ else
2031
+ 'patch'
2032
+ end
2033
+ confirm_basic(msg, rev, type == 1 ? 'N' : 'Y', style: style, timeout: timeout, **kwargs)
1947
2034
  end
1948
2035
 
1949
2036
  def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil, multiple: false,
1950
2037
  force: true, **kwargs)
1951
2038
  puts unless series || printfirst?
1952
- ret = choice(msg, list, multiple: multiple, force: force, **kwargs).tap do |val|
1953
- next unless !val || val.empty?
1954
-
2039
+ ret = choice(msg, list, multiple: multiple, force: force, **kwargs)
2040
+ unless ret && !ret.empty?
1955
2041
  exit 1 if force
1956
- return nil
2042
+ return
1957
2043
  end
1958
- ret = multiple ? ret.map! { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
2044
+ ret = multiple ? ret.map { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
1959
2045
  if column
1960
2046
  a, b = Array(column)
1961
- ret = Array(ret).map! { |val| val[a, b || 1] }
2047
+ ret = Array(ret).map { |val| val[a, b || 1] }
1962
2048
  ret = ret.first unless multiple
1963
2049
  end
1964
2050
  if accept
1965
2051
  hint = Array(ret).map { |val| sub_style(val.to_s, theme[:inline]) }.join(', ')
1966
2052
  accept = Array(accept).map { |val| Array(val) }
1967
2053
  ret = Array(ret) if accept.any? { |val| val[1] == true }
1968
- loop do
1969
- item = accept.first
2054
+ begin
2055
+ item = accept.shift
1970
2056
  c = confirm("#{item[0]}#{" [#{hint}]" if hint}", item[2] ? 'Y' : 'N')
1971
2057
  if item[1] == true
1972
2058
  ret << c
1973
2059
  elsif !c
1974
- break
2060
+ exit 1
1975
2061
  end
1976
2062
  hint = nil
1977
- accept.shift
1978
- break if accept.empty?
1979
- end
1980
- exit 1 unless accept.empty?
2063
+ end until accept.empty?
1981
2064
  end
1982
2065
  if values
1983
2066
  ret = Array(ret)
@@ -1988,7 +2071,7 @@ module Squared
1988
2071
  force = false
1989
2072
  end
1990
2073
  val = readline(val, force: force)
1991
- ret << (val unless val.empty?)
2074
+ ret << (val.empty? ? nil : val)
1992
2075
  end
1993
2076
  end
1994
2077
  printsucc unless series
@@ -2006,10 +2089,11 @@ module Squared
2006
2089
  def command_args(args, min: 0, force: false, **kwargs)
2007
2090
  return if args.size > min || option('i', 'interactive', **kwargs, equals: '0')
2008
2091
 
2009
- readline('Enter arguments', force: force)
2092
+ ret = readline('Enter arguments', force: force)
2093
+ args << ret unless ret.empty?
2010
2094
  end
2011
2095
 
2012
- def block_args(fallback = nil, &blk)
2096
+ def block_args(fallback = [], &blk)
2013
2097
  return fallback if (ret = instance_eval(&blk)).nil?
2014
2098
 
2015
2099
  Array(ret)
@@ -2028,33 +2112,40 @@ module Squared
2028
2112
  if workspace.powershell?
2029
2113
  "#{shell_bin('powershell.exe')} -Command \"& {#{args.join(' ; ')}}\"#{out}"
2030
2114
  else
2031
- args.map! { |val| "#{val}#{out}" }.join(' && ')
2115
+ args.map { |val| "#{val}#{out}" }.join(' && ')
2032
2116
  end
2033
2117
  end
2034
2118
 
2035
2119
  def relativepath(*list, all: false)
2036
- list.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
2037
- ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
2038
- all && val.to_s.end_with?('/') ? "#{ret}/*" : ret
2039
- end
2120
+ list.map { |val| Pathname.new(val) }
2121
+ .select { |val| projectpath?(val) }
2122
+ .map do |val|
2123
+ ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
2124
+ all && val.to_s.end_with?('/') ? "#{ret}/*" : ret
2125
+ end
2040
2126
  end
2041
2127
 
2042
- def projectmap(files, parent: false, pass: true)
2128
+ def projectmap(files, resolve: true, parent: false, pass: true)
2043
2129
  unless parent
2044
- proj = files.select { |val| projectpath?(val) }
2045
- raise_error 'pathspec not within worktree' unless pass || files.size == proj.size
2046
- files = proj
2130
+ files = files.select { |val| projectpath?(val) }.tap do |list|
2131
+ next if pass || files.size == list.size
2132
+
2133
+ raise_error 'pathspec not within worktree'
2134
+ end
2135
+ end
2136
+ files.map do |val|
2137
+ if val == '.'
2138
+ '.'
2139
+ else
2140
+ val = basepath(val)
2141
+ val = val.relative_path_from(path) unless resolve
2142
+ shell_quote val
2143
+ end
2047
2144
  end
2048
- files.map { |val| val == '.' ? '.' : shell_quote(basepath(val)) }
2049
2145
  end
2050
2146
 
2051
- def matchmap(list, prefix = nil)
2052
- list.map do |val|
2053
- next val if val.is_a?(Regexp)
2054
-
2055
- val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
2056
- Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
2057
- end
2147
+ def symjoin(*args, char: ':')
2148
+ args.join(char).to_sym
2058
2149
  end
2059
2150
 
2060
2151
  def semver(val)
@@ -2079,10 +2170,10 @@ module Squared
2079
2170
  return -1 if (b = other.scan(SEM_VER)).empty?
2080
2171
  return 1 if (a = val.scan(SEM_VER)).empty?
2081
2172
 
2082
- a, b = [a.first, b.first].map! do |c|
2173
+ a, b = [a.first, b.first].map do |c|
2083
2174
  d = begin
2084
2175
  Integer(c[5]).to_s
2085
- rescue StandardError
2176
+ rescue
2086
2177
  c[5] ? '-1' : '0'
2087
2178
  end
2088
2179
  [c[0], c[2], c[4] || '0', d]
@@ -2120,24 +2211,13 @@ module Squared
2120
2211
  end
2121
2212
 
2122
2213
  def semtype(cur, lat)
2123
- if semmajor?(cur, lat)
2124
- 1
2125
- else
2126
- cur[2] == lat[2] ? 3 : 2
2127
- end
2214
+ return 1 if semmajor?(cur, lat)
2215
+
2216
+ cur[2] == lat[2] ? 3 : 2
2128
2217
  end
2129
2218
 
2130
- def semrev(type)
2131
- case type
2132
- when 1
2133
- 'major'
2134
- when 2
2135
- 'minor'
2136
- when 3
2137
- 'patch'
2138
- else
2139
- 'unknown'
2140
- end
2219
+ def semmajor?(cur, want)
2220
+ (cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]) && !want[5]
2141
2221
  end
2142
2222
 
2143
2223
  def semgte?(val, other = nil)
@@ -2163,7 +2243,7 @@ module Squared
2163
2243
  def shortname(*args, suffix: '?', delim: ',', pass: false)
2164
2244
  return unless TASK_METADATA || pass
2165
2245
 
2166
- args.map! do |ch|
2246
+ args.map do |ch|
2167
2247
  "#{ch}/#{case ch
2168
2248
  when 'i'
2169
2249
  'nteractive'
@@ -2189,7 +2269,7 @@ module Squared
2189
2269
  end
2190
2270
 
2191
2271
  def color(val)
2192
- (ret = theme[val]) && !ret.empty? ? ret : [val]
2272
+ (ret = Array(theme[val])).empty? ? [val] : ret
2193
2273
  end
2194
2274
 
2195
2275
  def colormap(val)
@@ -2198,7 +2278,7 @@ module Squared
2198
2278
 
2199
2279
  def verbosetype
2200
2280
  case verbose
2201
- when TrueClass
2281
+ when true
2202
2282
  1
2203
2283
  when Numeric
2204
2284
  verbose.succ
@@ -2208,9 +2288,15 @@ module Squared
2208
2288
  end
2209
2289
 
2210
2290
  def on(event, from, *args, **kwargs)
2211
- return unless from && @events.key?(event)
2291
+ return unless from && @on[:event].key?(event)
2212
2292
 
2213
- Array(@events[event][from]).each do |obj|
2293
+ case event
2294
+ when :first
2295
+ @on[:from] << from
2296
+ when :last
2297
+ return unless @on[:from].delete(from)
2298
+ end
2299
+ Array(@on[:event][event][from]).each do |obj|
2214
2300
  target, opts = if obj.is_a?(Array) && obj[1].is_a?(Hash)
2215
2301
  [obj[0], kwargs.empty? ? obj[1] : obj[1].merge(kwargs)]
2216
2302
  else
@@ -2227,28 +2313,51 @@ module Squared
2227
2313
  end
2228
2314
  end
2229
2315
 
2230
- def on_error(err, from, pass: false, exception: self.exception, dryrun: false)
2316
+ def on_error(err, from = @on[:from].last, exception: exception?, fatal: true, dryrun: false, pass: false)
2231
2317
  log&.error err
2232
- unless dryrun
2233
- ret = on :error, from, err
2234
- raise err if exception && ret != true
2318
+ unless from == false
2319
+ cancel = -> { @on[:from] = [false] if fatal && @on[:from].include?(from) }
2320
+ if dryrun
2321
+ cancel.call
2322
+ else
2323
+ ret = on :error, from, err
2324
+ unless ret == true
2325
+ cancel.call
2326
+ raise err if exception
2327
+ end
2328
+ end
2235
2329
  end
2236
2330
  print_error(err, pass: pass) unless ret
2237
2331
  end
2238
2332
 
2239
- def pwd_set(pass: false, exception: self.exception, dryrun: false, from: nil)
2240
- return yield if (path.to_s == Dir.pwd || pass == true) && (workspace.mri? || !workspace.windows?)
2333
+ def pwd_set(cmd = nil, pass: false, timeout: nil, from: @on[:from].last, **kwargs, &blk)
2334
+ return if from == false
2241
2335
 
2242
- pwd = Dir.pwd
2243
- Dir.chdir(path)
2244
- yield
2245
- rescue StandardError => e
2246
- on_error(e, from, exception: exception, dryrun: dryrun)
2336
+ require 'timeout'
2337
+ unless (path.to_s == Dir.pwd || pass == true) && (workspace.mri? || !workspace.windows?)
2338
+ pwd = Dir.pwd
2339
+ Dir.chdir(path)
2340
+ end
2341
+ timeout = session_timeout cmd if cmd && timeout.nil?
2342
+ if timeout.to_f > 0
2343
+ Timeout.timeout(timeout, &blk)
2344
+ else
2345
+ yield
2346
+ end
2347
+ rescue Timeout::Error => e
2348
+ if cmd
2349
+ print_error(Logger::ERROR, cmd, subject: name, hint: e)
2350
+ else
2351
+ print_error(Logger::ERROR, e, subject: name)
2352
+ end
2353
+ exit 1 unless exception?(Logger::DEBUG, Logger::INFO)
2354
+ rescue => e
2355
+ on_error(e, from, **kwargs)
2247
2356
  ensure
2248
2357
  Dir.chdir(pwd) if pwd
2249
2358
  end
2250
2359
 
2251
- def run_set(cmd, val = nil, opts: nil, global: false, **)
2360
+ def run_set(cmd, val = nil, opts: nil, global: nil, **)
2252
2361
  noopt = @output[1] == false && !@output[0].nil?
2253
2362
  noenv = @output[2] == false
2254
2363
  parse = lambda do |data|
@@ -2267,9 +2376,18 @@ module Squared
2267
2376
  ret[2] = data[:env] unless dise
2268
2377
  ret
2269
2378
  end
2270
- self.global = global
2379
+ case global
2380
+ when true
2381
+ self.global = 1
2382
+ when false
2383
+ self.global = -1
2384
+ when Numeric
2385
+ self.global = 1 | global
2386
+ end
2271
2387
  case cmd
2272
- when Array
2388
+ when Hash
2389
+ @output = parse.call(data)
2390
+ when Enumerable
2273
2391
  @output = if cmd.all? { |data| data.is_a?(Hash) }
2274
2392
  noopt = false
2275
2393
  noenv = false
@@ -2278,8 +2396,6 @@ module Squared
2278
2396
  cmd.dup
2279
2397
  end
2280
2398
  return
2281
- when Hash
2282
- @output = parse.call(data)
2283
2399
  else
2284
2400
  @output[0] = cmd
2285
2401
  end
@@ -2299,12 +2415,19 @@ module Squared
2299
2415
  end
2300
2416
  end
2301
2417
 
2302
- def script_set(cmd, prod: nil, args: nil, global: false, **)
2418
+ def script_set(cmd, prod: nil, args: nil, global: nil, **)
2303
2419
  return if @output[1] == false && @output[0].nil?
2304
2420
 
2305
- self.global = global
2421
+ case global
2422
+ when true
2423
+ self.global = 2
2424
+ when false
2425
+ self.global = -2
2426
+ when Numeric
2427
+ self.global = 2 | global
2428
+ end
2306
2429
  @output[0] = nil
2307
- @output[1] = if self.global && cmd.is_a?(Array)
2430
+ @output[1] = if global && cmd.is_a?(Array)
2308
2431
  cmd[prod == true ? 1 : 0]
2309
2432
  else
2310
2433
  cmd
@@ -2326,6 +2449,15 @@ module Squared
2326
2449
  end
2327
2450
  end
2328
2451
 
2452
+ def timeout_set(val)
2453
+ case val
2454
+ when Hash
2455
+ @timeout.update(val)
2456
+ when Numeric
2457
+ @timeout[:_][2] = val
2458
+ end
2459
+ end
2460
+
2329
2461
  def pass_set(val)
2330
2462
  @pass = Array(val).freeze
2331
2463
  end
@@ -2373,8 +2505,8 @@ module Squared
2373
2505
  end
2374
2506
  end
2375
2507
 
2376
- def as_get(val, from)
2377
- (global && @as[from][val]) || val
2508
+ def as_get(val, from, *type)
2509
+ (global(*type) && @as[from][val]) || val
2378
2510
  end
2379
2511
 
2380
2512
  def unpack_get(*)
@@ -2397,7 +2529,7 @@ module Squared
2397
2529
  next if (items = children.select { |item| item.task_include?(key) }).empty?
2398
2530
 
2399
2531
  ws.task_desc(@desc, action, 'workspace')
2400
- task task_join(action, 'workspace') => items.map! { |item| task_join(item.name, action) }
2532
+ task task_join(action, 'workspace') => items.map { |item| task_join(item.name, action) }
2401
2533
  end
2402
2534
  end
2403
2535
  end
@@ -2406,10 +2538,6 @@ module Squared
2406
2538
  @only ? !@only.include?(key) : @pass.include?(key)
2407
2539
  end
2408
2540
 
2409
- def matchany?(val, list, empty: true)
2410
- list.empty? ? empty : list.any? { |pat| val.match?(pat) }
2411
- end
2412
-
2413
2541
  def projectpath?(val)
2414
2542
  ret = Pathname.new(val).cleanpath
2415
2543
  ret.absolute? ? ret.to_s.start_with?(File.join(path, '')) : !ret.to_s.start_with?(File.join('..', ''))
@@ -2422,10 +2550,6 @@ module Squared
2422
2550
  false
2423
2551
  end
2424
2552
 
2425
- def semmajor?(cur, want)
2426
- (cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]) && !want[5]
2427
- end
2428
-
2429
2553
  def printfirst?
2430
2554
  @@print_order == 0
2431
2555
  end
@@ -2467,26 +2591,24 @@ module Squared
2467
2591
  end
2468
2592
 
2469
2593
  def success?(run, *cond)
2470
- case run
2471
- when TrueClass
2472
- true
2473
- when FalseClass
2474
- false
2475
- else
2476
- $?.success?
2477
- end.tap do |ret|
2478
- next unless cond.none? { |val| val == false }
2479
-
2594
+ ret = case run
2595
+ when true, false
2596
+ run
2597
+ else
2598
+ $?.success?
2599
+ end
2600
+ if cond.none? { |val| val == false }
2480
2601
  if block_given?
2481
2602
  yield ret
2482
2603
  elsif ret && stdout? && banner?
2483
2604
  print_success
2484
2605
  end
2485
2606
  end
2607
+ ret
2486
2608
  end
2487
2609
 
2488
2610
  def banner?
2489
- ARG[:BANNER] && !env('BANNER', equals: '0')
2611
+ ARG[:BANNER] && env('BANNER', notequals: '0')
2490
2612
  end
2491
2613
 
2492
2614
  def pwd?
@@ -2513,29 +2635,9 @@ module Squared
2513
2635
  workspace.warning
2514
2636
  end
2515
2637
 
2516
- def has_value?(target, *args)
2517
- return false unless target.is_a?(Enumerable)
2518
-
2519
- args = args.first if args.size == 1 && args.first.is_a?(Enumerable)
2520
- args.any? { |obj,| target.include?(obj) }
2521
- end
2522
-
2523
- def has_value!(target, *args, first: false)
2524
- return unless target.is_a?(Enumerable)
2525
-
2526
- args = args.first if args.size == 1 && args.first.is_a?(Enumerable)
2527
- found = false
2528
- args.each do |val|
2529
- if target.respond_to?(:delete?)
2530
- found = true if target.delete?(val)
2531
- elsif target.respond_to?(:delete)
2532
- found = true if target.delete(val)
2533
- elsif target.include?(val)
2534
- found = true
2535
- end
2536
- break if found && first
2537
- end
2538
- target if found
2638
+ def exception?(*level)
2639
+ ex = exception
2640
+ level.empty? ? ex != false && ex != Logger::INFO : ex.is_a?(Numeric) && level.include?(ex)
2539
2641
  end
2540
2642
 
2541
2643
  def variables
@@ -2555,12 +2657,13 @@ module Squared
2555
2657
  end
2556
2658
 
2557
2659
  def scriptargs
2558
- { target: script? ? @output[1] : @output[0], script: script?, ref: ref, group: group, global: global }
2660
+ n = script? ? 1 : 0
2661
+ { target: @output[n], type: n == 1 ? :script : :run, ref: ref, group: group, global: global(n.succ) }
2559
2662
  end
2560
2663
  end
2561
2664
 
2562
- Application.implement(Base, base: true)
2563
- Application.attr_banner = Set.new(%i[name project path ref group parent])
2665
+ Application.implement Base, base: 0
2666
+ Application.attr_banner.merge(%i[name project path ref group parent])
2564
2667
  end
2565
2668
  end
2566
2669
  end