squared 0.0.9 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'date'
4
5
  require 'logger'
5
6
 
@@ -12,12 +13,18 @@ module Squared
12
13
  include System
13
14
  include Shell
14
15
  include Task
16
+ include Utils
15
17
  include ::Rake::DSL
16
18
 
17
- SEM_VER = /(\d+)(?:(\.)(\d+))?(?:(\.)(\d+))?/.freeze
19
+ SEM_VER = /(\d+)(?:(\.)(\d+))?(?:(\.)(\d+)(\S+)?)?/.freeze
20
+
21
+ VAR_SET = %i[parent global envname theme run script env depend doc test copy clean].freeze
22
+ private_constant :VAR_SET
18
23
 
19
24
  class << self
20
25
  def populate(*); end
26
+ def batchargs(*); end
27
+ def aliasargs(*); end
21
28
 
22
29
  def tasks
23
30
  [].freeze
@@ -39,42 +46,106 @@ module Squared
39
46
  def to_s
40
47
  super.to_s.match(/[^:]+$/)[0]
41
48
  end
49
+
50
+ def config?(*)
51
+ false
52
+ end
42
53
  end
43
54
 
44
55
  @@print_order = 0
45
56
  @@tasks = {}
46
57
 
47
- attr_reader :name, :project, :workspace, :group, :path, :theme
58
+ attr_reader :name, :project, :workspace, :path, :exception, :pipe, :verbose, :theme, :group, :parent
48
59
 
49
- def initialize(name, path, workspace, *, group: nil, **kwargs)
50
- @name = name.to_s
60
+ def initialize(workspace, path, name, *, group: nil, pass: nil, exclude: nil, common: ARG[:COMMON], **kwargs)
51
61
  @path = path
52
- @project = @path.basename.to_s
53
62
  @workspace = workspace
54
- @group = group&.to_s
63
+ @name = name.to_s.freeze
64
+ @project = @path.basename.to_s.freeze
65
+ @group = group&.to_s.freeze
55
66
  @depend = kwargs[:depend]
56
67
  @doc = kwargs[:doc]
57
68
  @test = kwargs[:test]
58
- @output = [kwargs[:run], nil]
59
69
  @copy = kwargs[:copy]
60
70
  @clean = kwargs[:clean]
61
- @theme = if !workspace.verbose
71
+ @exception = kwargs.key?(:exception) ? env_bool(kwargs[:exception]) : workspace.exception
72
+ @pipe = kwargs.key?(:pipe) ? env_pipe(kwargs[:pipe]) : workspace.pipe
73
+ @verbose = kwargs.key?(:verbose) ? kwargs[:verbose] : workspace.verbose
74
+ @theme = if !@verbose
62
75
  {}
63
- elsif kwargs.fetch(:common, true)
76
+ elsif common
64
77
  workspace.theme
65
78
  else
66
79
  __get__(:theme)[:project][to_sym] ||= {}
67
80
  end
81
+ @output = []
68
82
  @ref = []
69
- @exclude = as_a(kwargs[:exclude], :to_sym).freeze
83
+ @children = []
84
+ @pass = (pass ? as_a(pass, :to_sym) : []).freeze
85
+ @exclude = (exclude ? as_a(exclude, :to_sym) : []).freeze
86
+ @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
87
+ @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
88
+ @parent = nil
89
+ @global = false
90
+ run_set(kwargs[:run], kwargs[:env], opts: kwargs.fetch(:opts, true))
70
91
  initialize_ref(Base.ref)
92
+ end
93
+
94
+ def initialize_ref(ref)
95
+ @ref << ref unless @exclude.include?(ref)
96
+ end
97
+
98
+ def initialize_build(ref, **kwargs)
99
+ initialize_ref(ref)
100
+ if (@script = @workspace.script_get(group: @group, ref: ref))
101
+ if @script[:log] && !kwargs.key?(:log)
102
+ kwargs[:log] = @script[:log]
103
+ @log = nil
104
+ end
105
+ @depend = @script[:depend] if @depend.nil?
106
+ @doc = @script[:doc] if @doc.nil?
107
+ @test = @script[:test] if @test.nil?
108
+ @clean = @script[:clean] if @clean.nil?
109
+ @exclude = @script[:exclude] if @exclude.empty? && @script.key?(:exclude)
110
+ end
71
111
  initialize_logger(**kwargs)
112
+ return if @output[0] == false
113
+
114
+ data = @workspace.script_find(*@ref, @group)
115
+ if @output[0].nil?
116
+ if (scr = data[:script])
117
+ @global = true
118
+ script_set(scr, prod: kwargs[:prod])
119
+ elsif (run = data[:run])
120
+ @global = true
121
+ run_set run
122
+ end
123
+ unless data[:env]
124
+ if (scr = kwargs[:script])
125
+ @global = false
126
+ script_set scr
127
+ elsif @script && !data[:global]
128
+ if (scr = @script[:script])
129
+ @global = false
130
+ script_set scr
131
+ elsif (run = @script[:run])
132
+ @global = false
133
+ run_set run
134
+ end
135
+ end
136
+ end
137
+ elsif data[:env] && data[:run]
138
+ @global = true
139
+ run_set data[:run]
140
+ end
72
141
  end
73
142
 
74
143
  def initialize_logger(log: nil, **)
144
+ return if @log
145
+
75
146
  log = log.is_a?(::Hash) ? log.dup : { file: log }
76
- if (file = env('LOG_FILE')).nil? && (auto = env('LOG_AUTO'))
77
- file = case auto
147
+ unless (file = env('LOG_FILE'))
148
+ file = case env('LOG_AUTO')
78
149
  when 'y', 'year'
79
150
  "#{@name}-#{Date.today.year}.log"
80
151
  when 'm', 'month'
@@ -85,53 +156,51 @@ module Squared
85
156
  end
86
157
  if file ||= log[:file]
87
158
  file = Date.today.strftime(file)
88
- file = (dir = env('LOG_DIR')) ? Pathname.new(dir).join(file) : Pathname.new(file)
159
+ file = (dir = env('LOG_DIR')) ? @workspace.home.join(dir, file) : @workspace.home.join(file)
89
160
  begin
90
161
  file = file.realdirpath
91
162
  rescue StandardError => e
92
- raise if @workspace.exception
163
+ raise if @exception
93
164
 
94
165
  file = nil
95
- warn e if @workspace.warning
166
+ warn log_message(::Logger::WARN, e) if warning?
96
167
  end
97
168
  end
98
- log[:progname] = @name
99
- log[:level] = env('LOG_LEVEL', log[:level] || Logger::INFO, ignore: nil)
169
+ log[:progname] ||= @name
170
+ if (val = env('LOG_LEVEL', ignore: false))
171
+ log[:level] = val
172
+ end
100
173
  log.delete(:file)
101
174
  @log = [file, log]
102
175
  end
103
176
 
104
- def initialize_build(ref, **kwargs)
105
- initialize_script(ref, **kwargs)
106
- if (val = env('BUILD', strict: true))
107
- @output[0] = val
108
- elsif @script && @output[0] != false
109
- @output[0] ||= @script[:run]
177
+ def initialize_env(dev: nil, prod: nil, **)
178
+ pre = "BUILD_#{@envname}"
179
+ @dev = env_match("#{pre}_DEV", dev)
180
+ @prod = env_match("#{pre}_PROD", prod)
181
+ cmd = @output[0]
182
+ unless cmd == false || cmd.is_a?(::Array) || (val = env('BUILD', suffix: 'OPTS')).nil?
183
+ @output[cmd ? 1 : 3] = shell_split(val, join: true)
110
184
  end
111
- @dev = kwargs.delete(:dev)
112
- if env('BUILD', suffix: 'DEV', equals: '0')
113
- @dev = false
114
- elsif env('BUILD', suffix: 'DEV')
115
- @dev = true
116
- elsif @dev.nil?
117
- @dev = !group.nil? && workspace.dev?(group: group, global: true)
185
+ unless @output[2] == false || (val = env('BUILD', suffix: 'ENV')).nil?
186
+ begin
187
+ data = JSON.parse(val)
188
+ raise_error('invalid JSON object', val, hint: "#{prefix}_ENV") unless data.is_a?(::Hash)
189
+ @output[2] = data
190
+ rescue StandardError => e
191
+ log.warn e
192
+ end
118
193
  end
119
- end
120
-
121
- def initialize_script(ref, **)
122
- initialize_ref(ref)
123
- return unless (script = workspace.script(group: group, ref: ref))
124
-
125
- @depend = script[:depend] if @depend.nil?
126
- @doc = script[:doc] if @doc.nil?
127
- @test = script[:test] if @test.nil?
128
- @clean = script[:clean] if @clean.nil?
129
- @exclude = script[:exclude] if @exclude.empty? && script.key?(:exclude)
130
- @script = script
131
- end
194
+ return unless (val = env('BUILD', strict: true))
132
195
 
133
- def initialize_ref(ref)
134
- @ref << ref unless @exclude.include?(ref)
196
+ @global = false
197
+ if val == '0'
198
+ @output = [false]
199
+ elsif script?
200
+ script_set val
201
+ else
202
+ run_set val
203
+ end
135
204
  end
136
205
 
137
206
  def ref
@@ -139,80 +208,114 @@ module Squared
139
208
  end
140
209
 
141
210
  def populate(*)
142
- valid = ref?(Base.ref)
143
- series = workspace.series
144
-
145
211
  namespace name do
146
- series.each_key do |key|
147
- next unless series.include?(key) ? has?(key) && valid : workspace.task_extend?(self, key)
148
-
149
- desc message(*name.split(':'), key)
150
- task key do
151
- __send__(key)
212
+ workspace.series.each_key do |key|
213
+ next unless workspace.task_include?(self, key)
214
+
215
+ alt = workspace.series.name_get(key)
216
+ unless workspace.task_defined?(name, alt)
217
+ desc message(@desc, alt)
218
+ task alt do
219
+ __send__(key)
220
+ end
152
221
  end
222
+ next if (items = @children.select { |item| workspace.task_include?(item, key) }).empty?
223
+
224
+ desc message(@desc, alt, 'workspace')
225
+ task task_join(alt, 'workspace') => items.map { |item| task_join(item.name, alt) }
153
226
  end
154
227
  end
155
228
  end
156
229
 
157
- def build(*args, sync: true)
230
+ def with(**kwargs, &blk)
231
+ @withargs = kwargs.empty? ? nil : kwargs
232
+ if block_given?
233
+ instance_eval(&blk)
234
+ @withargs = nil
235
+ end
236
+ self
237
+ end
238
+
239
+ def add(path, name = nil, **kwargs, &blk)
240
+ if path.is_a?(::String) && (data = %r{^(.+)[\\/]\*+$}.match(path))
241
+ path = base_path(data[1]).children.select(&:directory?)
242
+ end
243
+ if path.is_a?(::Array)
244
+ name = @name if name == true
245
+ path.each { |val| add(val, name && task_join(name, File.basename(val)), **kwargs, &blk) }
246
+ return self
247
+ elsif !source_path?(path = base_path(path))
248
+ return self
249
+ elsif !name.is_a?(::String) && !name.is_a?(::Symbol)
250
+ name = nil
251
+ end
252
+ if @withargs
253
+ data = @withargs.dup
254
+ data.merge!(kwargs)
255
+ kwargs = data
256
+ end
257
+ kwargs[:group] = group unless kwargs.key?(:group)
258
+ kwargs[:ref] = ref unless kwargs.key?(:ref)
259
+ parent = self
260
+ proj = nil
261
+ workspace.add(path, name || path.basename, **kwargs) do
262
+ variable_set :parent, parent
263
+ proj = self
264
+ end
265
+ @children << proj
266
+ proj.instance_eval(&blk) if block_given?
267
+ self
268
+ end
269
+
270
+ def build(*args, sync: invoked_sync?('build'), **)
158
271
  if args.empty?
159
- cmd, opts = @output
160
- opts &&= shell_escape(opts)
272
+ cmd, opts, var, flags = @output
273
+ banner = verbose == 1
161
274
  else
162
275
  cmd = args.shift
163
- opts = sanitize_args(*args)
276
+ opts = args.map { |val| shell_quote(val, force: false) }.join(' ')
277
+ banner = verbose
164
278
  end
165
279
  if cmd
166
- if opts
280
+ case opts
281
+ when ::String
167
282
  cmd = "#{cmd} #{opts}"
168
- elsif cmd.is_a?(::Array)
283
+ when ::Array
169
284
  cmd = cmd.join(' && ')
285
+ else
286
+ cmd = [cmd, compose(opts, script: false)].compact.join(' ') unless opts == false || !respond_to?(:compose)
170
287
  end
171
- banner = verbose?
172
288
  else
173
289
  return unless respond_to?(:compose)
174
290
 
175
- cmd = compose(opts)
176
- banner = env('REPO_BUILD') == 'verbose'
177
- end
178
- run(cmd, banner: banner, sync: sync)
179
- end
180
-
181
- def refresh(*)
182
- build(sync: invoked_sync?('depend'))
183
- key = "#{name}:copy"
184
- if workspace.task_defined?(key)
185
- invoke(key, exception: workspace.exception, warning: workspace.warning)
186
- else
187
- copy
291
+ cmd = compose(opts, flags, script: true)
188
292
  end
293
+ run(cmd, var, banner: banner, sync: sync)
189
294
  end
190
295
 
191
- def depend(*)
192
- run(@depend, sync: invoked_sync?('depend')) if @depend
296
+ def depend(*, sync: invoked_sync?('depend'), **)
297
+ run(@depend, sync: sync) if @depend
193
298
  end
194
299
 
195
- def copy(*)
196
- run_s(@copy, sync: invoked_sync?('copy')) if @copy
300
+ def copy(*, sync: invoked_sync?('copy'), **)
301
+ run_s(@copy, sync: sync) if @copy
197
302
  end
198
303
 
199
- def doc
200
- build(@doc, sync: invoked_sync?('doc')) if @doc
304
+ def doc(*, sync: invoked_sync?('doc'), **)
305
+ build(@doc, sync: sync) if @doc
201
306
  end
202
307
 
203
- def test
204
- build(@test, sync: invoked_sync?('test')) if @test
308
+ def test(*, sync: invoked_sync?('test'), **)
309
+ build(@test, sync: sync) if @test
205
310
  end
206
311
 
207
- def clean
208
- return unless @clean
209
-
210
- if @clean.is_a?(::String)
312
+ def clean(*)
313
+ case @clean
314
+ when ::String
211
315
  run_s(@clean, sync: invoked_sync?('clean'))
212
- else
213
- @clean.each do |val|
214
- val = val.to_s
215
- if val =~ %r{[\\/]$}
316
+ when ::Enumerable
317
+ as_a(@clean).each do |val|
318
+ if (val = val.to_s) =~ %r{[\\/]$}
216
319
  dir = Pathname.new(val)
217
320
  dir = base_path(dir) unless dir.absolute?
218
321
  next unless dir.directory?
@@ -239,10 +342,52 @@ module Squared
239
342
  @log = Logger.new(enabled? ? @log[0] : nil, **@log[1])
240
343
  end
241
344
 
242
- def base_path(*args)
243
- path.join(*args)
345
+ def base_path(*args, ascend: nil)
346
+ ret = path.join(*args)
347
+ return ret unless ascend && !ret.exist?
348
+
349
+ path.parent.ascend.each do |dir|
350
+ target = dir.join(*args)
351
+ return target if target.exist?
352
+ break if (ascend.is_a?(String) && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
353
+ end
354
+ ret
244
355
  end
245
356
 
357
+ def color(val)
358
+ ret = theme[val]
359
+ ret && !ret.empty? ? ret : [val]
360
+ end
361
+
362
+ def variable_set(key, *val, **kwargs)
363
+ if variables.include?(key)
364
+ case key
365
+ when :run
366
+ run_set(*val, **kwargs)
367
+ when :script
368
+ script_set(*val, **kwargs)
369
+ when :env
370
+ run_set(output[0], *val, **kwargs)
371
+ when :parent
372
+ @parent = val if (val = val.first).is_a?(Project::Base)
373
+ else
374
+ instance_variable_set :"@#{key}", val.first
375
+ end
376
+ else
377
+ log.warn "variable_set: @#{key} (private)"
378
+ end
379
+ end
380
+
381
+ def variables
382
+ VAR_SET
383
+ end
384
+
385
+ def allref
386
+ @ref.reverse_each
387
+ end
388
+
389
+ def version(*); end
390
+
246
391
  def inspect
247
392
  "#<#{self.class}: #{name} => #{self}>"
248
393
  end
@@ -255,12 +400,16 @@ module Squared
255
400
  name.to_sym
256
401
  end
257
402
 
258
- def enabled?
403
+ def enabled?(ref = nil)
404
+ return false if ref && !ref?(ref)
405
+
259
406
  path.directory? && !path.empty?
260
407
  end
261
408
 
262
- def has?(method)
263
- respond_to?(m = :"#{method}?") && __send__(m)
409
+ def has?(meth, ref = nil)
410
+ return false if ref && !ref?(ref)
411
+
412
+ respond_to?(meth = :"#{meth}?") && __send__(meth)
264
413
  end
265
414
 
266
415
  def ref?(val)
@@ -268,15 +417,19 @@ module Squared
268
417
  end
269
418
 
270
419
  def build?
271
- !!@output[0]
420
+ !!@output[0] || script?
272
421
  end
273
422
 
274
- def refresh?
275
- build? && (copy? || workspace.task_defined?("#{name}:copy"))
423
+ def script?
424
+ @output[0].nil? && !!@output[1] && respond_to?(:compose)
276
425
  end
277
426
 
278
427
  def depend?
279
- @depend != false && (!@depend.nil? || has?('outdated'))
428
+ !!@depend
429
+ end
430
+
431
+ def copy?
432
+ runnable?(@copy) || workspace.task_defined?(name, 'copy')
280
433
  end
281
434
 
282
435
  def doc?
@@ -287,34 +440,40 @@ module Squared
287
440
  !!@test
288
441
  end
289
442
 
290
- def copy?
291
- @copy.is_a?(::String)
292
- end
293
-
294
443
  def clean?
295
- !!@clean
444
+ runnable?(@clean) || workspace.task_defined?(name, 'clean')
296
445
  end
297
446
 
298
447
  def dev?
299
- !!@dev
448
+ @dev != false && workspace.dev?(pat: @dev, **scriptargs)
449
+ end
450
+
451
+ def prod?
452
+ @prod != false && workspace.prod?(pat: @prod, **scriptargs)
300
453
  end
301
454
 
302
455
  private
303
456
 
304
- def run(cmd = @session, exception: workspace.exception, banner: true, sync: true, req: nil, **)
457
+ def puts(*args)
458
+ puts_oe(*args, pipe: pipe)
459
+ end
460
+
461
+ def run(cmd = @session, var = nil, exception: @exception, sync: true, req: nil, banner: ARG[:BANNER], **)
305
462
  if req && !base_path(req).exist?
306
463
  log.warn "#{req} (not found)"
307
464
  return
308
465
  end
309
- cmd = close_session(cmd)
466
+ cmd = session_done(cmd)
310
467
  log.info cmd
311
468
  begin
312
469
  if cmd =~ /^\S+:(\S+:?)+$/ && workspace.task_defined?(cmd)
313
470
  print_item if sync
314
- invoke(cmd, exception: exception, warning: workspace.warning)
471
+ log.warn "ENV was discarded: #{var}" if var
472
+ invoke(cmd, exception: exception, warning: warning?)
315
473
  else
316
474
  print_item format_banner(cmd, banner: banner) if sync
317
- shell(cmd, chdir: path, exception: exception)
475
+ args = var.is_a?(::Hash) ? [var, cmd] : [cmd]
476
+ shell(*args, chdir: path, exception: exception)
318
477
  end
319
478
  rescue StandardError => e
320
479
  log.error e
@@ -322,42 +481,57 @@ module Squared
322
481
  end
323
482
  end
324
483
 
325
- def run_s(cmd, **kwargs)
326
- run(cmd, banner: verbose?, **kwargs) if cmd.is_a?(::String)
484
+ def run_s(*cmd, env: nil, **kwargs)
485
+ cmd.each { |val| run(val, env, banner: !!verbose, **kwargs) }
327
486
  end
328
487
 
329
- def env(key, default = nil, equals: nil, ignore: ['0'].freeze, suffix: nil, strict: false)
330
- @env ||= name.gsub(/[^\w]+/, '_').upcase
331
- a = "#{key}_#{@env}"
332
- b = ''
333
- if suffix
334
- a = [a, suffix].flatten.join('_')
335
- elsif !strict
336
- b = ENV.fetch(key, '')
337
- end
338
- ret = ENV.fetch(a, b)
488
+ def run_task(key)
489
+ return false unless workspace.task_defined?(key)
490
+
491
+ invoke(key, **workspace.invokeargs)
492
+ true
493
+ end
494
+
495
+ def env(key, default = nil, suffix: nil, equals: nil, ignore: nil, strict: false)
496
+ a = "#{key}_#{@envname}"
497
+ ret = if suffix
498
+ ENV.fetch("#{a}_#{suffix}", '')
499
+ else
500
+ ignore = ['0'].freeze if ignore.nil? && !strict
501
+ ENV[a] || (strict ? '' : ENV.fetch(key, ''))
502
+ end
339
503
  return ret == equals.to_s unless equals.nil?
340
504
 
341
- ret.empty? || as_a(ignore).any? { |val| ret == val.to_s } ? default : ret
505
+ ret.empty? || (ignore && as_a(ignore).any? { |val| ret == val.to_s }) ? default : ret
342
506
  end
343
507
 
344
- def session(*cmd, options: nil)
345
- if (val = ENV["#{(options || cmd.first).upcase}_OPTIONS"])
508
+ def session(*cmd, prefix: cmd.first)
509
+ if (val = env("#{prefix.upcase}_OPTIONS"))
346
510
  split_escape(val).each { |opt| cmd << fill_option(opt) }
347
511
  end
348
512
  @session = JoinSet.new(cmd)
349
513
  end
350
514
 
351
- def close_session(cmd)
352
- return cmd unless cmd.respond_to?(:done)
515
+ def session_done(cmd)
516
+ return cmd.to_s unless cmd.respond_to?(:done)
353
517
 
354
- raise_error('none were provided', hint: name) if cmd.empty?
518
+ raise_error('no args were added', hint: cmd.first || name) unless cmd.size > 1
355
519
  @session = nil if cmd == @session
356
520
  cmd.done
357
521
  end
358
522
 
523
+ def option(*args, prefix: @session&.first, **kwargs)
524
+ if prefix
525
+ args.each do |val|
526
+ ret = env("#{prefix}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
527
+ return ret if ret
528
+ end
529
+ end
530
+ nil
531
+ end
532
+
359
533
  def print_item(*val)
360
- puts unless @@print_order == 0 || pipe?
534
+ puts unless @@print_order == 0 || stdin?
361
535
  @@print_order += 1
362
536
  puts val unless val.empty? || (val.size == 1 && val.first.nil?)
363
537
  end
@@ -387,41 +561,64 @@ module Squared
387
561
  out.join("\n")
388
562
  end
389
563
 
390
- def print_footer(*lines, sub: nil, border: theme[:border], reverse: false)
564
+ def print_footer(*lines, sub: nil, reverse: false, right: false, **kwargs)
565
+ border = kwargs.key?(:border) ? kwargs[:border] : borderstyle
391
566
  n = Project.max_width(lines)
392
567
  sub = as_a(sub)
393
568
  lines.map! do |val|
394
- s = val.ljust(n)
569
+ s = right ? val.rjust(n) : val.ljust(n)
395
570
  sub.each { |h| s = sub_style(s, **h) }
396
571
  s
397
572
  end
398
- ret = [sub_style('-' * n, styles: border), *lines]
573
+ ret = [sub_style('-' * n, styles: border || theme[:border]), *lines]
399
574
  ret.reverse! if reverse
400
575
  ret.join("\n")
401
576
  end
402
577
 
403
- def format_desc(action, flag, opts = nil, req: '', arg: 'opts*')
578
+ def format_desc(action, flag, opts = nil, req: nil, arg: 'opts*')
404
579
  opts = "#{arg}=#{opts.join(',')}" if opts.is_a?(::Array)
405
- unless flag
580
+ val = [@desc]
581
+ if flag
582
+ val << action
583
+ else
406
584
  flag = action
407
- action = ''
408
585
  end
409
- req = opts ? "#{req}," : "[#{req}]" unless req.to_s.empty?
410
- message(*name.split(':'), action, opts ? "#{flag}[#{req}#{opts}]" : flag.to_s + req)
586
+ req = opts ? "#{req}," : "[#{req}]" if req
587
+ val << (opts ? "#{flag}[#{req}#{opts}]" : "#{flag}#{req}")
588
+ message(*val)
411
589
  end
412
590
 
413
- def format_banner(cmd, banner: true, multiple: false)
591
+ def format_banner(cmd, banner: ARG[:BANNER], multiple: false)
414
592
  return unless banner
415
593
 
416
- if (data = workspace.banner(group: group, ref: ref))
594
+ if (data = workspace.banner_get(*@ref, group: group))
417
595
  client = true
418
596
  else
419
597
  data = { command: true, order: %i[path], styles: theme[:banner], border: theme[:border] }
420
598
  end
421
- if verbose?
599
+ if verbose
422
600
  out = []
423
601
  out << cmd.sub(/^\S+/, &:upcase) if data[:command]
424
- data[:order].each { |val| out << val.to_s if (val = __send__(val)) }
602
+ data[:order].each do |val|
603
+ if val.is_a?(::Array)
604
+ s = ' '
605
+ found = false
606
+ val = val.map do |meth|
607
+ if meth.is_a?(::String)
608
+ s = ''
609
+ meth
610
+ elsif respond_to?(meth)
611
+ found = true
612
+ __send__(meth)
613
+ end
614
+ end
615
+ val = val.compact.join(s)
616
+ next unless found && !val.empty?
617
+ elsif (val = __send__(val)).nil?
618
+ next
619
+ end
620
+ out << val.to_s
621
+ end
425
622
  print_banner(*out, styles: data[:styles], border: data[:border], client: client)
426
623
  elsif multiple && workspace.series.multiple?
427
624
  "## #{__send__(data[:order].first || :path)} ##"
@@ -432,8 +629,20 @@ module Squared
432
629
  "#{msg}#{!always && (!obj || obj == 0 || obj.to_s.empty?) ? '' : message(hint: message(title, obj.to_s))}"
433
630
  end
434
631
 
435
- def append_nocolor
436
- @session << (!ENV.fetch('NO_COLOR', '').empty? || pipe? ? '--no-color' : '')
632
+ def append_repeat(flag, opts)
633
+ opts.each { |val| @session << "--#{flag}=#{shell_escape(val, quote: true)}" }
634
+ end
635
+
636
+ def append_value(opts)
637
+ opts.each { |val| @session << val }
638
+ end
639
+
640
+ def append_nocolor(flag = nil)
641
+ @session << '--no-color' if flag || !ARG[:COLOR] || stdin?
642
+ end
643
+
644
+ def task_join(*val)
645
+ val.join(':')
437
646
  end
438
647
 
439
648
  def guard_params(action, flag, args: nil, key: nil, pat: nil)
@@ -450,22 +659,11 @@ module Squared
450
659
  end
451
660
  end
452
661
 
453
- def store_pwd(done = nil)
454
- if @pwd
455
- Dir.chdir(@pwd)
456
- @pwd = nil
457
- elsif !done && Dir.pwd != path.to_s
458
- @pwd = Dir.pwd
459
- Dir.chdir(path)
460
- @pwd
461
- end
462
- end
463
-
464
662
  def semver(val)
465
- unless val[3]
466
- val[3] = '.'
467
- val[4] = '0'
468
- end
663
+ return val if val[3]
664
+
665
+ val[3] = '.'
666
+ val[4] = '0'
469
667
  unless val[1]
470
668
  val[1] = '.'
471
669
  val[2] = '0'
@@ -474,39 +672,122 @@ module Squared
474
672
  end
475
673
 
476
674
  def semmajor(cur, want)
477
- cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]
675
+ (cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]) && !want[5]
478
676
  end
479
677
 
480
- def verbose?
481
- workspace.verbose
678
+ def semscan(val)
679
+ val.scan(SEM_VER).first
482
680
  end
483
681
 
484
- def pipe?
485
- workspace.pipe
486
- end
682
+ def pwd_set(done = nil, &blk)
683
+ pwd = Pathname.pwd
684
+ if block_given?
685
+ if path == pwd
686
+ instance_eval(&blk)
687
+ else
688
+ Dir.chdir(path)
689
+ instance_eval(&blk)
690
+ Dir.chdir(pwd)
691
+ end
692
+ elsif @pwd == pwd
693
+ @pwd = nil
694
+ pwd unless done
695
+ elsif @pwd
696
+ return unless path == pwd
487
697
 
488
- def invoked_sync?(action, flag = nil)
489
- action = workspace.task_name(action)
490
- return true if !flag.nil? || workspace.series.sync?("#{action}:sync")
698
+ Dir.chdir(@pwd)
699
+ @pwd = nil
700
+ elsif !done && path != pwd
701
+ @pwd = pwd
702
+ Dir.chdir(path)
703
+ @pwd
704
+ end
705
+ end
491
706
 
492
- check = lambda do |val|
493
- if invoked?(val)
494
- !workspace.task_defined?("#{val}:sync")
495
- elsif workspace.series.sync?("#{val}:sync")
496
- true
707
+ def run_set(cmd, val = nil, opts: nil, **)
708
+ unless @output[1] == false && !@output[0].nil?
709
+ if opts == false
710
+ @output[1] = false
711
+ elsif opts && opts != true
712
+ @output[1] = opts
497
713
  end
498
714
  end
499
- if group
500
- ret = check.("#{action}:#{group}")
501
- return ret unless ret.nil?
715
+ unless @output[2] == false
716
+ if val.is_a?(::Hash)
717
+ @output[2] = val
718
+ elsif val == false
719
+ @output[2] = false
720
+ end
721
+ end
722
+ @output[0] = cmd
723
+ end
724
+
725
+ def script_set(cmd, prod: nil, **)
726
+ return if @output[1] == false && @output[0].nil?
727
+
728
+ @output[0] = nil
729
+ @output[1] = if @global && cmd.is_a?(::Array)
730
+ cmd[prod == true ? 1 : 0]
731
+ else
732
+ cmd
733
+ end
734
+ end
735
+
736
+ def source_path?(val)
737
+ Pathname.new(val).absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.match?(%r{^\.\.[/\\]})
738
+ end
739
+
740
+ def runnable?(val)
741
+ case val
742
+ when ::String
743
+ true
744
+ when ::Enumerable
745
+ !val.is_a?(::Hash)
746
+ else
747
+ false
748
+ end
749
+ end
750
+
751
+ def from_sync?(*val)
752
+ if invoked?(key = task_join(*val))
753
+ !workspace.task_defined?(key, 'sync')
754
+ elsif workspace.series.sync?(task_join(key, 'sync'))
755
+ true
502
756
  end
503
- if (base = workspace.find_base(self))
504
- ret = check.("#{action}:#{base.ref}")
505
- return ret unless ret.nil?
757
+ end
758
+
759
+ def invoked_sync?(action, val = nil)
760
+ return true if !val.nil? || from_sync?(ac = workspace.task_name(action))
761
+
762
+ return val if group && !(val = from_sync?(ac, group)).nil?
763
+ return val if (base = workspace.find_base(self)) && !(val = from_sync?(ac, base.ref)).nil?
764
+
765
+ return true if invoked?(name, ac) && (!invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
766
+
767
+ val = workspace.series.name_get(action)
768
+ val == action ? false : invoked_sync?(val)
769
+ end
770
+
771
+ def stdin?
772
+ pipe == 0
773
+ end
774
+
775
+ def warning?
776
+ workspace.warning
777
+ end
778
+
779
+ def borderstyle
780
+ if (data = workspace.banner_get(*@ref, group: group))
781
+ data[:border]
506
782
  end
507
- invoked?("#{name}:#{action}") && (!invoked?(action) || !workspace.task_defined?("#{action}:sync"))
783
+ end
784
+
785
+ def scriptargs
786
+ { target: script? ? @output[1] : @output[0], ref: ref, group: group, global: @global }
508
787
  end
509
788
  end
789
+
790
+ Application.base_project = Base
510
791
  end
511
792
  end
512
793
  end