squared 0.0.5 → 0.0.6

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.
@@ -1,552 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Squared
4
- module Repo
5
- class Workspace
6
- include Common
7
- include Format
8
- include System
9
- include Task
10
- include ::Rake::DSL
11
-
12
- TASK_NAME = {
13
- build: [],
14
- refresh: [],
15
- depend: [],
16
- outdated: [],
17
- doc: [],
18
- test: [],
19
- copy: [],
20
- clean: []
21
- }
22
- TASK_BASE = {}
23
- TASK_KEYS = TASK_NAME.keys.freeze
24
- private_constant :TASK_NAME, :TASK_BASE
25
-
26
- class << self
27
- def implement(*objs)
28
- objs.each do |obj|
29
- next unless obj < Project::Base
30
-
31
- project_kind.unshift(obj)
32
- obj.tasks&.each do |val|
33
- TASK_NAME[val] = []
34
- TASK_BASE[val] = obj
35
- end
36
- end
37
- end
38
-
39
- def find(path: nil, ref: nil)
40
- if ref.is_a?(::Symbol) || ref.is_a?(::String)
41
- ret = project_kind.find { |proj| proj.to_sym == ref.to_sym }
42
- return ret if ret
43
- end
44
- project_kind.find { |proj| proj.is_a?(path) } if path
45
- end
46
-
47
- def to_s
48
- super.to_s.match(/[^:]+$/)[0]
49
- end
50
-
51
- attr_reader :project_kind
52
- end
53
-
54
- @project_kind = []
55
-
56
- attr_reader :main, :root, :home, :series, :theme, :sync, :multiple, :parallel
57
- attr_accessor :manifest, :manifest_url, :exception, :pipe, :verbose, :warning
58
-
59
- def initialize(main, *, common: true, **)
60
- @main = main.to_s
61
- @home = if (val = env('REPO_HOME'))
62
- home = Pathname.new(val)
63
- if @main == home.basename.to_s
64
- @root = home.parent
65
- if home.exist?
66
- @root = nil unless home.directory?
67
- elsif !@root.exist?
68
- @root.mkpath
69
- elsif !empty?(@root)
70
- if repo_prompt(@root)
71
- @override = true
72
- else
73
- @root = nil
74
- end
75
- end
76
- end
77
- raise ArgumentError, message('REPO_HOME', val, hint: 'invalid') unless @root
78
-
79
- home.realdirpath
80
- elsif (val = env('REPO_ROOT'))
81
- @root = Pathname.new(val).realdirpath
82
- if !@root.exist?
83
- @root.mkpath
84
- elsif !empty?(@root)
85
- raise ArgumentError, message('REPO_ROOT', val, hint: 'exist') unless repo_prompt(@root)
86
-
87
- @override = true
88
- end
89
- @root.join(@main).realdirpath
90
- else
91
- empty?(pwd = Pathname.pwd) ? pwd.join(@main) : pwd
92
- end
93
- @root ||= @home.parent
94
- @series = TASK_NAME.dup
95
- @project = {}
96
- @script = {
97
- group: {},
98
- ref: {},
99
- build: nil,
100
- dev: nil,
101
- prod: nil
102
- }
103
- @exception = !env('PIPE_FAIL', ignore: '0').nil?
104
- @pipe = !env('PIPE_OUT', ignore: '0').nil?
105
- @verbose = !@pipe
106
- @warning = !empty?(@root, repo: false)
107
- @sync = []
108
- @multiple = []
109
- @parallel = []
110
- @theme = common && @verbose ? __get__(:theme)[:workspace] : {}
111
- end
112
-
113
- def build(**kwargs)
114
- return unless enabled?
115
-
116
- default = kwargs[:default]
117
- multi = env('REPO_SYNC', ignore: '0') ? [] : kwargs.fetch(:parallel, []).map!(&:to_sym)
118
-
119
- group = {}
120
- parent = {}
121
- incl = []
122
- @project.each do |name, proj|
123
- next unless proj.enabled?
124
-
125
- series.each do |key, items|
126
- target = "#{name}:#{key}"
127
- case key
128
- when :build, :refresh, :depend, :copy, :outdated, :doc, :test, :clean
129
- valid = proj.has?(key) || ::Rake::Task.task_defined?(target)
130
- next unless valid || (key == :refresh && series[:build].include?(target = "#{name}:build"))
131
- else
132
- next unless task_defined?(proj, key)
133
- end
134
- if proj.group
135
- incl << proj.group
136
- (group[:"#{key}:#{proj.group}"] ||= []).push(target)
137
- else
138
- items << target
139
- end
140
- next unless (base = find_base(proj))
141
-
142
- unless (id = base.to_sym.to_s) == proj.group
143
- incl << id
144
- (parent[:"#{key}:#{id}"] ||= []).push(target)
145
- end
146
- end
147
- proj.populate(**kwargs)
148
- end
149
- series.merge!(parent) if incl.uniq.size > 1
150
- series.merge!(group)
151
- series[:refresh].clear if series[:refresh].all? { |target| target.end_with?(':build') }
152
-
153
- if repo?
154
- branch = env('REPO_MANIFEST') || read_manifest
155
- target = branch || manifest
156
- stage = nil
157
- failfast = true
158
- cmd = []
159
- newline = ARGV.index { |val| val.start_with?('repo:') }.to_i > 0
160
- parse_opts = lambda do |args|
161
- collect_args(args, :opts).each do |val|
162
- case val
163
- when 'no-fail'
164
- failfast = false
165
- when 'force'
166
- cmd << '--force-checkout'
167
- when 'rebase'
168
- cmd << '--rebase'
169
- when 'detach'
170
- cmd << '--detach'
171
- when 'gc'
172
- cmd << '--auto-gc'
173
- when 'no-update'
174
- cmd << '--no-manifest-update'
175
- end
176
- end
177
- end
178
- status = lambda do |val, alt = nil|
179
- ver = branch || alt
180
- ver ? message('repo', val.sub('{0}', 'opts*=force,rebase,detach,gc,no-update,no-fail'), ver) : 'inactive'
181
- end
182
-
183
- namespace :repo do |repo|
184
- desc status.('all[{0}]')
185
- task :all, [:opts] do |_, args|
186
- parse_opts.(args)
187
- stage ||= :all
188
- repo[:sync].invoke
189
- @project.select do |_, proj|
190
- if proj.enabled?
191
- proj.depend
192
- proj.build?
193
- end
194
- end
195
- .each_value { |proj| proj.has?(:dev) ? proj.refresh : proj.build }
196
- end
197
-
198
- desc status.("init[manifest?=#{target},{0}]", target)
199
- task :init, [:manifest, :opts] do |_, args|
200
- parse_opts.(args)
201
- stage = :init
202
- puts if newline
203
- system("repo init -u #{manifest_url} -m #{args.manifest || target}.xml", chdir: root)
204
- repo[:all].invoke
205
- end
206
-
207
- desc status.('sync[{0}]')
208
- task :sync, [:opts] do |_, args|
209
- raise LoadError, message('repo is not initialized', 'rake repo:init') unless branch || stage == :init
210
-
211
- parse_opts.(args)
212
- cmd << "-j#{ENV.fetch('REPO_JOBS', ::Rake::CpuCounter.count)}"
213
- cmd << '--fail-fast' if failfast
214
- puts if newline && stage != :init
215
- begin
216
- shell("repo sync #{cmd.join(' ')}", chdir: root, exception: failfast)
217
- rescue StandardError => e
218
- emphasize(e, title: "rake stash repo:#{stage || 'sync'}")
219
- raise
220
- end
221
- end
222
- end
223
- end
224
-
225
- Workspace.project_kind.each { |obj| obj.populate(self, **kwargs) }
226
-
227
- if !series[:build].empty?
228
- init = [:depend, dev? && !series[:refresh].empty? ? :refresh : :build]
229
-
230
- task default: default || init[1]
231
-
232
- desc 'init'
233
- task init: init
234
- elsif default && !series[default].empty?
235
- task default: default
236
- end
237
-
238
- series.each do |key, items|
239
- next if items.empty?
240
-
241
- s = key.to_s
242
- if items.size > 1
243
- multiple << s
244
- unless (valid = multi.include?(key))
245
- group = key.to_s
246
- valid = multi.include?(group.split(':').first.to_sym) if group.include?(':')
247
- end
248
- if valid
249
- desc "#{key} (thread)"
250
- multitask key => items
251
- parallel << s
252
-
253
- desc "#{key} (sync)"
254
- task "#{key}:sync": items
255
- sync << "#{key}:sync"
256
- multiple << "#{key}:sync"
257
- next
258
- end
259
- end
260
- desc s
261
- task key => items
262
- end
263
-
264
- yield self if block_given?
265
- end
266
-
267
- def repo(url, manifest = 'latest', run: nil, dev: nil, prod: nil)
268
- @manifest_url = url
269
- @manifest = manifest
270
- script = case (val = env('REPO_BUILD'))
271
- when 'verbose'
272
- @verbose = true
273
- run ? "#{run}:verbose" : nil
274
- when 'silent'
275
- @verbose = false
276
- @warning = false
277
- run
278
- else
279
- val || run
280
- end
281
- case env('REPO_WARN')
282
- when '0'
283
- @warning = false
284
- when '1'
285
- @warning = true
286
- end
287
- return self unless script
288
-
289
- run(script, dev: bool_match(env('REPO_DEV'), dev), prod: bool_match(env('REPO_DEV'), prod))
290
- end
291
-
292
- def run(script, group: nil, ref: nil, **kwargs)
293
- if group || ref
294
- script_command :run, script, group, ref
295
- else
296
- @script[:build] = script
297
- @script[:dev] = kwargs[:dev]
298
- @script[:prod] = kwargs[:prod]
299
- self
300
- end
301
- end
302
-
303
- def depend(script, group: nil, ref: nil)
304
- script_command :depend, script, group, ref
305
- end
306
-
307
- def clean(script, group: nil, ref: nil)
308
- script_command :clean, script, group, ref
309
- end
310
-
311
- def doc(script, group: nil, ref: nil)
312
- script_command :doc, script, group, ref
313
- end
314
-
315
- def test(script, group: nil, ref: nil)
316
- script_command :test, script, group, ref
317
- end
318
-
319
- def add(name, path = nil, ref: nil, **kwargs)
320
- path = root_path((path || name).to_s)
321
- project = if !ref.is_a?(::Class)
322
- Workspace.find(path: path, ref: ref)
323
- elsif ref < Project::Base
324
- ref
325
- end
326
- instance = (project || Project::Base).new(name, path, self, **kwargs)
327
- @project[n = name.to_sym] = instance
328
- __get__(:project)[n] = instance unless kwargs[:private]
329
- self
330
- end
331
-
332
- def group(name, path, **kwargs)
333
- root_path(path).children.map do |ent|
334
- next unless (dir = Pathname.new(ent)).directory?
335
-
336
- index = 0
337
- basename = dir.basename.to_s.to_sym
338
- override = kwargs.delete(basename)
339
- while @project[basename]
340
- index += 1
341
- basename = :"#{basename}-#{index}"
342
- end
343
- [basename, dir, override]
344
- end
345
- .each do |basename, dir, override|
346
- opts = kwargs.dup
347
- opts.merge!(override) if override
348
- add(basename, dir, group: name, **opts)
349
- end
350
- self
351
- end
352
-
353
- def compose(name, &blk)
354
- namespace(name, &blk)
355
- self
356
- end
357
-
358
- def apply(&blk)
359
- instance_eval(&blk)
360
- self
361
- end
362
-
363
- def style(name, *args, target: nil, empty: false)
364
- data = nil
365
- if target
366
- as_a(target).each_with_index do |val, i|
367
- if i == 0
368
- break unless (data = __get__(:theme)[val.to_sym])
369
- else
370
- data = data[val.to_sym] ||= {}
371
- end
372
- end
373
- return unless data
374
- end
375
- apply_style(data || theme, name, *args, empty: empty)
376
- self
377
- end
378
-
379
- def script(group: nil, ref: nil)
380
- if group || ref
381
- (group && @script[:group][group.to_sym]) || (ref && @script[:ref][ref.to_sym])
382
- else
383
- @script[:build]
384
- end
385
- end
386
-
387
- def env(key, equals: nil, ignore: nil)
388
- ret = ENV.fetch("#{key}_#{main.gsub(/[^\w]/, '_').upcase}", ENV[key]).to_s
389
- return ret == equals.to_s if equals
390
-
391
- ret.empty? || as_a(ignore).any? { |val| ret == val.to_s } ? nil : ret
392
- end
393
-
394
- def read_manifest(*)
395
- require 'rexml/document'
396
- return unless (file = root_path('.repo/manifest.xml')).exist?
397
-
398
- doc = REXML::Document.new(file.read)
399
- doc.elements['manifest/include'].attributes['name']&.sub('.xml', '')
400
- end
401
-
402
- def root_path(*args)
403
- root.join(*args)
404
- end
405
-
406
- def home_path(*args)
407
- home.join(*args)
408
- end
409
-
410
- def find_base(obj)
411
- Workspace.project_kind.find { |proj| obj.instance_of?(proj) }
412
- end
413
-
414
- def bool_match(val, pat)
415
- case val
416
- when nil, ''
417
- pat.is_a?(::Regexp) ? pat : nil
418
- when '0'
419
- false
420
- when '1'
421
- true
422
- else
423
- Regexp.new(val)
424
- end
425
- end
426
-
427
- def to_s
428
- root.to_s
429
- end
430
-
431
- def inspect
432
- "#<#{self.class}: #{main} => #{self}>"
433
- end
434
-
435
- def enabled?
436
- repo? || @project.any? { |_, proj| proj.enabled? }
437
- end
438
-
439
- def repo?
440
- !manifest_url.nil? && (empty?(root) || @override)
441
- end
442
-
443
- def multiple?(val = nil)
444
- return multiple.include?(val) && invoked?(val) if val
445
-
446
- ::Rake::Task.tasks.any? { |item| item.already_invoked && multiple.include?(item.name) }
447
- end
448
-
449
- def sync?(val = nil)
450
- return sync.include?(val) && invoked?(val) if val
451
-
452
- ::Rake::Task.tasks.any? { |item| item.already_invoked && sync.include?(item.name) }
453
- end
454
-
455
- def parallel?(val = nil)
456
- return parallel.include?(val) && invoked?(val) if val
457
-
458
- ::Rake::Task.tasks.any? { |item| item.already_invoked && parallel.include?(item.name) }
459
- end
460
-
461
- def empty?(dir, repo: true)
462
- return true if dir.empty? || (repo && dir.join('.repo').directory?)
463
- return true if dir.children.size == 1 && dir.join(dir.children.first).to_s == __FILE__
464
-
465
- dir.to_s == root.to_s && !env('REPO_ROOT').nil? && root.children.none? do |path|
466
- path.directory? && !path.basename.to_s.start_with?('.') && path.to_s != home.to_s
467
- end
468
- end
469
-
470
- def task_defined?(obj, key)
471
- obj.is_a?(TASK_BASE[key])
472
- end
473
-
474
- def dev?(script: nil, pat: nil, global: nil)
475
- with?(:dev, script: script, pat: pat, global: global || (pat.nil? && global.nil?))
476
- end
477
-
478
- def prod?(script: nil, pat: nil, global: false)
479
- return false if global && (@script[:dev] == true || !@script[:prod])
480
-
481
- with?(:prod, script: script, pat: pat, global: global)
482
- end
483
-
484
- protected
485
-
486
- def confirm(msg, agree: 'Y', cancel: 'N', default: nil, attempts: 5, timeout: 15)
487
- require 'readline'
488
- require 'timeout'
489
- Timeout.timeout(ENV.fetch('REPO_TIMEOUT', timeout).to_i) do
490
- ret = false
491
- begin
492
- while (ch = Readline.readline(msg, true))
493
- case ch.chomp.upcase
494
- when agree
495
- ret = true
496
- break
497
- when cancel
498
- break
499
- when ''
500
- case default
501
- when agree
502
- ret = true
503
- break
504
- when cancel
505
- break
506
- end
507
- end
508
- attempts -= 1
509
- exit 1 unless attempts >= 0
510
- end
511
- rescue Interrupt
512
- puts
513
- exit 0
514
- else
515
- ret
516
- end
517
- end
518
- end
519
-
520
- private
521
-
522
- def script_command(task, val, group, ref)
523
- if group
524
- label = :group
525
- items = as_a(group)
526
- else
527
- label = :ref
528
- items = as_a(ref)
529
- end
530
- items.each { |name| (@script[label][name.to_sym] ||= {})[task] = val }
531
- self
532
- end
533
-
534
- def repo_prompt(path)
535
- return false unless path.directory?
536
-
537
- confirm("#{log_title(:warn)} \"#{sub_style(path, :bold)}\" is not empty. Continue with installation? [y/N] ",
538
- default: 'N')
539
- end
540
-
541
- def with?(state, script: nil, pat: nil, global: false)
542
- if global
543
- pat = @script[state] if pat.nil?
544
- script ||= @script[:build]
545
- end
546
- pat.is_a?(::Regexp) ? pat.match?(script) : pat == true
547
- end
548
- end
549
- end
550
- end
551
-
552
- Repo = Squared::Repo