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.
- checksums.yaml +4 -4
- data/README.ruby.md +3 -2
- data/lib/squared/common/base.rb +91 -0
- data/lib/squared/common/format.rb +14 -5
- data/lib/squared/common/shell.rb +7 -7
- data/lib/squared/common/system.rb +4 -1
- data/lib/squared/common/task.rb +5 -1
- data/lib/squared/common.rb +1 -78
- data/lib/squared/config.rb +7 -7
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +306 -0
- data/lib/squared/{repo → workspace}/project/base.rb +47 -41
- data/lib/squared/{repo → workspace}/project/git.rb +71 -53
- data/lib/squared/{repo → workspace}/project/node.rb +17 -18
- data/lib/squared/{repo → workspace}/project/python.rb +3 -3
- data/lib/squared/{repo → workspace}/project/ruby.rb +7 -8
- data/lib/squared/{repo → workspace}/project.rb +1 -1
- data/lib/squared/workspace/repo.rb +201 -0
- data/lib/squared/workspace/series.rb +125 -0
- data/lib/squared/{repo.rb → workspace.rb} +6 -3
- data/lib/squared.rb +9 -8
- metadata +13 -10
- data/lib/squared/repo/workspace.rb +0 -552
@@ -0,0 +1,306 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Squared
|
4
|
+
module Workspace
|
5
|
+
class Application
|
6
|
+
include Common
|
7
|
+
include Format
|
8
|
+
include ::Rake::DSL
|
9
|
+
|
10
|
+
TASK_BASE = {
|
11
|
+
build: [],
|
12
|
+
refresh: [],
|
13
|
+
depend: [],
|
14
|
+
outdated: [],
|
15
|
+
doc: [],
|
16
|
+
test: [],
|
17
|
+
copy: [],
|
18
|
+
clean: []
|
19
|
+
}
|
20
|
+
TASK_EXTEND = {}
|
21
|
+
WORKSPACE_KEYS = TASK_BASE.keys.freeze
|
22
|
+
private_constant :TASK_BASE, :TASK_EXTEND
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def implement(*objs)
|
26
|
+
objs.each do |obj|
|
27
|
+
next unless obj < Project::Base
|
28
|
+
|
29
|
+
project_kind.unshift(obj)
|
30
|
+
obj.tasks&.each do |val|
|
31
|
+
TASK_BASE[val] ||= []
|
32
|
+
(TASK_EXTEND[val] ||= []).push(obj)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def find(path: nil, ref: nil)
|
38
|
+
if ref.is_a?(::Symbol) || ref.is_a?(::String)
|
39
|
+
ret = project_kind.find { |proj| proj.to_sym == ref.to_sym }
|
40
|
+
return ret if ret
|
41
|
+
end
|
42
|
+
project_kind.find { |proj| proj.is_a?(path) } if path
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
super.to_s.match(/[^:]+$/)[0]
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader :project_kind
|
50
|
+
end
|
51
|
+
|
52
|
+
@project_kind = []
|
53
|
+
|
54
|
+
attr_reader :main, :root, :home, :series, :theme, :warning
|
55
|
+
attr_accessor :exception, :pipe, :verbose
|
56
|
+
|
57
|
+
def initialize(main, *, common: true, exception: false, pipe: false, verbose: nil, **)
|
58
|
+
@main = main.to_s
|
59
|
+
@home = Pathname.pwd
|
60
|
+
@root = @home.parent
|
61
|
+
@series = Series.new(TASK_BASE, self)
|
62
|
+
@project = {}
|
63
|
+
@extensions = []
|
64
|
+
@script = {
|
65
|
+
group: {},
|
66
|
+
ref: {},
|
67
|
+
build: nil,
|
68
|
+
dev: nil,
|
69
|
+
prod: nil
|
70
|
+
}
|
71
|
+
@theme = common && @verbose ? __get__(:theme)[:workspace] : {}
|
72
|
+
@exception = exception.is_a?(String) ? !env(exception, ignore: '0').nil? : exception
|
73
|
+
@pipe = pipe.is_a?(String) ? !env(pipe, ignore: '0').nil? : pipe
|
74
|
+
@verbose = verbose.nil? ? !@pipe : verbose
|
75
|
+
@warning = true
|
76
|
+
end
|
77
|
+
|
78
|
+
def build(**kwargs)
|
79
|
+
return unless enabled?
|
80
|
+
|
81
|
+
@project.each_value do |proj|
|
82
|
+
next unless proj.enabled?
|
83
|
+
|
84
|
+
series.populate(proj)
|
85
|
+
proj.populate(**kwargs)
|
86
|
+
end
|
87
|
+
|
88
|
+
Application.project_kind.each { |obj| obj.populate(self, **kwargs) }
|
89
|
+
@extensions.each { |ext| __send__(ext, **kwargs) }
|
90
|
+
|
91
|
+
series[:refresh].clear if series[:refresh].all? { |val| val.end_with?(':build') }
|
92
|
+
|
93
|
+
task default: kwargs[:default] if series.has?(kwargs[:default]) && !task_defined?('default')
|
94
|
+
|
95
|
+
if series.has?('build')
|
96
|
+
init = ['depend', dev? && series.has?('refresh') ? 'refresh' : 'build']
|
97
|
+
|
98
|
+
task default: init[1] unless task_defined?('default')
|
99
|
+
|
100
|
+
if series.has?(init[0]) && !task_defined?('init')
|
101
|
+
desc 'init'
|
102
|
+
task init: init
|
103
|
+
end
|
104
|
+
end
|
105
|
+
series.finalize!(kwargs.fetch(:parallel, []))
|
106
|
+
|
107
|
+
yield self if block_given?
|
108
|
+
end
|
109
|
+
|
110
|
+
def run(script, group: nil, ref: nil)
|
111
|
+
script_command :run, script, group, ref
|
112
|
+
end
|
113
|
+
|
114
|
+
def depend(script, group: nil, ref: nil)
|
115
|
+
script_command :depend, script, group, ref
|
116
|
+
end
|
117
|
+
|
118
|
+
def clean(script, group: nil, ref: nil)
|
119
|
+
script_command :clean, script, group, ref
|
120
|
+
end
|
121
|
+
|
122
|
+
def doc(script, group: nil, ref: nil)
|
123
|
+
script_command :doc, script, group, ref
|
124
|
+
end
|
125
|
+
|
126
|
+
def test(script, group: nil, ref: nil)
|
127
|
+
script_command :test, script, group, ref
|
128
|
+
end
|
129
|
+
|
130
|
+
def add(name, path = nil, ref: nil, **kwargs)
|
131
|
+
path = root_path((path || name).to_s)
|
132
|
+
project = if !ref.is_a?(::Class)
|
133
|
+
Application.find(path: path, ref: ref)
|
134
|
+
elsif ref < Project::Base
|
135
|
+
ref
|
136
|
+
end
|
137
|
+
instance = (project || Project::Base).new(name, path, self, **kwargs)
|
138
|
+
@project[n = name.to_sym] = instance
|
139
|
+
__get__(:project)[n] = instance unless kwargs[:private]
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def group(name, path, **kwargs)
|
144
|
+
root_path(path).children.map do |ent|
|
145
|
+
next unless (dir = Pathname.new(ent)).directory?
|
146
|
+
|
147
|
+
index = 0
|
148
|
+
basename = dir.basename.to_s.to_sym
|
149
|
+
override = kwargs.delete(basename)
|
150
|
+
while @project[basename]
|
151
|
+
index += 1
|
152
|
+
basename = :"#{basename}-#{index}"
|
153
|
+
end
|
154
|
+
[basename, dir, override]
|
155
|
+
end
|
156
|
+
.each do |basename, dir, override|
|
157
|
+
opts = kwargs.dup
|
158
|
+
opts.merge!(override) if override
|
159
|
+
add(basename, dir, group: name, **opts)
|
160
|
+
end
|
161
|
+
self
|
162
|
+
end
|
163
|
+
|
164
|
+
def compose(name, &blk)
|
165
|
+
namespace(name, &blk)
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
def apply(&blk)
|
170
|
+
instance_eval(&blk)
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
def style(name, *args, target: nil, empty: false)
|
175
|
+
data = nil
|
176
|
+
if target
|
177
|
+
as_a(target).each_with_index do |val, i|
|
178
|
+
if i == 0
|
179
|
+
break unless (data = __get__(:theme)[val.to_sym])
|
180
|
+
else
|
181
|
+
data = data[val.to_sym] ||= {}
|
182
|
+
end
|
183
|
+
end
|
184
|
+
return unless data
|
185
|
+
end
|
186
|
+
apply_style(data || theme, name, *args, empty: empty)
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
def script(group: nil, ref: nil)
|
191
|
+
if group || ref
|
192
|
+
(group && @script[:group][group.to_sym]) || (ref && @script[:ref][ref.to_sym])
|
193
|
+
else
|
194
|
+
@script[:build]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def env(key, equals: nil, ignore: nil, default: nil)
|
199
|
+
ret = ENV.fetch("#{key}_#{main.gsub(/[^\w]/, '_').upcase}", ENV[key]).to_s
|
200
|
+
return ret == equals.to_s unless equals.nil?
|
201
|
+
|
202
|
+
ret.empty? || (ignore && as_a(ignore).any? { |val| ret == val.to_s }) ? default : ret
|
203
|
+
end
|
204
|
+
|
205
|
+
def root_path(*args)
|
206
|
+
root.join(*args)
|
207
|
+
end
|
208
|
+
|
209
|
+
def home_path(*args)
|
210
|
+
home.join(*args)
|
211
|
+
end
|
212
|
+
|
213
|
+
def find_base(obj)
|
214
|
+
Application.project_kind.find { |proj| obj.instance_of?(proj) }
|
215
|
+
end
|
216
|
+
|
217
|
+
def to_s
|
218
|
+
root.to_s
|
219
|
+
end
|
220
|
+
|
221
|
+
def inspect
|
222
|
+
"#<#{self.class}: #{main} => #{self}>"
|
223
|
+
end
|
224
|
+
|
225
|
+
def enabled?
|
226
|
+
!@extensions.empty? || @project.any? { |_, proj| proj.enabled? }
|
227
|
+
end
|
228
|
+
|
229
|
+
def task_base?(key, val)
|
230
|
+
return val if task_defined?(key)
|
231
|
+
|
232
|
+
val if key == :refresh && series[:build].include?(val = "#{val.split(':').first}:build")
|
233
|
+
end
|
234
|
+
|
235
|
+
def task_include?(obj, key)
|
236
|
+
TASK_EXTEND.fetch(key, []).any? { |item| obj.is_a?(item) }
|
237
|
+
end
|
238
|
+
|
239
|
+
def task_defined?(key)
|
240
|
+
::Rake::Task.task_defined?(key)
|
241
|
+
end
|
242
|
+
|
243
|
+
def dev?(script: nil, pat: nil, global: nil)
|
244
|
+
with?(:dev, script: script, pat: pat, global: global || (pat.nil? && global.nil?))
|
245
|
+
end
|
246
|
+
|
247
|
+
def prod?(script: nil, pat: nil, global: false)
|
248
|
+
return false if global && (@script[:dev] == true || !@script[:prod])
|
249
|
+
|
250
|
+
with?(:prod, script: script, pat: pat, global: global)
|
251
|
+
end
|
252
|
+
|
253
|
+
protected
|
254
|
+
|
255
|
+
def confirm(msg, agree: 'Y', cancel: 'N', default: nil, attempts: 5, timeout: 15)
|
256
|
+
require 'readline'
|
257
|
+
require 'timeout'
|
258
|
+
agree = /^#{agree}$/i if agree.is_a?(String)
|
259
|
+
cancel = /^#{cancel}$/i if cancel.is_a?(String)
|
260
|
+
Timeout.timeout(timeout) do
|
261
|
+
begin
|
262
|
+
while (ch = Readline.readline(msg, true))
|
263
|
+
ch = ch.chomp
|
264
|
+
ch = default if ch.empty?
|
265
|
+
case ch
|
266
|
+
when agree
|
267
|
+
return true
|
268
|
+
when cancel
|
269
|
+
return false
|
270
|
+
end
|
271
|
+
attempts -= 1
|
272
|
+
exit 1 unless attempts >= 0
|
273
|
+
end
|
274
|
+
rescue Interrupt
|
275
|
+
puts
|
276
|
+
exit 0
|
277
|
+
else
|
278
|
+
false
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
def script_command(task, val, group, ref)
|
286
|
+
if group
|
287
|
+
label = :group
|
288
|
+
items = as_a(group)
|
289
|
+
else
|
290
|
+
label = :ref
|
291
|
+
items = as_a(ref)
|
292
|
+
end
|
293
|
+
items.each { |name| (@script[label][name.to_sym] ||= {})[task] = val }
|
294
|
+
self
|
295
|
+
end
|
296
|
+
|
297
|
+
def with?(state, script: nil, pat: nil, global: false)
|
298
|
+
if global
|
299
|
+
pat = @script[state] if pat.nil?
|
300
|
+
script ||= @script[:build]
|
301
|
+
end
|
302
|
+
pat.is_a?(::Regexp) ? pat.match?(script) : pat == true
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'date'
|
4
|
+
require 'logger'
|
4
5
|
|
5
6
|
module Squared
|
6
|
-
module
|
7
|
+
module Workspace
|
7
8
|
module Project
|
8
9
|
class Base
|
9
10
|
include Common
|
@@ -12,6 +13,8 @@ module Squared
|
|
12
13
|
include Task
|
13
14
|
include ::Rake::DSL
|
14
15
|
|
16
|
+
SEM_VER = /^(\d+)(\.)(\d+)(?:(\.)(\d+))?$/.freeze
|
17
|
+
|
15
18
|
class << self
|
16
19
|
include Common::Task
|
17
20
|
include ::Rake::DSL
|
@@ -23,9 +26,12 @@ module Squared
|
|
23
26
|
end
|
24
27
|
|
25
28
|
def as_path(val)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
case val
|
30
|
+
when ::Pathname
|
31
|
+
val
|
32
|
+
when ::String
|
33
|
+
Pathname.new(val)
|
34
|
+
end
|
29
35
|
end
|
30
36
|
|
31
37
|
def to_s
|
@@ -117,7 +123,7 @@ module Squared
|
|
117
123
|
def populate(*)
|
118
124
|
namespace name do
|
119
125
|
workspace.series.each_key do |key|
|
120
|
-
next unless
|
126
|
+
next unless Application::WORKSPACE_KEYS.include?(key) ? has?(key) : workspace.task_include?(self, key)
|
121
127
|
|
122
128
|
desc message(name, key)
|
123
129
|
task key do
|
@@ -127,7 +133,7 @@ module Squared
|
|
127
133
|
end
|
128
134
|
end
|
129
135
|
|
130
|
-
def build(*args)
|
136
|
+
def build(*args, sync: true)
|
131
137
|
if args.empty?
|
132
138
|
cmd, opts = @output
|
133
139
|
opts &&= shell_escape(opts)
|
@@ -148,13 +154,13 @@ module Squared
|
|
148
154
|
cmd = compose(opts)
|
149
155
|
banner = env('REPO_BUILD') == 'verbose'
|
150
156
|
end
|
151
|
-
run(cmd, exception: workspace.exception, banner: banner)
|
157
|
+
run(cmd, exception: workspace.exception, banner: banner, sync: sync)
|
152
158
|
end
|
153
159
|
|
154
160
|
def refresh(*)
|
155
|
-
build
|
161
|
+
build(sync: invoked_sync?('depend'))
|
156
162
|
key = "#{name}:copy"
|
157
|
-
if
|
163
|
+
if workspace.task_defined?(key)
|
158
164
|
invoke key
|
159
165
|
else
|
160
166
|
copy
|
@@ -162,29 +168,30 @@ module Squared
|
|
162
168
|
end
|
163
169
|
|
164
170
|
def depend(*)
|
165
|
-
run(@depend, exception: workspace.exception) if @depend
|
171
|
+
run(@depend, exception: workspace.exception, sync: invoked_sync?('depend')) if @depend
|
166
172
|
end
|
167
173
|
|
168
174
|
def copy(*)
|
169
|
-
run_s
|
175
|
+
run_s(@copy, sync: invoked_sync?('copy'))
|
170
176
|
end
|
171
177
|
|
172
178
|
def doc
|
173
|
-
build
|
179
|
+
build(@doc, sync: invoked_sync?('doc')) if @doc
|
174
180
|
end
|
175
181
|
|
176
182
|
def test
|
177
|
-
build
|
183
|
+
build(@test, sync: invoked_sync?('test')) if @test
|
178
184
|
end
|
179
185
|
|
180
186
|
def clean
|
181
187
|
return unless @clean
|
182
188
|
|
183
189
|
if @clean.is_a?(::String)
|
184
|
-
run_s
|
190
|
+
run_s(@clean, sync: invoked_sync?('clean'))
|
185
191
|
else
|
186
192
|
@clean.each do |val|
|
187
|
-
|
193
|
+
val = val.to_s
|
194
|
+
if val =~ %r{[\\/]$}
|
188
195
|
dir = Pathname.new(val)
|
189
196
|
dir = base_path(dir) unless dir.absolute?
|
190
197
|
next unless dir.directory?
|
@@ -234,7 +241,7 @@ module Squared
|
|
234
241
|
end
|
235
242
|
|
236
243
|
def refresh?
|
237
|
-
build? && (copy? ||
|
244
|
+
build? && (copy? || workspace.task_defined?("#{name}:copy"))
|
238
245
|
end
|
239
246
|
|
240
247
|
def depend?
|
@@ -259,12 +266,12 @@ module Squared
|
|
259
266
|
|
260
267
|
protected
|
261
268
|
|
262
|
-
def run(cmd = @session, exception: false, banner: true, req: nil)
|
269
|
+
def run(cmd = @session, exception: false, banner: true, sync: true, req: nil)
|
263
270
|
return if req && !base_path(req).exist?
|
264
271
|
|
265
272
|
cmd = close_session(cmd)
|
266
273
|
log.info cmd
|
267
|
-
print_item format_banner(cmd, banner: banner)
|
274
|
+
print_item format_banner(cmd, banner: banner) if sync
|
268
275
|
begin
|
269
276
|
shell(cmd, chdir: path, exception: exception)
|
270
277
|
rescue StandardError => e
|
@@ -273,22 +280,22 @@ module Squared
|
|
273
280
|
end
|
274
281
|
end
|
275
282
|
|
276
|
-
def run_s(cmd)
|
277
|
-
run(cmd, exception: workspace.exception, banner: verbose
|
283
|
+
def run_s(cmd, **kwargs)
|
284
|
+
run(cmd, exception: workspace.exception, banner: verbose?, **kwargs) if cmd.is_a?(::String)
|
278
285
|
end
|
279
286
|
|
280
|
-
def env(key,
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
if !equals.nil?
|
288
|
-
ret == equals.to_s
|
289
|
-
elsif !ret.empty? && ret != '0'
|
290
|
-
ret
|
287
|
+
def env(key, equals: nil, ignore: ['0'].freeze, default: nil, suffix: nil, strict: false)
|
288
|
+
a = "#{key}_#{name.upcase}"
|
289
|
+
b = ''
|
290
|
+
if suffix
|
291
|
+
a = [a, suffix].flatten.join('_')
|
292
|
+
elsif !strict
|
293
|
+
b = ENV.fetch(key, '')
|
291
294
|
end
|
295
|
+
ret = ENV.fetch(a, b)
|
296
|
+
return ret == equals.to_s unless equals.nil?
|
297
|
+
|
298
|
+
ret.empty? || as_a(ignore).any? { |val| ret == val.to_s } ? default : ret
|
292
299
|
end
|
293
300
|
|
294
301
|
def session(*cmd, options: nil)
|
@@ -300,8 +307,8 @@ module Squared
|
|
300
307
|
|
301
308
|
def close_session(cmd)
|
302
309
|
return cmd unless cmd.respond_to?(:done)
|
303
|
-
raise ArgumentError, message('none were provided', hint: name) if cmd.empty?
|
304
310
|
|
311
|
+
raise_error('none were provided', hint: name) if cmd.empty?
|
305
312
|
@session = nil if cmd == @session
|
306
313
|
cmd.done
|
307
314
|
end
|
@@ -362,7 +369,7 @@ module Squared
|
|
362
369
|
|
363
370
|
if verbose?
|
364
371
|
print_banner(cmd.sub(/^\S+/, &:upcase), path.to_s)
|
365
|
-
elsif multiple && workspace.multiple?
|
372
|
+
elsif multiple && workspace.series.multiple?
|
366
373
|
"## #{path} ##"
|
367
374
|
end
|
368
375
|
end
|
@@ -380,17 +387,17 @@ module Squared
|
|
380
387
|
return unless (val = args[key]).nil? || (pat && !val.match?(pat))
|
381
388
|
|
382
389
|
@session = nil
|
383
|
-
|
390
|
+
raise_error(action, "#{flag}[#{key}]", hint: val.nil? ? 'missing' : 'invalid')
|
384
391
|
elsif args.is_a?(::Array)
|
385
392
|
return unless args.empty?
|
386
393
|
|
387
394
|
@session = nil
|
388
|
-
|
395
|
+
raise_error(action, "#{flag}[]", hint: 'empty')
|
389
396
|
end
|
390
397
|
return unless action.to_s.empty?
|
391
398
|
|
392
399
|
@session = nil
|
393
|
-
|
400
|
+
raise_error('parameter', flag, hint: 'missing')
|
394
401
|
end
|
395
402
|
|
396
403
|
def verbose?
|
@@ -402,13 +409,12 @@ module Squared
|
|
402
409
|
end
|
403
410
|
|
404
411
|
def invoked_sync?(action)
|
405
|
-
return true if workspace.sync?("#{action}:sync")
|
412
|
+
return true if workspace.series.sync?("#{action}:sync")
|
406
413
|
|
407
|
-
missing = ->(val) { ::Rake::Task.tasks.none? { |item| item.name == val } }
|
408
414
|
check = lambda do |val|
|
409
415
|
if invoked?(val)
|
410
|
-
|
411
|
-
elsif workspace.sync?("#{val}:sync")
|
416
|
+
!workspace.task_defined?("#{val}:sync")
|
417
|
+
elsif workspace.series.sync?("#{val}:sync")
|
412
418
|
true
|
413
419
|
end
|
414
420
|
end
|
@@ -420,7 +426,7 @@ module Squared
|
|
420
426
|
ret = check.("#{action}:#{base.to_sym}")
|
421
427
|
return ret unless ret.nil?
|
422
428
|
end
|
423
|
-
invoked?("#{name}:#{action}") && (!invoked?(action) ||
|
429
|
+
invoked?("#{name}:#{action}") && (!invoked?(action) || !workspace.task_defined?("#{action}:sync"))
|
424
430
|
end
|
425
431
|
|
426
432
|
private
|