squared 0.4.12 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ module Squared
9
9
  module Workspace
10
10
  module Project
11
11
  class Base
12
+ include Comparable
12
13
  include Common::Format
13
14
  include System
14
15
  include Shell
@@ -17,7 +18,8 @@ module Squared
17
18
  include Support
18
19
  include Rake::DSL
19
20
 
20
- VAR_SET = %i[parent global envname dependfile dependindex theme run script env pass].freeze
21
+ VAR_SET = %i[parent global script index envname desc dependfile dependindex theme archive env dev prod graph
22
+ pass exclude].freeze
21
23
  BLK_SET = %i[run depend doc lint test copy clean].freeze
22
24
  SEM_VER = /\b(\d+)(?:(\.)(\d+))?(?:(\.)(\d+)(\S+)?)?\b/.freeze
23
25
  URI_SCHEME = %r{^([a-z][a-z\d+-.]*)://[^@:\[\]\\^<>|\s]}i.freeze
@@ -31,7 +33,7 @@ module Squared
31
33
  def bannerargs(*); end
32
34
 
33
35
  def tasks
34
- (%i[build archive graph] + BLK_SET).freeze
36
+ (%i[build archive graph prereqs] + BLK_SET).freeze
35
37
  end
36
38
 
37
39
  def as_path(val)
@@ -63,6 +65,7 @@ module Squared
63
65
  end
64
66
 
65
67
  @@tasks = {}
68
+ @@graph = { _: [] }
66
69
  @@print_order = 0
67
70
 
68
71
  subtasks({
@@ -73,8 +76,8 @@ module Squared
73
76
  attr_reader :name, :project, :workspace, :path, :theme, :exception, :pipe, :verbose,
74
77
  :group, :parent, :dependfile
75
78
 
76
- def initialize(workspace, path, name, *, group: nil, graph: nil, pass: nil, exclude: nil, release: nil,
77
- archive: nil, verbose: nil, first: {}, last: {}, error: {}, common: ARG[:COMMON], **kwargs)
79
+ def initialize(workspace, path, name, *, group: nil, first: {}, last: {}, error: {}, common: ARG[:COMMON],
80
+ **kwargs)
78
81
  @path = path
79
82
  @workspace = workspace
80
83
  @name = name.to_s.freeze
@@ -87,48 +90,34 @@ module Squared
87
90
  @copy = kwargs[:copy]
88
91
  @clean = kwargs[:clean]
89
92
  @version = kwargs[:version]
90
- @archive = case archive
91
- when String, Array
92
- { uri: archive }
93
- when Hash
94
- archive
95
- end
96
- @release = release
97
- @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
98
- @exception = env_bool(kwargs[:exception], workspace.exception, strict: true)
99
- @pipe = env_pipe(kwargs[:pipe], workspace.pipe, strict: true)
100
- @verbose = case verbose
101
- when NilClass
102
- workspace.verbose
103
- when String
104
- env_bool(verbose, workspace.verbose, strict: true, index: true)
105
- else
106
- verbose
107
- end
108
- @theme = if !@verbose
109
- {}
110
- elsif common
111
- workspace.theme
112
- else
113
- __get__(:theme)[:project][to_sym] ||= {}
114
- end
93
+ @release = kwargs[:release]
115
94
  @output = []
116
95
  @ref = []
117
96
  @children = []
118
- @graph = if graph
119
- as_a(graph, workspace.prefix ? ->(val) { workspace.task_name(val).to_sym } : :to_sym).freeze
120
- end
121
- @pass = (pass ? as_a(pass) : []).freeze
122
- @exclude = (exclude ? as_a(exclude, :to_sym) : []).freeze
123
97
  @events = {
124
98
  first: first,
125
99
  last: last,
126
100
  error: error
127
101
  }
102
+ @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
128
103
  @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
129
104
  @parent = nil
130
105
  @global = false
106
+ @log = nil
107
+ @dev = nil
108
+ @prod = nil
109
+ @withargs = nil
110
+ @session = nil
111
+ @index = -1
131
112
  run_set(kwargs[:run], kwargs[:env], opts: kwargs.fetch(:opts, true))
113
+ exception_set kwargs[:exception]
114
+ pipe_set kwargs[:pipe]
115
+ verbose_set kwargs[:verbose]
116
+ graph_set kwargs[:graph]
117
+ pass_set kwargs[:pass]
118
+ exclude_set kwargs[:exclude]
119
+ archive_set kwargs[:archive]
120
+ theme_set common
132
121
  initialize_ref Base.ref
133
122
  end
134
123
 
@@ -254,6 +243,44 @@ module Squared
254
243
  end
255
244
  end
256
245
 
246
+ def <=>(other)
247
+ return 0 unless workspace == other.workspace
248
+
249
+ a, b = graph_deps
250
+ return 1 if a.include?(other)
251
+
252
+ c, d = graph_deps other
253
+ e = b.reject { |val| d.include?(val) }
254
+ f = d.reject { |val| b.include?(val) }
255
+ if parent == other.parent
256
+ g = []
257
+ h = []
258
+ else
259
+ g = a.reject { |val| c.include?(val) }
260
+ h = c.reject { |val| a.include?(val) }
261
+ end
262
+ g << self
263
+ h << other
264
+ e.concat(g)
265
+ f.concat(h)
266
+ if g.any? { |val| f.include?(val) }
267
+ -1
268
+ elsif h.any? { |val| e.include?(val) }
269
+ 1
270
+ elsif e.any? { |val| f.include?(val) }
271
+ -1
272
+ elsif f.any? { |val| e.include?(val) }
273
+ 1
274
+ elsif @index >= 0 && (i = other.instance_variable_get(:@index)) >= 0
275
+ @index <= i ? -1 : 1
276
+ else
277
+ 0
278
+ end
279
+ rescue StandardError => e
280
+ log&.debug e
281
+ 0
282
+ end
283
+
257
284
  def ref
258
285
  Base.ref
259
286
  end
@@ -274,31 +301,31 @@ module Squared
274
301
 
275
302
  format_desc action, flag, '(-)project*'
276
303
  task flag do |_, args|
277
- args = args.to_a.reject { |val| name == val.to_s }
304
+ args = args.to_a.reject { |val| name == val }
278
305
  if flag == :run
279
306
  graph args
280
307
  else
281
308
  out, done = graph(args, out: [])
282
309
  out.map! do |val|
283
310
  done.each_with_index do |proj, i|
284
- next unless val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/)
285
-
286
- val += " (#{i.succ})"
287
- break
311
+ if val.match?(/ #{Regexp.escape(proj.name)}(?:@\d|\z)/)
312
+ val += " (#{i.succ})"
313
+ break
314
+ end
288
315
  end
289
316
  val
290
317
  end
291
318
  emphasize(out, title: path, right: true, border: borderstyle, sub: [
292
319
  { pat: /\A(#{Regexp.escape(path.to_s)})(.*)\z/, styles: theme[:header] },
293
320
  { pat: /\A(#{Regexp.escape(name)})(.*)\z/, styles: theme[:active] },
294
- { pat: /\A(.+ )(\()(\d+)(\))(.*)\z/, styles: theme[:inline], index: 3 }
321
+ { pat: /\A((?~ \() \()(\d+)(\).*)\z/, styles: theme[:inline], index: 2 }
295
322
  ])
296
323
  end
297
324
  end
298
325
  when 'unpack'
299
326
  format_desc(action, flag, 'tag/url,dir,digest?,f|force?', before: flag == :ext ? 'ext' : nil)
300
327
  params = %i[tag dir digest force]
301
- params.unshift(:ext) if flag == :ext
328
+ params.prepend(:ext) if flag == :ext
302
329
  task flag, params do |_, args|
303
330
  ext = flag == :ext ? param_guard(action, flag, args: args, key: :ext) : flag.to_s
304
331
  tag = param_guard(action, flag, args: args, key: :tag)
@@ -342,42 +369,31 @@ module Squared
342
369
  end
343
370
 
344
371
  def add(path, name = nil, **kwargs, &blk)
345
- checkdir = lambda do |val|
346
- if val.directory? && !val.empty?
347
- true
348
- else
349
- log&.warn "workspace \"#{val}\" (#{val.empty? ? 'empty' : 'not found'})"
350
- false
351
- end
352
- end
353
372
  if path.is_a?(String) && (seg = path[%r{\A(.+)[\\/]\*+\z}, 1])
354
- return self unless checkdir.call(path = basepath(seg))
373
+ return self unless checkdir?(path = basepath(seg))
355
374
 
356
- path = path.children.select { |val| checkdir.call(val) }
375
+ path = path.children.select { |val| checkdir?(val) }
357
376
  end
358
377
  if path.is_a?(Array)
359
378
  name = @name if name == true
360
379
  path.each { |val| add(val, name && task_join(name, File.basename(val)), **kwargs, &blk) }
361
380
  return self
362
- elsif !projectpath?(path = basepath(path)) || !checkdir.call(path)
381
+ elsif !projectpath?(path = basepath(path)) || !checkdir?(path)
363
382
  return self
364
- else
365
- name = case name
366
- when String, Symbol
367
- name.to_s
368
- end
369
- end
370
- if @withargs
371
- data = @withargs.dup
372
- data.merge!(kwargs)
373
- kwargs = data
374
383
  end
384
+ kwargs = @withargs.yield_self { |data| data.dup.update(kwargs) } if @withargs
375
385
  kwargs[:group] = group unless kwargs.key?(:group)
376
386
  kwargs[:ref] = ref unless kwargs.key?(:ref)
377
387
  parent = self
378
388
  proj = nil
379
- workspace.add(path, name || path.basename, **kwargs) do
380
- variable_set :parent, parent
389
+ name = case name
390
+ when String, Symbol
391
+ name.to_s
392
+ else
393
+ path.basename
394
+ end
395
+ workspace.add(path, name, **kwargs) do
396
+ __send__ :parent_set, parent
381
397
  proj = self
382
398
  end
383
399
  @children << proj
@@ -409,7 +425,7 @@ module Squared
409
425
 
410
426
  run_b(@run, sync: sync, from: from) if series?(@run)
411
427
  args = @output
412
- banner = verbosetype > 1 if task_invoked?('build', 'build:sync', 'default')
428
+ banner = verbosetype > 1 if from_base?('build')
413
429
  end
414
430
  if args.first.is_a?(Struct)
415
431
  f, blk = args.first.to_a
@@ -437,7 +453,7 @@ module Squared
437
453
 
438
454
  cmd << a if (a = compose(as_get(b), d, script: true, args: e, from: from))
439
455
  end
440
- var.merge!(c) if c.is_a?(Hash)
456
+ var.update(c) if c.is_a?(Hash)
441
457
  end
442
458
  cmd = cmd.join(' && ')
443
459
  else
@@ -450,7 +466,7 @@ module Squared
450
466
  case opts
451
467
  when Hash
452
468
  opts = append_hash(opts, build: true)
453
- cmd = as_a(cmd).push(flags).concat(opts).compact.join(' ')
469
+ cmd = as_a(cmd).append(flags).concat(opts).compact.join(' ')
454
470
  when Enumerable
455
471
  cmd = as_a(cmd).concat(opts.to_a)
456
472
  cmd.map! { |val| "#{val} #{flags}" } if flags
@@ -464,13 +480,42 @@ module Squared
464
480
  cmd = compose(as_get(opts), flags, script: true, args: extra, from: from)
465
481
  from = :script if from == :run && script?
466
482
  end
467
- run(cmd, var, from: from, banner: banner, sync: sync)
483
+ run(cmd, var, sync: sync, from: from, banner: banner)
468
484
  end
469
485
 
470
486
  def depend(*, sync: invoked_sync?('depend'), **)
471
487
  run_b(@depend, sync: sync, from: :depend)
472
488
  end
473
489
 
490
+ def prereqs(*, sync: invoked_sync?('prereqs'), **)
491
+ on :first, :prereqs
492
+ graph_deps.flatten(1).sort.each do |proj|
493
+ next if @@graph[:_].include?(proj)
494
+
495
+ if (val = ENV["PREREQS_#{proj.instance_variable_get(:@envname)}"] || ENV["PREREQS_#{proj.ref.upcase}"])
496
+ val.split(/\s*,\s*/).each do |meth|
497
+ if proj.respond_to?(meth.to_sym)
498
+ begin
499
+ proj.__send__(meth, sync: sync)
500
+ rescue StandardError => e
501
+ ret = on(:error, :prereqs, e)
502
+ raise unless ret == true
503
+ else
504
+ next
505
+ end
506
+ end
507
+ if warning?
508
+ warn log_message(Logger::WARN, name, 'method not found', subject: 'prereqs', hint: meth, pass: true)
509
+ end
510
+ end
511
+ elsif proj.build?
512
+ proj.build(sync: sync)
513
+ end
514
+ @@graph[:_] << proj
515
+ end
516
+ on :last, :prereqs
517
+ end
518
+
474
519
  def archive(*, sync: invoked_sync?('archive'), **)
475
520
  return unless @archive.is_a?(Hash)
476
521
 
@@ -478,7 +523,7 @@ module Squared
478
523
  end
479
524
 
480
525
  def doc(*, sync: invoked_sync?('doc'), **)
481
- run_b(@doc, sync: sync, from: :doc)
526
+ run_b(@doc, sync: sync, from: :doc, banner: from_base?('doc') ? verbosetype > 1 : verbose)
482
527
  end
483
528
 
484
529
  def lint(*, sync: invoked_sync?('lint'), **)
@@ -542,7 +587,7 @@ module Squared
542
587
  on :last, :clean unless pass
543
588
  end
544
589
 
545
- def graph(start = [], tasks = nil, sync: invoked_sync?('graph'), pass: [], out: nil)
590
+ def graph(start = [], tasks = nil, *, sync: invoked_sync?('graph'), pass: [], out: nil, **)
546
591
  if (val = env('GRAPH', strict: true))
547
592
  tasks ||= []
548
593
  split_escape(val).each do |task|
@@ -560,20 +605,20 @@ module Squared
560
605
  data[name] << self
561
606
  on :first, :graph
562
607
  end
563
- begin
564
- done = graph_branch(self, data, tasks, out, sync: sync, pass: pass)
565
- rescue StandardError => e
566
- ret = on(:error, :graph, e)
567
- raise unless ret == true
568
- end
608
+ ret = graph_branch(self, data, tasks, out, sync: sync, pass: pass)
609
+ rescue StandardError => e
610
+ ret = on(:error, :graph, e)
611
+ raise unless ret == true
612
+ else
569
613
  if out
570
- [out, done]
614
+ [out, ret]
571
615
  else
572
616
  on :last, :graph
573
617
  end
574
618
  end
575
619
 
576
620
  def unpack(target, uri:, sync: true, digest: nil, ext: nil, force: false, depth: 1, headers: {}, from: :unpack)
621
+ require 'open-uri'
577
622
  if !target.exist?
578
623
  target.mkpath
579
624
  elsif !target.directory?
@@ -607,7 +652,7 @@ module Squared
607
652
  end
608
653
  data = nil
609
654
  (uri = as_a(uri)).each_with_index do |url, index|
610
- fetch_uri(url, headers) do |f|
655
+ URI.open(url, headers) do |f|
611
656
  data = f.read
612
657
  if algo && algo.hexdigest(data) != digest
613
658
  data = nil
@@ -679,7 +724,7 @@ module Squared
679
724
  i += 1
680
725
  end
681
726
  FileUtils.mv(entry, dest)
682
- dest.children.each { |child| FileUtils.mv(child, target) }
727
+ dest.each_child { |child| FileUtils.mv(child, target) }
683
728
  dest.rmdir
684
729
  target = entry
685
730
  depth -= 1
@@ -736,32 +781,44 @@ module Squared
736
781
  self
737
782
  end
738
783
 
739
- def variable_set(key, *val, **kwargs, &blk)
740
- if variables.include?(key)
784
+ def variable_set(key, *args, **kwargs, &blk)
785
+ if block_given?
786
+ if blocks.include?(key)
787
+ series key, &blk
788
+ return self
789
+ end
790
+ args = block_args args, &blk
791
+ end
792
+ if variables.include?(key) || blocks.include?(key)
793
+ val = case args.size
794
+ when 0
795
+ nil
796
+ when 1
797
+ args.first
798
+ else
799
+ args
800
+ end
741
801
  case key
742
- when :build, :run
743
- run_set(*val, **kwargs)
802
+ when :index
803
+ index_set val
804
+ when :graph
805
+ graph_set val
806
+ when :pass
807
+ pass_set val
808
+ when :exclude
809
+ exclude_set val
810
+ when :parent
811
+ parent_set val
812
+ when :run
813
+ run_set(*args, **kwargs)
744
814
  when :script
745
- script_set(*val, **kwargs)
815
+ script_set(*args, **kwargs)
746
816
  when :env
747
- run_set(output[0], *val, **kwargs)
748
- when :parent
749
- @parent = val if (val = val.first).is_a?(Project::Base)
750
- when :graph
751
- @graph = case val.first
752
- when NilClass, FalseClass
753
- nil
754
- else
755
- val.flatten.map!(&:to_s).freeze
756
- end
817
+ run_set(output[0], *args, **kwargs)
757
818
  when :dependfile
758
- @dependfile = basepath(*val)
819
+ @dependfile = basepath(*args)
759
820
  else
760
- if blocks.include?(key) && block_given?
761
- series key, &blk
762
- else
763
- instance_variable_set(:"@#{key}", block_given? && val.empty? ? blk : val.first)
764
- end
821
+ instance_variable_set(:"@#{key}", val)
765
822
  end
766
823
  else
767
824
  log&.warn "variable_set: @#{key} (private)"
@@ -805,6 +862,15 @@ module Squared
805
862
  @graph.is_a?(Array) && !@graph.empty?
806
863
  end
807
864
 
865
+ def prereqs?
866
+ target = self
867
+ loop do
868
+ return true if target.graph?
869
+ break unless (target = target.parent)
870
+ end
871
+ false
872
+ end
873
+
808
874
  def copy?
809
875
  runnable?(@copy) || workspace.task_defined?(name, 'copy')
810
876
  end
@@ -897,7 +963,7 @@ module Squared
897
963
  puts_oe(*args, pipe: pipe)
898
964
  end
899
965
 
900
- def run(cmd = @session, var = nil, exception: @exception, sync: true, banner: true, chdir: path, from: nil, **)
966
+ def run(cmd = @session, var = nil, exception: @exception, sync: true, from: nil, banner: true, chdir: path, **)
901
967
  unless cmd
902
968
  if warning?
903
969
  from &&= from.to_s
@@ -910,7 +976,7 @@ module Squared
910
976
  log&.info cmd
911
977
  on :first, from
912
978
  begin
913
- if cmd.match?(/\A[^:]+:[^:]/) && workspace.task_defined?(cmd)
979
+ if cmd.start_with?(/[^:]+:[^:]/) && workspace.task_defined?(cmd)
914
980
  log&.warn "ENV discarded: #{var}" if var
915
981
  task_invoke(cmd, exception: exception, warning: warning?)
916
982
  else
@@ -922,7 +988,7 @@ module Squared
922
988
  when Enumerable
923
989
  cmd = command(*pre.to_a, cmd)
924
990
  else
925
- cmd = command(pre, cmd)
991
+ cmd = command pre, cmd
926
992
  end
927
993
  end
928
994
  args = var.is_a?(Hash) ? [var, cmd] : [cmd]
@@ -940,7 +1006,7 @@ module Squared
940
1006
  end
941
1007
  end
942
1008
 
943
- def run_s(*cmd, env: nil, sync: true, banner: verbose != false, from: nil, **kwargs)
1009
+ def run_s(*cmd, env: nil, sync: true, from: nil, banner: verbose != false, **kwargs)
944
1010
  on :first, from
945
1011
  begin
946
1012
  cmd.flatten.each { |val| run(val, env, sync: sync, banner: banner, **kwargs) }
@@ -951,11 +1017,11 @@ module Squared
951
1017
  on :last, from
952
1018
  end
953
1019
 
954
- def run_b(obj, from: nil, sync: true)
1020
+ def run_b(obj, **kwargs)
955
1021
  case obj
956
1022
  when Struct
957
1023
  if (any = instance_eval(&obj.block) || obj.run)
958
- run_b(any, from: from, sync: sync)
1024
+ run_b(any, **kwargs)
959
1025
  end
960
1026
  when Proc
961
1027
  instance_eval(&obj)
@@ -965,9 +1031,9 @@ module Squared
965
1031
  if series?(obj)
966
1032
  obj.each(&:call)
967
1033
  elsif obj.is_a?(Array) && obj.any? { |val| !val.is_a?(String) }
968
- build(*obj, from: from, sync: sync)
1034
+ build(*obj, **kwargs)
969
1035
  elsif obj
970
- run_s(obj.is_a?(Enumerable) ? obj.to_a : obj, from: from, sync: sync)
1036
+ run_s(obj.is_a?(Enumerable) ? obj.to_a : obj, **kwargs)
971
1037
  end
972
1038
  end
973
1039
  end
@@ -1052,7 +1118,7 @@ module Squared
1052
1118
  k = 0
1053
1119
  final = data.keys.last
1054
1120
  while k < depth
1055
- indent = k > 0 && ((last && !j) || (j && k == depth - 1) || single)
1121
+ indent = k > 0 ? ((last && !j) || (j && k == depth - 1) || single) : j && last && depth == 1
1056
1122
  s += "#{indent || (last && data[final].last == context) ? ' ' : a} "
1057
1123
  k += 1
1058
1124
  end
@@ -1087,10 +1153,28 @@ module Squared
1087
1153
  deps.concat(objs)
1088
1154
  end
1089
1155
  end
1090
- data[target.name] = deps.uniq.reject { |proj| proj == target }
1156
+ deps.uniq!
1157
+ deps.delete(target)
1158
+ data[target.name] = deps
1091
1159
  data
1092
1160
  end
1093
1161
 
1162
+ def graph_deps(target = self)
1163
+ key = target.name
1164
+ return @@graph[key] if @@graph.key?(key)
1165
+
1166
+ base = []
1167
+ deps = []
1168
+ loop do
1169
+ deps.concat(graph_branch(target, graph_collect(target), []))
1170
+ break unless (target = target.parent)
1171
+
1172
+ base << target
1173
+ end
1174
+ deps.uniq!
1175
+ @@graph[key] = [base, deps]
1176
+ end
1177
+
1094
1178
  def env(key, default = nil, suffix: nil, equals: nil, ignore: nil, strict: false)
1095
1179
  a = "#{key}_#{@envname}"
1096
1180
  ret = if suffix
@@ -1142,21 +1226,24 @@ module Squared
1142
1226
  cmd.done
1143
1227
  end
1144
1228
 
1229
+ def session_arg?(*args, target: @session, **kwargs)
1230
+ return false unless target
1231
+
1232
+ OptionPartition.arg?(target, *args, **kwargs)
1233
+ end
1234
+
1145
1235
  def option(*args, target: @session, prefix: target&.first, **kwargs)
1146
1236
  if prefix
1147
1237
  args.each do |val|
1148
- ret = env("#{stripext(prefix)}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
1149
- return ret if ret
1238
+ next unless (ret = env("#{stripext(prefix)}_#{val.gsub(/\W/, '_')}".upcase, **kwargs))
1239
+ return ret unless block_given?
1240
+
1241
+ return yield ret
1150
1242
  end
1151
1243
  end
1152
1244
  nil
1153
1245
  end
1154
1246
 
1155
- def option_sanitize(opts, list, target: @session, **kwargs)
1156
- op = OptionPartition.new(opts, list, target, project: self, **kwargs)
1157
- [op.extras, op.values]
1158
- end
1159
-
1160
1247
  def option_clear(opts, target: @session, **kwargs)
1161
1248
  return unless target
1162
1249
 
@@ -1168,7 +1255,7 @@ module Squared
1168
1255
  end
1169
1256
 
1170
1257
  def print_item(*val)
1171
- puts if !printfirst? && stdout?
1258
+ puts unless printfirst?
1172
1259
  printsucc
1173
1260
  puts val unless val.empty? || (val.size == 1 && val.first.nil?)
1174
1261
  end
@@ -1182,7 +1269,7 @@ module Squared
1182
1269
  styles = [:bold] + styles
1183
1270
  end
1184
1271
  end
1185
- n = Project.max_width(lines)
1272
+ n = line_width lines
1186
1273
  ch = ' ' * pad
1187
1274
  index = -1
1188
1275
  lines.map! do |val|
@@ -1198,7 +1285,7 @@ module Squared
1198
1285
  end
1199
1286
 
1200
1287
  def print_footer(*lines, sub: nil, reverse: false, right: false, **kwargs)
1201
- n = Project.max_width(lines)
1288
+ n = line_width lines
1202
1289
  lines.map! do |val|
1203
1290
  s = right ? val.rjust(n) : val.ljust(n)
1204
1291
  sub&.each { |h| s = sub_style(s, **h) }
@@ -1212,31 +1299,30 @@ module Squared
1212
1299
  def format_desc(action, flag, opts = nil, **kwargs)
1213
1300
  return unless TASK_METADATA
1214
1301
 
1215
- ret = [@desc, action]
1216
- ret << flag if flag
1217
- workspace.format_desc(ret, opts, **kwargs)
1302
+ workspace.format_desc([@desc, action, flag].compact, opts, **kwargs)
1218
1303
  end
1219
1304
 
1220
1305
  def format_banner(cmd, banner: true)
1221
1306
  return unless banner && banner?
1222
1307
 
1223
1308
  if (data = workspace.banner_get(*@ref, group: group))
1224
- return if data.empty?
1309
+ return if !data.command && data.order.empty?
1225
1310
 
1226
1311
  client = true
1227
1312
  else
1228
- data = { command: true, order: [:path], styles: theme[:banner], border: theme[:border] }
1313
+ data = Workspace::Support::BannerData.new(true, [:path], theme[:banner], theme[:border])
1229
1314
  end
1230
1315
  if verbose
1231
1316
  out = []
1232
- if data[:command]
1233
- if cmd =~ /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+)) /
1317
+ if data.command
1318
+ if cmd =~ /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+))( |\z)/
1234
1319
  path = $3 || $2 || $1
1235
- cmd = cmd.sub(path, stripext(path).upcase)
1320
+ name = stripext path
1321
+ cmd = cmd.sub(path, data.command == 0 ? name : name.upcase)
1236
1322
  end
1237
1323
  out << cmd
1238
1324
  end
1239
- data[:order].each do |val|
1325
+ data.order.each do |val|
1240
1326
  if val.is_a?(Array)
1241
1327
  s = ' '
1242
1328
  found = false
@@ -1246,7 +1332,7 @@ module Squared
1246
1332
  meth
1247
1333
  elsif respond_to?(meth)
1248
1334
  found = true
1249
- __send__(meth)
1335
+ __send__ meth
1250
1336
  end
1251
1337
  end
1252
1338
  val = val.compact.join(s)
@@ -1256,9 +1342,9 @@ module Squared
1256
1342
  end
1257
1343
  out << val.to_s
1258
1344
  end
1259
- print_banner(*out, styles: data[:styles], border: data[:border], client: client)
1345
+ print_banner(*out, styles: data.styles, border: data.border, client: client)
1260
1346
  elsif workspace.series.multiple?
1261
- "## #{__send__(data[:order].first || :path)} ##"
1347
+ "## #{__send__(data.order.first || :path)} ##"
1262
1348
  end
1263
1349
  end
1264
1350
 
@@ -1335,14 +1421,15 @@ module Squared
1335
1421
  end
1336
1422
 
1337
1423
  def append_any(val, target: @session, build: false, delim: false)
1424
+ return unless val
1425
+
1338
1426
  if delim && !target.include?('--')
1339
1427
  target << '--'
1340
1428
  else
1341
1429
  delim = false
1342
1430
  end
1431
+ val = shell_split(val) if val.is_a?(String)
1343
1432
  case val
1344
- when String
1345
- target << val
1346
1433
  when Hash
1347
1434
  append_hash(val, target: target, build: build)
1348
1435
  when Enumerable
@@ -1373,7 +1460,7 @@ module Squared
1373
1460
  return target << (if flag
1374
1461
  shell_option(opt, equals ? val : nil, quote: quote, escape: escape, force: force)
1375
1462
  else
1376
- shell_quote(val)
1463
+ shell_quote val
1377
1464
  end)
1378
1465
  end
1379
1466
  nil
@@ -1383,17 +1470,18 @@ module Squared
1383
1470
  **kwargs)
1384
1471
  return if list.empty?
1385
1472
 
1386
- ret = []
1387
- list.flatten.each do |flag|
1388
- next unless (val = option(flag, target: target, **kwargs))
1473
+ [].tap do |ret|
1474
+ list.flatten.each do |flag|
1475
+ next unless (val = option(flag, target: target, **kwargs))
1389
1476
 
1390
- if val == '0' && no
1391
- flag = "no-#{flag}"
1392
- val = nil
1477
+ if val == '0' && no
1478
+ flag = "no-#{flag}"
1479
+ val = nil
1480
+ end
1481
+ ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1393
1482
  end
1394
- ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1483
+ ret.each { |val| target << val } unless ret.empty?
1395
1484
  end
1396
- ret.each { |val| target << val } unless ret.empty?
1397
1485
  end
1398
1486
 
1399
1487
  def append_nocolor(target: @session)
@@ -1441,9 +1529,9 @@ module Squared
1441
1529
  end
1442
1530
 
1443
1531
  def collect_hash(data, pass: [])
1444
- ret = []
1445
- data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1446
- ret
1532
+ [].tap do |ret|
1533
+ data.each { |key, val| ret.concat(val) unless pass.include?(key) }
1534
+ end
1447
1535
  end
1448
1536
 
1449
1537
  def parse_json(val, kind: Hash, hint: nil)
@@ -1456,15 +1544,6 @@ module Squared
1456
1544
  ret
1457
1545
  end
1458
1546
 
1459
- def fetch_uri(*args, **kwargs, &blk)
1460
- require 'open-uri'
1461
- if RUBY_VERSION < '2.5'
1462
- open(*args, **kwargs, &blk)
1463
- else
1464
- URI.open(*args, **kwargs, &blk)
1465
- end
1466
- end
1467
-
1468
1547
  def param_guard(action, flag, args:, key: nil, pat: nil, values: nil)
1469
1548
  if args && key
1470
1549
  val = args.fetch(key, nil)
@@ -1479,7 +1558,7 @@ module Squared
1479
1558
  args
1480
1559
  end
1481
1560
 
1482
- def confirm_outdated(pkg, ver, rev, lock: false)
1561
+ def confirm_outdated(pkg, ver, rev, cur = nil, lock: false, col1: 0)
1483
1562
  a = sub_style(case rev
1484
1563
  when 1
1485
1564
  'MAJOR'
@@ -1487,11 +1566,11 @@ module Squared
1487
1566
  'MINOR'
1488
1567
  else
1489
1568
  'PATCH'
1490
- end, styles: theme[:header])
1491
- b = sub_style("#{pkg} #{ver}", styles: theme[:inline])
1569
+ end, styles: (rev == 1 && theme[:major]) || theme[:header])
1570
+ b = sub_style(pkg.ljust(col1), styles: theme[:inline])
1492
1571
  c, d = rev == 1 || lock ? ['y/N', 'N'] : ['Y/n', 'Y']
1493
- e = lock ? " #{sub_style('(locked)', styles: color(:red))}" : ''
1494
- confirm "Upgrade to #{a}? #{b + e} [#{c}] ", d
1572
+ e = lock ? sub_style((cur || 'locked').rjust(7), styles: color(:red)) : cur&.rjust(7)
1573
+ confirm "#{a}: #{b}#{e} #{sub_style(ver.rjust(col1 > 0 ? 10 : 0), styles: theme[:inline])} [#{c}] ", d
1495
1574
  end
1496
1575
 
1497
1576
  def choice_index(msg, list, values: nil, accept: nil, series: false, trim: nil, column: nil,
@@ -1547,6 +1626,20 @@ module Squared
1547
1626
  ret
1548
1627
  end
1549
1628
 
1629
+ def command_args(args, force: false, **kwargs)
1630
+ return unless args.size == 1 && !option('i', 'interactive', **kwargs, equals: '0')
1631
+
1632
+ readline('Enter arguments', force: force)
1633
+ end
1634
+
1635
+ def block_args(val = nil, &blk)
1636
+ if (ret = instance_eval(&blk)).nil?
1637
+ val
1638
+ else
1639
+ ret.is_a?(Array) ? ret : [ret]
1640
+ end
1641
+ end
1642
+
1550
1643
  def runenv
1551
1644
  nil
1552
1645
  end
@@ -1592,8 +1685,7 @@ module Squared
1592
1685
  end
1593
1686
 
1594
1687
  def semscan(val, fill: true)
1595
- ret = val.scan(SEM_VER).first
1596
- fill ? semver(ret) : ret
1688
+ val.scan(SEM_VER).first.yield_self { |data| fill ? semver(data) : data }
1597
1689
  end
1598
1690
 
1599
1691
  def indexitem(val)
@@ -1609,8 +1701,7 @@ module Squared
1609
1701
  end
1610
1702
 
1611
1703
  def color(val)
1612
- ret = theme[val]
1613
- ret && !ret.empty? ? ret : [val]
1704
+ theme[val].yield_self { |styles| styles && !styles.empty? ? styles : [val] }
1614
1705
  end
1615
1706
 
1616
1707
  def colormap(val)
@@ -1652,39 +1743,22 @@ module Squared
1652
1743
  end
1653
1744
  end
1654
1745
 
1655
- def pwd_set(done = nil, pass: false, from: nil, dryrun: false)
1656
- pwd = Pathname.pwd
1657
- if block_given?
1658
- begin
1659
- if path == pwd || pass == true || (pass.is_a?(String) && semscan(pass).join <= RUBY_VERSION)
1660
- ret = yield
1661
- else
1662
- Dir.chdir(path)
1663
- ret = yield
1664
- Dir.chdir(pwd)
1665
- end
1666
- rescue StandardError => e
1667
- log&.error e
1668
- unless dryrun
1669
- ret = on(:error, from, e)
1670
- raise if exception && ret != true
1671
- end
1672
- else
1673
- ret
1674
- end
1675
- elsif @pwd == pwd
1676
- @pwd = nil
1677
- pwd unless done
1678
- elsif @pwd
1679
- return unless path == pwd
1680
-
1681
- Dir.chdir(@pwd)
1682
- @pwd = nil
1683
- elsif !done && path != pwd
1684
- @pwd = pwd
1746
+ def pwd_set(pass: false, from: nil)
1747
+ pwd = Dir.pwd
1748
+ if path.to_s == pwd || pass == true || (pass.is_a?(String) && semscan(pass).join <= RUBY_VERSION)
1749
+ ret = yield
1750
+ else
1685
1751
  Dir.chdir(path)
1686
- @pwd
1752
+ ret = yield
1753
+ Dir.chdir(pwd)
1687
1754
  end
1755
+ rescue StandardError => e
1756
+ puts e
1757
+ log&.error e
1758
+ ret = on(:error, from, e)
1759
+ raise if exception && ret != true
1760
+ else
1761
+ ret
1688
1762
  end
1689
1763
 
1690
1764
  def run_set(cmd, val = nil, opts: nil, **)
@@ -1749,6 +1823,72 @@ module Squared
1749
1823
  @output[4] = args unless @output[4] == false || args.nil?
1750
1824
  end
1751
1825
 
1826
+ def index_set(val)
1827
+ @index = val if val.is_a?(Numeric)
1828
+ end
1829
+
1830
+ def parent_set(val)
1831
+ @parent = val if val.is_a?(Project::Base)
1832
+ end
1833
+
1834
+ def exception_set(val)
1835
+ @exception = env_bool(val, workspace.exception, strict: true)
1836
+ end
1837
+
1838
+ def pipe_set(val)
1839
+ @pipe = env_pipe(val, workspace.pipe, strict: true)
1840
+ end
1841
+
1842
+ def verbose_set(val)
1843
+ @verbose = case val
1844
+ when NilClass
1845
+ workspace.verbose
1846
+ when String
1847
+ env_bool(val, workspace.verbose, strict: true, index: true)
1848
+ else
1849
+ val
1850
+ end
1851
+ end
1852
+
1853
+ def graph_set(val)
1854
+ @graph = if val
1855
+ as_a(val).map { |s| workspace.prefix ? workspace.task_name(s).to_sym : s.to_sym }.freeze
1856
+ end
1857
+ end
1858
+
1859
+ def pass_set(val)
1860
+ @pass = (val ? as_a(val, :to_s) : []).freeze
1861
+ end
1862
+
1863
+ def exclude_set(val)
1864
+ @exclude = (val ? as_a(val, :to_sym) : []).freeze
1865
+ end
1866
+
1867
+ def archive_set(val)
1868
+ @archive = case val
1869
+ when String, Array
1870
+ { uri: val }
1871
+ when Hash
1872
+ val
1873
+ end
1874
+ end
1875
+
1876
+ def theme_set(common)
1877
+ @theme = if !verbose
1878
+ {}
1879
+ elsif common
1880
+ workspace.theme
1881
+ else
1882
+ __get__(:theme)[:project][to_sym] ||= {}
1883
+ end
1884
+ end
1885
+
1886
+ def dependfile_set(list)
1887
+ @dependindex = list.index { |file| basepath(file).exist? }.tap do |index|
1888
+ @dependfile = @path + list[index || 0]
1889
+ end
1890
+ end
1891
+
1752
1892
  def as_get(val)
1753
1893
  return unless val
1754
1894
 
@@ -1765,7 +1905,7 @@ module Squared
1765
1905
  unless @pass.include?(key.to_s) || ws.task_defined?(name, action) || ws.task_exclude?(action, self)
1766
1906
  ws.task_desc(@desc, action)
1767
1907
  task action do
1768
- __send__(key)
1908
+ __send__ key
1769
1909
  end
1770
1910
  end
1771
1911
  next if (items = @children.select { |item| item.task_include?(key) }).empty?
@@ -1777,8 +1917,18 @@ module Squared
1777
1917
  end
1778
1918
 
1779
1919
  def projectpath?(val)
1780
- val = Pathname.new(val).cleanpath
1781
- val.absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?(File.join('..', ''))
1920
+ Pathname.new(val).cleanpath.yield_self do |file|
1921
+ file.absolute? ? file.to_s.start_with?(File.join(path, '')) : !file.to_s.start_with?(File.join('..', ''))
1922
+ end
1923
+ end
1924
+
1925
+ def checkdir?(val)
1926
+ if val.directory? && !val.empty?
1927
+ true
1928
+ else
1929
+ log&.warn "directory \"#{val}\" (#{val.empty? ? 'empty' : 'not found'})"
1930
+ false
1931
+ end
1782
1932
  end
1783
1933
 
1784
1934
  def semmajor?(cur, want)
@@ -1802,8 +1952,8 @@ module Squared
1802
1952
  val.is_a?(Array) && val.all? { |p| p.is_a?(Proc) || p.is_a?(Method) }
1803
1953
  end
1804
1954
 
1805
- def session_arg?(*args, target: @session, **kwargs)
1806
- !!target && OptionPartition.arg?(target, *args, **kwargs)
1955
+ def from_base?(val)
1956
+ task_invoked?(val, "#{val}:sync", 'default')
1807
1957
  end
1808
1958
 
1809
1959
  def from_sync?(*val)
@@ -1819,13 +1969,9 @@ module Squared
1819
1969
  return val if group && !(val = from_sync?(ac, group)).nil?
1820
1970
  return val if (base = workspace.find_base(self)) && !(val = from_sync?(ac, base.ref)).nil?
1821
1971
  return false if workspace.series.chain?(val = task_join(name, action))
1972
+ return true if task_invoked?(val) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
1822
1973
 
1823
- if task_invoked?(val) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
1824
- true
1825
- else
1826
- val = workspace.series.name_get(action)
1827
- val != action && invoked_sync?(val)
1828
- end
1974
+ workspace.series.name_get(action).yield_self { |name| name != action && invoked_sync?(name) }
1829
1975
  end
1830
1976
 
1831
1977
  def success?(ret)
@@ -1848,22 +1994,19 @@ module Squared
1848
1994
  workspace.warning
1849
1995
  end
1850
1996
 
1851
- def has_value?(data, val)
1852
- return data.value?(val) if data.is_a?(Hash)
1853
- return data.is_a?(Enumerable) && data.to_a.include?(val) if !val.is_a?(Enumerable) || val.is_a?(Hash)
1854
-
1855
- val.to_a.any? do |obj|
1856
- case data
1857
- when Hash
1858
- data.value?(obj)
1859
- when Enumerable
1860
- data.to_a.include?(obj)
1861
- end
1997
+ def has_value?(data, other)
1998
+ case data
1999
+ when Hash
2000
+ other.is_a?(Enumerable) ? other.any? { |obj| data.value?(obj) } : data.value?(other)
2001
+ when Enumerable
2002
+ other.is_a?(Enumerable) ? other.any? { |obj| data.include?(obj) } : data.include?(other)
2003
+ else
2004
+ false
1862
2005
  end
1863
2006
  end
1864
2007
 
1865
2008
  def variables
1866
- Base.tasks + VAR_SET
2009
+ VAR_SET
1867
2010
  end
1868
2011
 
1869
2012
  def blocks
@@ -1871,7 +2014,7 @@ module Squared
1871
2014
  end
1872
2015
 
1873
2016
  def borderstyle
1874
- ((data = workspace.banner_get(*@ref, group: group)) && data[:border]) || theme[:border]
2017
+ workspace.banner_get(*@ref, group: group)&.border || theme[:border]
1875
2018
  end
1876
2019
 
1877
2020
  def headerstyle
@@ -1884,7 +2027,7 @@ module Squared
1884
2027
  end
1885
2028
 
1886
2029
  Application.implement(Base, base: true)
1887
- Application.attr_banner = Common::SymSet.new(%i[name project path ref group parent])
2030
+ Application.attr_banner = Set.new(%i[name project path ref group parent])
1888
2031
  end
1889
2032
  end
1890
2033
  end