squared 0.6.9 → 0.7.1

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,8 +412,8 @@ 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
 
@@ -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]
@@ -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,8 +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
- on_error(e, from, exception: kwargs.fetch(:exception, exception))
1296
+ rescue => e
1297
+ on_error(e, exception: kwargs.fetch(:exception, exception?))
1230
1298
  end
1231
1299
  on :last, from
1232
1300
  end
@@ -1240,11 +1308,8 @@ module Squared
1240
1308
  when Proc
1241
1309
  instance_eval(&obj)
1242
1310
  when Method
1243
- args = if (n = obj.arity.abs) > 0
1244
- Array.new(n).tap { |data| data[0] = self }
1245
- else
1246
- []
1247
- end
1311
+ n = obj.arity.abs
1312
+ args = n > 0 ? Array.new(n).tap { |data| data[0] = self } : []
1248
1313
  obj.call(*args)
1249
1314
  else
1250
1315
  if series?(obj)
@@ -1257,8 +1322,43 @@ module Squared
1257
1322
  end
1258
1323
  end
1259
1324
 
1260
- def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], order: nil, depth: 0,
1261
- 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 run_p(*cmd, sync: true, banner: verbose?, from: nil, **kwargs)
1343
+ case cmd.last
1344
+ when Hash, true, false
1345
+ var = cmd.pop
1346
+ end
1347
+ ws = workspace
1348
+ cmd.each do |meth|
1349
+ meth = meth.to_s
1350
+ if !meth.include?(':') && has?(meth) && respond_to?(meth)
1351
+ __send__(meth, sync: sync)
1352
+ elsif ws.task_defined?(val = task_join(name, meth)) || (meth.include?(':') && ws.task_defined?(val = meth))
1353
+ run(val, var, sync: sync, banner: banner, **kwargs)
1354
+ elsif from
1355
+ print_error(from, "method: #{meth}", subject: name, hint: 'undefined')
1356
+ end
1357
+ end
1358
+ end
1359
+
1360
+ def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], depth: 0, single: false,
1361
+ last: false, order: nil, **kwargs)
1262
1362
  tag = ->(proj) { "#{proj.name}#{"@#{proj.version}" if SEM_VER.match?(proj.version)}" }
1263
1363
  uniq = lambda do |name|
1264
1364
  return [] unless (ret = data[name])
@@ -1294,6 +1394,7 @@ module Squared
1294
1394
  "#{single ? ' ' : a}#{' ' * depth.pred}#{last ? d : c}#{b * 3}#{items.empty? ? b : e} #{f}"
1295
1395
  end
1296
1396
  end
1397
+ ctx = { sync: sync, pass: pass, done: done, order: order, single: single, depth: depth.succ, context: target }
1297
1398
  items.each_with_index do |proj, i|
1298
1399
  next if done.include?(proj)
1299
1400
 
@@ -1306,8 +1407,7 @@ module Squared
1306
1407
  end
1307
1408
  end
1308
1409
  unless target.name == name || (none = (t - done).empty?)
1309
- graph_branch(proj, data, tasks, out, sync: sync, pass: pass, done: done, order: order, depth: depth.succ,
1310
- single: single, last: j == true, context: target)
1410
+ graph_branch(proj, data, tasks, out, last: j == true, **ctx)
1311
1411
  end
1312
1412
  if out
1313
1413
  if none
@@ -1320,7 +1420,7 @@ module Squared
1320
1420
  final = data.keys.last
1321
1421
  while k < depth
1322
1422
  indent = k > 0 ? ((last && !j) || (j && k == depth.pred) || single) : j && last && depth == 1
1323
- s += "#{indent || (last && data[final].last == context) ? ' ' : a} "
1423
+ s += "#{indent || (last && data[final].last == kwargs[:context]) ? ' ' : a} "
1324
1424
  k += 1
1325
1425
  end
1326
1426
  s += "#{j ? d : c}#{b * 3} #{tag.call(proj)}"
@@ -1346,15 +1446,15 @@ module Squared
1346
1446
  next if pass.include?(meth)
1347
1447
 
1348
1448
  if workspace.task_defined?(cmd = task_join(name, meth))
1349
- if ENV.key?(key = "BANNER_#{name.upcase}")
1449
+ if ENV.key?(key = "BANNER_#{envname_get}")
1350
1450
  key = nil
1351
1451
  else
1352
1452
  ENV[key] = '0'
1353
1453
  end
1354
1454
  run(cmd, sync: false, banner: false)
1355
1455
  ENV.delete(key) if key
1356
- elsif proj.has?(meth, (workspace.baseref unless tasks || graph))
1357
- proj.__send__(meth.to_sym, sync: sync)
1456
+ elsif proj.has?(meth, tasks || graph ? nil : workspace.baseref)
1457
+ proj.__send__(meth, sync: sync)
1358
1458
  end
1359
1459
  end
1360
1460
  end
@@ -1364,8 +1464,7 @@ module Squared
1364
1464
  end
1365
1465
 
1366
1466
  def graph_collect(target, start = [], data: {}, pass: [], root: [])
1367
- deps = []
1368
- (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
1467
+ deps = Array(start.empty? ? target.graph_get : start).each_with_object([]) do |val, out|
1369
1468
  next if pass.include?(val)
1370
1469
 
1371
1470
  if (obj = workspace.find(name: val))
@@ -1380,8 +1479,8 @@ module Squared
1380
1479
  end
1381
1480
  next if (objs = data.fetch(name, [])).include?(target)
1382
1481
 
1383
- deps << proj
1384
- deps.concat(objs)
1482
+ out << proj
1483
+ out.concat(objs)
1385
1484
  end
1386
1485
  end
1387
1486
  deps.uniq!
@@ -1392,22 +1491,22 @@ module Squared
1392
1491
 
1393
1492
  def graph_deps(target = self)
1394
1493
  key = target.name
1395
- return @@graph[key] if @@graph.key?(key)
1396
-
1397
- base = []
1398
- deps = []
1399
- loop do
1400
- deps.concat(graph_branch(target, graph_collect(target), []))
1401
- break unless (target = target.parent)
1494
+ @@graph[key] ||= begin
1495
+ base = []
1496
+ deps = []
1497
+ loop do
1498
+ deps.concat(graph_branch(target, graph_collect(target), []))
1499
+ break unless (target = target.parent)
1402
1500
 
1403
- base << target
1501
+ base << target
1502
+ end
1503
+ deps.uniq!
1504
+ [base, deps]
1404
1505
  end
1405
- deps.uniq!
1406
- @@graph[key] = [base, deps]
1407
1506
  end
1408
1507
 
1409
- def env(key, default = nil, suffix: nil, equals: nil, ignore: nil, strict: false)
1410
- name = "#{key}_#{@envname}"
1508
+ def env(key, default = nil, suffix: nil, strict: false, ignore: nil, **kwargs, &blk)
1509
+ name = "#{key}_#{envname_get}"
1411
1510
  ret = if suffix
1412
1511
  ENV.fetch("#{name}_#{suffix}", '')
1413
1512
  elsif strict
@@ -1416,15 +1515,7 @@ module Squared
1416
1515
  ignore = ['0'].freeze if ignore.nil?
1417
1516
  ENV[name] || ENV.fetch(key, '')
1418
1517
  end
1419
- if equals.nil?
1420
- ret = default if ret.empty? || (ignore && Array(ignore).any? { |val| ret == val.to_s })
1421
- return ret if ret.nil?
1422
- else
1423
- ret = Array(equals).any? { |val| ret == val.to_s }
1424
- end
1425
- return yield ret if block_given?
1426
-
1427
- ret
1518
+ env_yield(ret, default, ignore: ignore, **kwargs, &blk)
1428
1519
  end
1429
1520
 
1430
1521
  def session(*cmd, prefix: cmd.first, main: true, path: true, options: true)
@@ -1432,7 +1523,7 @@ module Squared
1432
1523
  if path && (val = shell_bin(prefix))
1433
1524
  cmd[0] = shell_quote(val, force: false)
1434
1525
  end
1435
- ret = JoinSet.new(cmd.flatten(1))
1526
+ ret = JoinSet.new(cmd)
1436
1527
  if options
1437
1528
  env("#{prefix.upcase}_OPTIONS") do |val|
1438
1529
  if val.start_with?('-')
@@ -1442,9 +1533,17 @@ module Squared
1442
1533
  end
1443
1534
  end
1444
1535
  end
1445
- return ret unless main
1536
+ cache = @timeout[:_]
1537
+ cache[1] ||= workspace.timeout_get(*@ref, group: group)
1538
+ cache[0][ret] = if cmd[1..-1].find { |val| val =~ /\A([A-Za-z][\w-]*)(?<![-_])(?: |\z)/ }
1539
+ command = :"#{prefix}_#{$1}"
1540
+ cache[1][command] || @timeout[command]
1541
+ end || cache[1][prefix.to_sym] || @timeout[prefix.to_sym]
1542
+ main ? @session = ret : ret
1543
+ end
1446
1544
 
1447
- @session = ret
1545
+ def session_timeout(cmd)
1546
+ @timeout[:_][0][cmd] || @timeout[:_][2]
1448
1547
  end
1449
1548
 
1450
1549
  def session_output(*cmd, **kwargs)
@@ -1468,18 +1567,18 @@ module Squared
1468
1567
  end
1469
1568
 
1470
1569
  def session_apply(val, args: nil, kwargs: nil, pass: [], keys: [:opts], exclude: [])
1471
- a = []
1472
1570
  b = {}
1473
- Array(val).each do |c|
1571
+ a = Array(val).each_with_object([]) do |c, out|
1474
1572
  d, e, f = session_get c
1475
- unless (f -= exclude).empty?
1573
+ f -= exclude
1574
+ unless f.empty?
1476
1575
  h = []
1477
1576
  i = {}
1478
- session_apply(f.map!(&:to_s), args: h, kwargs: i, pass: pass, keys: keys, exclude: exclude.concat(f))
1479
- a.concat(h)
1577
+ session_apply(f.map(&:to_s), args: h, kwargs: i, pass: pass, keys: keys, exclude: exclude.concat(f))
1578
+ out.concat(h)
1480
1579
  append_keys(b, i, *keys)
1481
1580
  end
1482
- a.concat(d)
1581
+ out.concat(d)
1483
1582
  append_keys(b, e, *keys)
1484
1583
  end
1485
1584
  if args
@@ -1512,23 +1611,48 @@ module Squared
1512
1611
  OptionPartition.arg?(target, *args, **kwargs)
1513
1612
  end
1514
1613
 
1515
- def option(*args, target: @session, prefix: target&.first, **kwargs)
1614
+ def option(*args, target: @session, prefix: target&.first, path: false, escape: false, quote: false, pat: nil,
1615
+ **kwargs)
1516
1616
  return unless prefix
1517
1617
 
1518
- args.each do |val|
1519
- next unless (ret = env(env_key(prefix.to_s.stripext, val), **kwargs))
1618
+ args.each do |key|
1619
+ next unless (ret = env(env_key(prefix.to_s.stripext, key), **kwargs)) && (!pat || ret.match?(pat))
1520
1620
 
1621
+ if path
1622
+ target << quote_option(key, basepath(ret))
1623
+ elsif quote
1624
+ target << quote_option(key, ret)
1625
+ elsif escape
1626
+ target << shell_option(key, ret)
1627
+ end
1521
1628
  return block_given? ? yield(ret) : ret
1522
1629
  end
1523
1630
  nil
1524
1631
  end
1525
1632
 
1526
- def option_clear(opts, empty = true, target: @session, **kwargs)
1527
- return unless target
1633
+ def write_lines(data, grep: [], prefix: nil, sub: nil, banner: nil, loglevel: nil, pass: false, first: false)
1634
+ grep = grep.empty? ? nil : matchmap(grep, prefix)
1635
+ sub = stdin? ? nil : as_a(sub)
1636
+ ret = 0
1637
+ lines = data.each_with_object([]) do |line, out|
1638
+ next if grep&.none? { |pat| pat.match?(line) }
1639
+ next if block_given? && !(line = yield(line, ret))
1528
1640
 
1529
- OptionPartition.clear(target, opts, styles: theme[:inline], **kwargs)
1530
- opts.clear if empty
1531
- nil
1641
+ if loglevel
1642
+ log&.add loglevel, line
1643
+ else
1644
+ sub&.each { |h| sub_style!(line, **h) }
1645
+ if banner
1646
+ out << line
1647
+ else
1648
+ puts line
1649
+ end
1650
+ end
1651
+ ret += 1
1652
+ break out if first
1653
+ end
1654
+ print_item banner, lines if banner && (ret > 0 || (!pass && !first))
1655
+ ret
1532
1656
  end
1533
1657
 
1534
1658
  def print_success
@@ -1538,7 +1662,18 @@ module Squared
1538
1662
  def print_error(*args, loglevel: Logger::WARN, **kwargs)
1539
1663
  return unless warning?
1540
1664
 
1541
- warn log_message(loglevel, *args, **kwargs)
1665
+ loglevel = args.shift if args.first.is_a?(Numeric) && args.first.between?(Logger::DEBUG, Logger::UNKNOWN)
1666
+ if loglevel >= Logger::WARN && exception?(Logger::ERROR, Logger::FATAL)
1667
+ $stderr.puts log_message([loglevel, Logger::Error].max, *args, **kwargs)
1668
+ if exception == Logger::ERROR
1669
+ ex = [args.first, kwargs[:subject], kwargs[:hint]].compact
1670
+ raise(ex.find { |val| val.class < Exception } || ex.first)
1671
+ else
1672
+ exit 1
1673
+ end
1674
+ else
1675
+ warn log_message(loglevel, *args, **kwargs)
1676
+ end
1542
1677
  end
1543
1678
 
1544
1679
  def print_run(cmd, banner = true, verbose: nil, **)
@@ -1554,19 +1689,19 @@ module Squared
1554
1689
  puts val unless val.empty? || (val.size == 1 && !val.first)
1555
1690
  end
1556
1691
 
1557
- def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle, **)
1692
+ def print_banner(*lines, client: false, styles: theme[:banner], border: borderstyle)
1558
1693
  pad = 0
1559
1694
  if styles
1560
1695
  if styles.any? { |s| s.to_s.end_with?('!') }
1561
1696
  pad = 1
1562
1697
  elsif !client && styles.size <= 1
1563
- styles = [:bold] + styles
1698
+ styles += [:bold]
1564
1699
  end
1565
1700
  end
1566
1701
  n = line_width lines
1567
1702
  ch = ' ' * pad
1568
1703
  index = -1
1569
- lines.map! do |val|
1704
+ ret = lines.map do |val|
1570
1705
  index += 1
1571
1706
  val = ch + val.ljust(n - (pad * 2)) + ch
1572
1707
  if styles && (pad == 1 || index == 0)
@@ -1575,23 +1710,24 @@ module Squared
1575
1710
  val
1576
1711
  end
1577
1712
  end
1578
- (lines << sub_style(ARG[:BORDER][1] * n, border)).join("\n")
1713
+ ret << sub_style(ARG[:BORDER][1] * n, border)
1714
+ ret.join("\n")
1579
1715
  end
1580
1716
 
1581
- def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle, **)
1717
+ def print_footer(*lines, sub: nil, reverse: false, right: false, border: borderstyle)
1582
1718
  n = line_width lines
1583
1719
  sub = as_a sub
1584
- lines.map! do |val|
1720
+ ret = lines.map do |val|
1585
1721
  s = right ? val.rjust(n) : val.ljust(n)
1586
1722
  sub.each { |h| sub_style!(s, **h) }
1587
1723
  s
1588
1724
  end
1589
- [sub_style(ARG[:BORDER][1] * n, border)].concat(lines)
1590
- .tap { |ret| ret.reverse! if reverse }
1591
- .join("\n")
1725
+ ret.unshift(sub_style(ARG[:BORDER][1] * n, border))
1726
+ ret.reverse! if reverse
1727
+ ret.join("\n")
1592
1728
  end
1593
1729
 
1594
- def print_status(*args, from: nil, **kwargs)
1730
+ def print_status(*args, loglevel: Logger::INFO, from: :completed, **kwargs)
1595
1731
  return if stdin? || silent?
1596
1732
 
1597
1733
  case from
@@ -1600,10 +1736,10 @@ module Squared
1600
1736
  sub_style!(out[1], **opt_style(theme[:major], /^( +major )(\d+)(.+)$/, 2))
1601
1737
  sub_style!(out[1], **opt_style(theme[:active], /^(.+)(minor )(\d+)(.+)$/, 3))
1602
1738
  sub_style!(out[1], **opt_style(theme[:current], /^(.+)(patch )(\d+)(.+)$/, 3)) if theme[:current]
1603
- when :completed
1739
+ when :completed, :failed
1604
1740
  return unless kwargs[:start]
1605
1741
 
1606
- out = log_message(Logger::INFO, *args, sub_style('completed', theme[:active]),
1742
+ out = log_message(loglevel, *args, sub_style(from.to_s, theme[:active]),
1607
1743
  subject: kwargs[:subject], hint: time_format(time_epoch - kwargs[:start]))
1608
1744
  else
1609
1745
  return
@@ -1617,7 +1753,7 @@ module Squared
1617
1753
  workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
1618
1754
  end
1619
1755
 
1620
- def format_banner(cmd, banner: true, hint: nil, strip: nil)
1756
+ def format_banner(cmd, banner: true, hint: nil, strip: nil, quote: false)
1621
1757
  return unless banner && banner?
1622
1758
 
1623
1759
  if (data = workspace.banner_get(*@ref, group: group))
@@ -1625,7 +1761,7 @@ module Squared
1625
1761
 
1626
1762
  client = true
1627
1763
  else
1628
- data = Struct::BannerData.new(true, [:path], theme[:banner], theme[:border])
1764
+ data = Struct::BannerData.new(true, theme[:banner], theme[:border], [:path])
1629
1765
  end
1630
1766
  if verbose
1631
1767
  out = []
@@ -1638,6 +1774,7 @@ module Squared
1638
1774
  cmd = cmd.gsub(/(?:#{s = Regexp.escape(File.join(path, ''))}?(?=["'])|#{s})/, '')
1639
1775
  .gsub(/(?: -[^ ])? (?:""|'')/, '')
1640
1776
  end
1777
+ cmd.gsub!(/(?<= )(["'])([\w.-]+)\1(?= |\z)/, '\2') unless quote
1641
1778
  out << cmd.subhint(hint)
1642
1779
  end
1643
1780
  data.order.each do |val|
@@ -1672,7 +1809,7 @@ module Squared
1672
1809
  unless items.empty?
1673
1810
  pad = items.size.to_s.size
1674
1811
  items.each_with_index do |val, i|
1675
- next unless matchany?(val.first, reg)
1812
+ next unless matchany?(reg, val.first)
1676
1813
 
1677
1814
  out << ('%*d. %s' % [pad, i.succ, block_given? ? yield(val) : val.first])
1678
1815
  end
@@ -1686,7 +1823,7 @@ module Squared
1686
1823
  out << ''
1687
1824
  end
1688
1825
  if from
1689
- out << (from = from.to_s)
1826
+ out << from
1690
1827
  /\A(#{Regexp.escape(from)})(.*)\z/
1691
1828
  end
1692
1829
  else
@@ -1719,7 +1856,8 @@ module Squared
1719
1856
  end
1720
1857
  end
1721
1858
  data.each do |key, val|
1722
- next if (key = key.to_s).start_with?('__')
1859
+ key = key.to_s
1860
+ next if key.start_with?('__')
1723
1861
 
1724
1862
  if val.nil? || extra || session_arg?(key, target: target)
1725
1863
  OptionPartition.delete_key(target, key)
@@ -1732,7 +1870,7 @@ module Squared
1732
1870
  append_repeat(key, val, target: target)
1733
1871
  when Numeric
1734
1872
  target << basic_option(key, val)
1735
- when FalseClass
1873
+ when false
1736
1874
  target << shell_option(key).sub(/^--(?!no-)/, '--no-')
1737
1875
  when Pathname
1738
1876
  target << shell_option(key, val, escape: false)
@@ -1769,9 +1907,7 @@ module Squared
1769
1907
 
1770
1908
  def append_first(*list, target: @session, flag: true, equals: false, escape: true, quote: true, force: true,
1771
1909
  **kwargs)
1772
- return if list.empty?
1773
-
1774
- list.flatten.each do |opt|
1910
+ list.each do |opt|
1775
1911
  next unless (val = option(opt, **kwargs))
1776
1912
 
1777
1913
  return target << if flag
@@ -1785,30 +1921,25 @@ module Squared
1785
1921
 
1786
1922
  def append_option(*list, target: @session, no: false, equals: false, escape: true, quote: true, force: true,
1787
1923
  **kwargs)
1788
- return if list.empty?
1789
-
1790
1924
  kwargs[:ignore] = false if no && !kwargs.key?(:ignore)
1791
- ret = []
1792
- list.flatten.each do |flag|
1925
+ ret = list.each_with_object([]) do |flag, out|
1793
1926
  next unless (val = option(flag, target: target, **kwargs))
1794
1927
 
1795
1928
  if no && val == '0'
1796
1929
  flag = "no-#{flag}"
1797
1930
  val = nil
1798
1931
  end
1799
- ret << shell_option(flag, (val if equals), escape: escape, quote: quote, force: force)
1932
+ out << shell_option(flag, (val if equals), escape: escape, quote: quote, force: force)
1800
1933
  end
1801
1934
  merge_list target, ret unless ret.empty?
1802
- ret
1803
1935
  end
1804
1936
 
1805
- def append_nocolor(target: @session)
1806
- target << '--no-color' if !ARG[:COLOR] || stdin? || option('color', target: target, equals: '0')
1937
+ def append_nocolor(flag = '--no-color', target: @session, **kwargs)
1938
+ target << flag if !ARG[:COLOR] || stdin? || option('color', target: target, equals: '0', **kwargs)
1807
1939
  end
1808
1940
 
1809
1941
  def append_keys(base, data, *keys)
1810
- out = {}
1811
- keys.each do |key|
1942
+ ret = keys.each_with_object({}) do |key, out|
1812
1943
  next unless data.key?(key)
1813
1944
 
1814
1945
  out[key] = case (val = data[key])
@@ -1821,7 +1952,7 @@ module Squared
1821
1952
  end
1822
1953
  end
1823
1954
  base.update(data)
1824
- .update(out)
1955
+ .update(ret)
1825
1956
  end
1826
1957
 
1827
1958
  def merge_opts(base, data)
@@ -1864,22 +1995,9 @@ module Squared
1864
1995
  end
1865
1996
  end
1866
1997
 
1867
- def merge_list(base, data)
1868
- data = Array(data)
1869
- case base
1870
- when Array
1871
- base.concat(data)
1872
- when Set
1873
- base.merge(data)
1874
- else
1875
- Array(base).concat(data)
1876
- end
1877
- end
1878
-
1879
1998
  def collect_hash(data, pass: [])
1880
- ret = []
1881
- data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1882
- ret
1999
+ data = data.reject { |key,| pass.include?(key) } unless pass.empty?
2000
+ data.values.flatten
1883
2001
  end
1884
2002
 
1885
2003
  def replace_bin(val)
@@ -1893,14 +2011,13 @@ module Squared
1893
2011
  ret = JSON.parse(val)
1894
2012
  raise_error 'invalid JSON'.subhint(kind.name), val, hint: hint if kind && !ret.is_a?(kind)
1895
2013
  ret
1896
- rescue StandardError => e
1897
- log&.warn e
1898
- print_error(e, subject: name)
2014
+ rescue => e
2015
+ print_error(kind ? Logger::ERROR : Logger::INFO, e, subject: name)
1899
2016
  end
1900
2017
 
1901
2018
  def parse_env(key)
1902
2019
  env(key) do |val|
1903
- val.start_with?('-') ? shell_parse(val) : split_escape(val).map! { |opt| fill_option(opt) }
2020
+ val.start_with?('-') ? shell_parse(val) : split_escape(val).map { |opt| fill_option(opt) }
1904
2021
  end || []
1905
2022
  end
1906
2023
 
@@ -1918,69 +2035,52 @@ module Squared
1918
2035
  args
1919
2036
  end
1920
2037
 
1921
- def confirm_basic(msg, target, default = 'Y', style: :inline, **kwargs)
1922
- confirm("#{msg} [#{sub_style(target.to_s, style.is_a?(Symbol) ? theme[style] : style)}]", default, **kwargs)
1923
- end
2038
+ def confirm_basic(msg, hint, default = 'Y', style: :inline, target: @session, prefix: nil, **kwargs)
2039
+ return true if prefix ? option('y', prefix: prefix) : target && option('y', target: target)
1924
2040
 
1925
- def confirm_outdated(pkg, ver, type, cur = nil, lock: false, col0: 0, col1: 0, col2: nil, col3: 0, col4: 0,
1926
- **kwargs)
1927
- h = sub_style(semrev(type).upcase, (type == 1 && theme[:major]) || theme[:header])
1928
- case col0
1929
- when 0
1930
- col0 = "#{h}: "
1931
- when Numeric
1932
- puts h
1933
- col0 = ' ' * col0
1934
- else
1935
- puts h
1936
- end
1937
- b = sub_style pkg.ljust(col1), theme[:inline]
1938
- cur ||= 'locked' if lock
1939
- c = if cur
1940
- cur = cur.ljust(col2 || cur.size.succ)
1941
- lock ? sub_style(cur, color(:red)) : cur
1942
- end
1943
- d = type == 1 || lock ? 'N' : 'Y'
1944
- e = "#{col0}#{b}#{c}#{sub_style(col1 > 0 ? ver.ljust(col3) : ver.rjust(ver.size.succ), theme[:inline])}"
1945
- confirm("#{e}#{col4 > 0 ? ' ' * [col4 - e.stripstyle.size - 1, 2].max : ' '}", d, **kwargs)
2041
+ confirm("#{msg} [#{sub_style(hint.to_s, style.is_a?(Symbol) ? theme[style] : style)}]", default, **kwargs)
1946
2042
  end
1947
2043
 
1948
2044
  def confirm_semver(msg, type, style: (type == 1 && theme[:major]) || :inline, timeout: 0, **kwargs)
1949
- confirm_basic(msg, semrev(type), type == 1 ? 'N' : 'Y', style: style, timeout: timeout, **kwargs)
2045
+ rev = case type
2046
+ when 1
2047
+ 'major'
2048
+ when 2
2049
+ 'minor'
2050
+ else
2051
+ 'patch'
2052
+ end
2053
+ confirm_basic(msg, rev, type == 1 ? 'N' : 'Y', style: style, timeout: timeout, **kwargs)
1950
2054
  end
1951
2055
 
1952
2056
  def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil, multiple: false,
1953
2057
  force: true, **kwargs)
1954
2058
  puts unless series || printfirst?
1955
- ret = choice(msg, list, multiple: multiple, force: force, **kwargs).tap do |val|
1956
- next unless !val || val.empty?
1957
-
2059
+ ret = choice(msg, list, multiple: multiple, force: force, **kwargs)
2060
+ unless ret && !ret.empty?
1958
2061
  exit 1 if force
1959
- return nil
2062
+ return
1960
2063
  end
1961
- ret = multiple ? ret.map! { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
2064
+ ret = multiple ? ret.map { |val| val.sub(trim, '') } : ret.sub(trim, '') if trim
1962
2065
  if column
1963
2066
  a, b = Array(column)
1964
- ret = Array(ret).map! { |val| val[a, b || 1] }
2067
+ ret = Array(ret).map { |val| val[a, b || 1] }
1965
2068
  ret = ret.first unless multiple
1966
2069
  end
1967
2070
  if accept
1968
2071
  hint = Array(ret).map { |val| sub_style(val.to_s, theme[:inline]) }.join(', ')
1969
2072
  accept = Array(accept).map { |val| Array(val) }
1970
2073
  ret = Array(ret) if accept.any? { |val| val[1] == true }
1971
- loop do
1972
- item = accept.first
2074
+ begin
2075
+ item = accept.shift
1973
2076
  c = confirm("#{item[0]}#{" [#{hint}]" if hint}", item[2] ? 'Y' : 'N')
1974
2077
  if item[1] == true
1975
2078
  ret << c
1976
2079
  elsif !c
1977
- break
2080
+ exit 1
1978
2081
  end
1979
2082
  hint = nil
1980
- accept.shift
1981
- break if accept.empty?
1982
- end
1983
- exit 1 unless accept.empty?
2083
+ end until accept.empty?
1984
2084
  end
1985
2085
  if values
1986
2086
  ret = Array(ret)
@@ -1991,7 +2091,7 @@ module Squared
1991
2091
  force = false
1992
2092
  end
1993
2093
  val = readline(val, force: force)
1994
- ret << (val unless val.empty?)
2094
+ ret << (val.empty? ? nil : val)
1995
2095
  end
1996
2096
  end
1997
2097
  printsucc unless series
@@ -2009,10 +2109,11 @@ module Squared
2009
2109
  def command_args(args, min: 0, force: false, **kwargs)
2010
2110
  return if args.size > min || option('i', 'interactive', **kwargs, equals: '0')
2011
2111
 
2012
- readline('Enter arguments', force: force)
2112
+ ret = readline('Enter arguments', force: force)
2113
+ args << ret unless ret.empty?
2013
2114
  end
2014
2115
 
2015
- def block_args(fallback = nil, &blk)
2116
+ def block_args(fallback = [], &blk)
2016
2117
  return fallback if (ret = instance_eval(&blk)).nil?
2017
2118
 
2018
2119
  Array(ret)
@@ -2031,33 +2132,40 @@ module Squared
2031
2132
  if workspace.powershell?
2032
2133
  "#{shell_bin('powershell.exe')} -Command \"& {#{args.join(' ; ')}}\"#{out}"
2033
2134
  else
2034
- args.map! { |val| "#{val}#{out}" }.join(' && ')
2135
+ args.map { |val| "#{val}#{out}" }.join(' && ')
2035
2136
  end
2036
2137
  end
2037
2138
 
2038
2139
  def relativepath(*list, all: false)
2039
- list.flatten.map! { |val| Pathname.new(val) }.select { |val| projectpath?(val) }.map! do |val|
2040
- ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
2041
- all && val.to_s.end_with?('/') ? "#{ret}/*" : ret
2042
- end
2140
+ list.map { |val| Pathname.new(val) }
2141
+ .select { |val| projectpath?(val) }
2142
+ .map do |val|
2143
+ ret = (val.absolute? ? val.relative_path_from(path) : val.cleanpath).to_s
2144
+ all && val.to_s.end_with?('/') ? "#{ret}/*" : ret
2145
+ end
2043
2146
  end
2044
2147
 
2045
- def projectmap(files, parent: false, pass: true)
2148
+ def projectmap(files, resolve: true, parent: false, pass: true)
2046
2149
  unless parent
2047
- proj = files.select { |val| projectpath?(val) }
2048
- raise_error 'pathspec not within worktree' unless pass || files.size == proj.size
2049
- files = proj
2150
+ files = files.select { |val| projectpath?(val) }.tap do |list|
2151
+ next if pass || files.size == list.size
2152
+
2153
+ raise_error 'pathspec not within worktree'
2154
+ end
2155
+ end
2156
+ files.map do |val|
2157
+ if val == '.'
2158
+ '.'
2159
+ else
2160
+ val = basepath(val)
2161
+ val = val.relative_path_from(path) unless resolve
2162
+ shell_quote val
2163
+ end
2050
2164
  end
2051
- files.map { |val| val == '.' ? '.' : shell_quote(basepath(val)) }
2052
2165
  end
2053
2166
 
2054
- def matchmap(list, prefix = nil)
2055
- list.map do |val|
2056
- next val if val.is_a?(Regexp)
2057
-
2058
- val = ".*#{val}" if prefix && !val.sub!(/\A(\^|\\A)/, '')
2059
- Regexp.new("#{prefix}#{val == '*' ? '.+' : val}")
2060
- end
2167
+ def symjoin(*args, char: ':')
2168
+ args.join(char).to_sym
2061
2169
  end
2062
2170
 
2063
2171
  def semver(val)
@@ -2082,10 +2190,10 @@ module Squared
2082
2190
  return -1 if (b = other.scan(SEM_VER)).empty?
2083
2191
  return 1 if (a = val.scan(SEM_VER)).empty?
2084
2192
 
2085
- a, b = [a.first, b.first].map! do |c|
2193
+ a, b = [a.first, b.first].map do |c|
2086
2194
  d = begin
2087
2195
  Integer(c[5]).to_s
2088
- rescue StandardError
2196
+ rescue
2089
2197
  c[5] ? '-1' : '0'
2090
2198
  end
2091
2199
  [c[0], c[2], c[4] || '0', d]
@@ -2123,24 +2231,13 @@ module Squared
2123
2231
  end
2124
2232
 
2125
2233
  def semtype(cur, lat)
2126
- if semmajor?(cur, lat)
2127
- 1
2128
- else
2129
- cur[2] == lat[2] ? 3 : 2
2130
- end
2234
+ return 1 if semmajor?(cur, lat)
2235
+
2236
+ cur[2] == lat[2] ? 3 : 2
2131
2237
  end
2132
2238
 
2133
- def semrev(type)
2134
- case type
2135
- when 1
2136
- 'major'
2137
- when 2
2138
- 'minor'
2139
- when 3
2140
- 'patch'
2141
- else
2142
- 'unknown'
2143
- end
2239
+ def semmajor?(cur, want)
2240
+ (cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]) && !want[5]
2144
2241
  end
2145
2242
 
2146
2243
  def semgte?(val, other = nil)
@@ -2166,7 +2263,7 @@ module Squared
2166
2263
  def shortname(*args, suffix: '?', delim: ',', pass: false)
2167
2264
  return unless TASK_METADATA || pass
2168
2265
 
2169
- args.map! do |ch|
2266
+ args.map do |ch|
2170
2267
  "#{ch}/#{case ch
2171
2268
  when 'i'
2172
2269
  'nteractive'
@@ -2192,7 +2289,7 @@ module Squared
2192
2289
  end
2193
2290
 
2194
2291
  def color(val)
2195
- (ret = theme[val]) && !ret.empty? ? ret : [val]
2292
+ (ret = Array(theme[val])).empty? ? [val] : ret
2196
2293
  end
2197
2294
 
2198
2295
  def colormap(val)
@@ -2201,7 +2298,7 @@ module Squared
2201
2298
 
2202
2299
  def verbosetype
2203
2300
  case verbose
2204
- when TrueClass
2301
+ when true
2205
2302
  1
2206
2303
  when Numeric
2207
2304
  verbose.succ
@@ -2211,9 +2308,15 @@ module Squared
2211
2308
  end
2212
2309
 
2213
2310
  def on(event, from, *args, **kwargs)
2214
- return unless from && @events.key?(event)
2311
+ return unless from && @on[:event].key?(event)
2215
2312
 
2216
- Array(@events[event][from]).each do |obj|
2313
+ case event
2314
+ when :first
2315
+ @on[:from] << from
2316
+ when :last
2317
+ return unless @on[:from].delete(from)
2318
+ end
2319
+ Array(@on[:event][event][from]).each do |obj|
2217
2320
  target, opts = if obj.is_a?(Array) && obj[1].is_a?(Hash)
2218
2321
  [obj[0], kwargs.empty? ? obj[1] : obj[1].merge(kwargs)]
2219
2322
  else
@@ -2230,28 +2333,51 @@ module Squared
2230
2333
  end
2231
2334
  end
2232
2335
 
2233
- def on_error(err, from, pass: false, exception: self.exception, dryrun: false)
2336
+ def on_error(err, from = @on[:from].last, exception: exception?, fatal: true, dryrun: false, pass: false)
2234
2337
  log&.error err
2235
- unless dryrun
2236
- ret = on :error, from, err
2237
- raise err if exception && ret != true
2338
+ unless from == false
2339
+ cancel = -> { @on[:from] = [false] if fatal && @on[:from].include?(from) }
2340
+ if dryrun
2341
+ cancel.call
2342
+ else
2343
+ ret = on :error, from, err
2344
+ unless ret == true
2345
+ cancel.call
2346
+ raise err if exception
2347
+ end
2348
+ end
2238
2349
  end
2239
2350
  print_error(err, pass: pass) unless ret
2240
2351
  end
2241
2352
 
2242
- def pwd_set(pass: false, exception: self.exception, dryrun: false, from: nil)
2243
- return yield if (path.to_s == Dir.pwd || pass == true) && (workspace.mri? || !workspace.windows?)
2353
+ def pwd_set(cmd = nil, pass: false, timeout: nil, from: @on[:from].last, **kwargs, &blk)
2354
+ return if from == false
2244
2355
 
2245
- pwd = Dir.pwd
2246
- Dir.chdir(path)
2247
- yield
2248
- rescue StandardError => e
2249
- on_error(e, from, exception: exception, dryrun: dryrun)
2356
+ require 'timeout'
2357
+ unless (path.to_s == Dir.pwd || pass == true) && (workspace.mri? || !workspace.windows?)
2358
+ pwd = Dir.pwd
2359
+ Dir.chdir(path)
2360
+ end
2361
+ timeout = session_timeout cmd if cmd && timeout.nil?
2362
+ if timeout.to_f > 0
2363
+ Timeout.timeout(timeout, &blk)
2364
+ else
2365
+ yield
2366
+ end
2367
+ rescue Timeout::Error => e
2368
+ if cmd
2369
+ print_error(Logger::ERROR, cmd, subject: name, hint: e)
2370
+ else
2371
+ print_error(Logger::ERROR, e, subject: name)
2372
+ end
2373
+ exit 1 unless exception?(Logger::DEBUG, Logger::INFO)
2374
+ rescue => e
2375
+ on_error(e, from, **kwargs)
2250
2376
  ensure
2251
2377
  Dir.chdir(pwd) if pwd
2252
2378
  end
2253
2379
 
2254
- def run_set(cmd, val = nil, opts: nil, global: false, **)
2380
+ def run_set(cmd, val = nil, opts: nil, global: nil, **)
2255
2381
  noopt = @output[1] == false && !@output[0].nil?
2256
2382
  noenv = @output[2] == false
2257
2383
  parse = lambda do |data|
@@ -2270,7 +2396,14 @@ module Squared
2270
2396
  ret[2] = data[:env] unless dise
2271
2397
  ret
2272
2398
  end
2273
- self.global = global
2399
+ case global
2400
+ when true
2401
+ self.global = 1
2402
+ when false
2403
+ self.global = -1
2404
+ when Numeric
2405
+ self.global = 1 | global
2406
+ end
2274
2407
  case cmd
2275
2408
  when Hash
2276
2409
  @output = parse.call(data)
@@ -2302,12 +2435,19 @@ module Squared
2302
2435
  end
2303
2436
  end
2304
2437
 
2305
- def script_set(cmd, prod: nil, args: nil, global: false, **)
2438
+ def script_set(cmd, prod: nil, args: nil, global: nil, **)
2306
2439
  return if @output[1] == false && @output[0].nil?
2307
2440
 
2308
- self.global = global
2441
+ case global
2442
+ when true
2443
+ self.global = 2
2444
+ when false
2445
+ self.global = -2
2446
+ when Numeric
2447
+ self.global = 2 | global
2448
+ end
2309
2449
  @output[0] = nil
2310
- @output[1] = if self.global && cmd.is_a?(Array)
2450
+ @output[1] = if global && cmd.is_a?(Array)
2311
2451
  cmd[prod == true ? 1 : 0]
2312
2452
  else
2313
2453
  cmd
@@ -2329,6 +2469,15 @@ module Squared
2329
2469
  end
2330
2470
  end
2331
2471
 
2472
+ def timeout_set(val)
2473
+ case val
2474
+ when Hash
2475
+ @timeout.update(val)
2476
+ when Numeric
2477
+ @timeout[:_][2] = val
2478
+ end
2479
+ end
2480
+
2332
2481
  def pass_set(val)
2333
2482
  @pass = Array(val).freeze
2334
2483
  end
@@ -2376,8 +2525,8 @@ module Squared
2376
2525
  end
2377
2526
  end
2378
2527
 
2379
- def as_get(val, from)
2380
- (global && @as[from][val]) || val
2528
+ def as_get(val, from, *type)
2529
+ (global(*type) && @as[from][val]) || val
2381
2530
  end
2382
2531
 
2383
2532
  def unpack_get(*)
@@ -2400,7 +2549,7 @@ module Squared
2400
2549
  next if (items = children.select { |item| item.task_include?(key) }).empty?
2401
2550
 
2402
2551
  ws.task_desc(@desc, action, 'workspace')
2403
- task task_join(action, 'workspace') => items.map! { |item| task_join(item.name, action) }
2552
+ task task_join(action, 'workspace') => items.map { |item| task_join(item.name, action) }
2404
2553
  end
2405
2554
  end
2406
2555
  end
@@ -2409,10 +2558,6 @@ module Squared
2409
2558
  @only ? !@only.include?(key) : @pass.include?(key)
2410
2559
  end
2411
2560
 
2412
- def matchany?(val, list, empty: true)
2413
- list.empty? ? empty : list.any? { |pat| val.match?(pat) }
2414
- end
2415
-
2416
2561
  def projectpath?(val)
2417
2562
  ret = Pathname.new(val).cleanpath
2418
2563
  ret.absolute? ? ret.to_s.start_with?(File.join(path, '')) : !ret.to_s.start_with?(File.join('..', ''))
@@ -2425,10 +2570,6 @@ module Squared
2425
2570
  false
2426
2571
  end
2427
2572
 
2428
- def semmajor?(cur, want)
2429
- (cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]) && !want[5]
2430
- end
2431
-
2432
2573
  def printfirst?
2433
2574
  @@print_order == 0
2434
2575
  end
@@ -2470,26 +2611,24 @@ module Squared
2470
2611
  end
2471
2612
 
2472
2613
  def success?(run, *cond)
2473
- case run
2474
- when TrueClass
2475
- true
2476
- when FalseClass
2477
- false
2478
- else
2479
- $?.success?
2480
- end.tap do |ret|
2481
- next unless cond.none? { |val| val == false }
2482
-
2614
+ ret = case run
2615
+ when true, false
2616
+ run
2617
+ else
2618
+ $?.success?
2619
+ end
2620
+ if cond.none? { |val| val == false }
2483
2621
  if block_given?
2484
2622
  yield ret
2485
2623
  elsif ret && stdout? && banner?
2486
2624
  print_success
2487
2625
  end
2488
2626
  end
2627
+ ret
2489
2628
  end
2490
2629
 
2491
2630
  def banner?
2492
- ARG[:BANNER] && !env('BANNER', equals: '0')
2631
+ ARG[:BANNER] && env('BANNER', notequals: '0')
2493
2632
  end
2494
2633
 
2495
2634
  def pwd?
@@ -2516,29 +2655,9 @@ module Squared
2516
2655
  workspace.warning
2517
2656
  end
2518
2657
 
2519
- def has_value?(target, *args)
2520
- return false unless target.is_a?(Enumerable)
2521
-
2522
- args = args.first if args.size == 1 && args.first.is_a?(Enumerable)
2523
- args.any? { |obj,| target.include?(obj) }
2524
- end
2525
-
2526
- def has_value!(target, *args, first: false)
2527
- return unless target.is_a?(Enumerable)
2528
-
2529
- args = args.first if args.size == 1 && args.first.is_a?(Enumerable)
2530
- found = false
2531
- args.each do |val|
2532
- if target.respond_to?(:delete?)
2533
- found = true if target.delete?(val)
2534
- elsif target.respond_to?(:delete)
2535
- found = true if target.delete(val)
2536
- elsif target.include?(val)
2537
- found = true
2538
- end
2539
- break if found && first
2540
- end
2541
- target if found
2658
+ def exception?(*level)
2659
+ ex = exception
2660
+ level.empty? ? ex != false && ex != Logger::INFO : ex.is_a?(Numeric) && level.include?(ex)
2542
2661
  end
2543
2662
 
2544
2663
  def variables
@@ -2558,12 +2677,13 @@ module Squared
2558
2677
  end
2559
2678
 
2560
2679
  def scriptargs
2561
- { target: script? ? @output[1] : @output[0], script: script?, ref: ref, group: group, global: global }
2680
+ n = script? ? 1 : 0
2681
+ { target: @output[n], type: n == 1 ? :script : :run, ref: ref, group: group, global: global(n.succ) }
2562
2682
  end
2563
2683
  end
2564
2684
 
2565
- Application.implement(Base, base: true)
2566
- Application.attr_banner = Set.new(%i[name project path ref group parent])
2685
+ Application.implement Base, base: 0
2686
+ Application.attr_banner.merge(%i[name project path ref group parent])
2567
2687
  end
2568
2688
  end
2569
2689
  end