squared 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +11 -0
- data/README.md +1347 -0
- data/README.ruby.md +50 -0
- data/lib/squared/common/class.rb +18 -0
- data/lib/squared/common/format.rb +151 -0
- data/lib/squared/common/shell.rb +40 -0
- data/lib/squared/common/system.rb +55 -0
- data/lib/squared/common/task.rb +27 -0
- data/lib/squared/common.rb +66 -0
- data/lib/squared/config.rb +240 -0
- data/lib/squared/repo/project/base.rb +357 -0
- data/lib/squared/repo/project/git.rb +521 -0
- data/lib/squared/repo/project/node.rb +394 -0
- data/lib/squared/repo/project/python.rb +104 -0
- data/lib/squared/repo/project/ruby.rb +277 -0
- data/lib/squared/repo/project.rb +49 -0
- data/lib/squared/repo/workspace.rb +427 -0
- data/lib/squared/repo.rb +39 -0
- data/lib/squared/version.rb +5 -0
- data/lib/squared.rb +13 -0
- data/squared.gemspec +29 -0
- metadata +95 -0
@@ -0,0 +1,357 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Squared
|
6
|
+
module Repo
|
7
|
+
module Project
|
8
|
+
class Base
|
9
|
+
include Common
|
10
|
+
include Shell
|
11
|
+
include System
|
12
|
+
include Task
|
13
|
+
include ::Rake::DSL
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def tasks
|
18
|
+
[].freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
to_sym.to_s.capitalize
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_sym
|
26
|
+
:base
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@@print_order = 0
|
31
|
+
@@tasks = {}
|
32
|
+
|
33
|
+
attr_reader :name, :project, :workspace, :group, :path, :logger
|
34
|
+
|
35
|
+
protected :logger
|
36
|
+
|
37
|
+
def_delegators :logger, :log, :level=, :<<, :debug, :info, :warn, :error, :fatal, :unknown
|
38
|
+
|
39
|
+
def initialize(name, project, workspace, **kwargs)
|
40
|
+
@name = name.to_s
|
41
|
+
@path = workspace.root_path(project.to_s)
|
42
|
+
@project = @path.basename.to_s
|
43
|
+
@workspace = workspace
|
44
|
+
@depend = kwargs[:depend]
|
45
|
+
@doc = kwargs[:doc]
|
46
|
+
@group = kwargs[:group]&.to_s
|
47
|
+
run = kwargs[:run]
|
48
|
+
opts = env('BUILD', strict: true)
|
49
|
+
raise ArgumentError, message("BUILD_#{name.to_s.upcase}", opts) if opts && run.is_a?(::Array)
|
50
|
+
|
51
|
+
@output = [run, opts]
|
52
|
+
@copy = kwargs[:copy]
|
53
|
+
@clean = kwargs[:clean]
|
54
|
+
log = kwargs[:log]
|
55
|
+
log = { file: log } unless log.is_a?(::Hash)
|
56
|
+
if (logfile = env('LOG_FILE')).nil? && (auto = env('LOG_AUTO'))
|
57
|
+
require 'date'
|
58
|
+
logfile = case auto
|
59
|
+
when 'y', 'year'
|
60
|
+
"#{name}-#{Date.today.year}.log"
|
61
|
+
when 'm', 'month'
|
62
|
+
"#{name}-#{Date.today.strftime('%Y-%m')}.log"
|
63
|
+
when 'd', 'day', '1'
|
64
|
+
"#{name}-#{Date.today}.log"
|
65
|
+
else
|
66
|
+
log[:file]
|
67
|
+
end
|
68
|
+
else
|
69
|
+
logfile = log[:file]
|
70
|
+
end
|
71
|
+
if (dir = env('LOG_DIR'))
|
72
|
+
logfile &&= Pathname.new(dir).join(logfile)
|
73
|
+
end
|
74
|
+
@logger = Logger.new(logfile, level: env('LOG_LEVEL') || log[:level] || Logger::INFO)
|
75
|
+
@logger.progname = name
|
76
|
+
@session = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def populate(*)
|
80
|
+
namespace @name do
|
81
|
+
@workspace.series.each_key do |key|
|
82
|
+
case key
|
83
|
+
when :build, :refresh, :depend, :outdated, :doc, :copy, :clean
|
84
|
+
next unless has?(key)
|
85
|
+
else
|
86
|
+
next unless @workspace.task_defined?(self, key)
|
87
|
+
end
|
88
|
+
desc message(@name, key)
|
89
|
+
task key do
|
90
|
+
send(key)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def build(*args)
|
97
|
+
if args.empty?
|
98
|
+
cmd, opts = @output
|
99
|
+
opts &&= shell_escape(opts)
|
100
|
+
else
|
101
|
+
cmd = args.shift
|
102
|
+
opts = sanitize_args(*args)
|
103
|
+
end
|
104
|
+
if cmd
|
105
|
+
if opts
|
106
|
+
cmd = "#{cmd} #{opts}"
|
107
|
+
elsif cmd.is_a?(::Array)
|
108
|
+
cmd = cmd.join(' && ')
|
109
|
+
end
|
110
|
+
banner = verbose?
|
111
|
+
else
|
112
|
+
return unless respond_to?(:compose)
|
113
|
+
|
114
|
+
cmd = compose(opts)
|
115
|
+
banner = env('REPO_BUILD') == 'verbose'
|
116
|
+
end
|
117
|
+
run(cmd, exception: @workspace.exception, banner: banner)
|
118
|
+
end
|
119
|
+
|
120
|
+
def refresh(*)
|
121
|
+
build
|
122
|
+
key = "#{@name}:copy"
|
123
|
+
if ::Rake::Task.task_defined?(key)
|
124
|
+
invoke key
|
125
|
+
else
|
126
|
+
copy
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def depend(*)
|
131
|
+
run(@depend, exception: @workspace.exception) if @depend
|
132
|
+
end
|
133
|
+
|
134
|
+
def doc
|
135
|
+
build @doc if @doc
|
136
|
+
end
|
137
|
+
|
138
|
+
def copy(*)
|
139
|
+
run_s @copy
|
140
|
+
end
|
141
|
+
|
142
|
+
def clean
|
143
|
+
return run_s @clean if @clean.is_a?(::String)
|
144
|
+
|
145
|
+
@clean&.each do |path|
|
146
|
+
path = path.to_s
|
147
|
+
if %r{[\\/]$}.match?(path)
|
148
|
+
dir = Pathname.new(path)
|
149
|
+
dir = base_path(dir) unless dir.absolute?
|
150
|
+
next unless dir.directory?
|
151
|
+
|
152
|
+
warn "rm -rf #{dir}"
|
153
|
+
dir.rmtree(verbose: true)
|
154
|
+
else
|
155
|
+
files = path.include?('*') ? Dir[base_path(path)] : [base_path(path)]
|
156
|
+
files.each do |val|
|
157
|
+
begin
|
158
|
+
File.delete(val) if File.file?(val)
|
159
|
+
rescue StandardError => e
|
160
|
+
warn e
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def styles
|
168
|
+
@workspace.styles
|
169
|
+
end
|
170
|
+
|
171
|
+
def base_path(*args)
|
172
|
+
@path.join(*args)
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_s
|
176
|
+
@path.to_s
|
177
|
+
end
|
178
|
+
|
179
|
+
def to_sym
|
180
|
+
@name.to_sym
|
181
|
+
end
|
182
|
+
|
183
|
+
def enabled?
|
184
|
+
@path.directory? && !@path.empty?
|
185
|
+
end
|
186
|
+
|
187
|
+
def has?(method)
|
188
|
+
respond_to?(m = :"#{method}?") && send(m)
|
189
|
+
end
|
190
|
+
|
191
|
+
def build?
|
192
|
+
return false if @output[0] == false
|
193
|
+
|
194
|
+
!doc? || !@output[0].nil?
|
195
|
+
end
|
196
|
+
|
197
|
+
def refresh?
|
198
|
+
build?
|
199
|
+
end
|
200
|
+
|
201
|
+
def depend?
|
202
|
+
!@depend.nil?
|
203
|
+
end
|
204
|
+
|
205
|
+
def doc?
|
206
|
+
!@doc.nil?
|
207
|
+
end
|
208
|
+
|
209
|
+
def copy?
|
210
|
+
@copy.is_a?(::String)
|
211
|
+
end
|
212
|
+
|
213
|
+
def clean?
|
214
|
+
!@clean.nil?
|
215
|
+
end
|
216
|
+
|
217
|
+
def verbose?
|
218
|
+
@workspace.verbose && !pipe?
|
219
|
+
end
|
220
|
+
|
221
|
+
protected
|
222
|
+
|
223
|
+
def run(cmd = @session, exception: false, banner: true, req: nil)
|
224
|
+
return if req && !base_path(req).exist?
|
225
|
+
|
226
|
+
cmd = close_session(cmd)
|
227
|
+
info cmd
|
228
|
+
print_item format_banner(cmd, banner: banner)
|
229
|
+
begin
|
230
|
+
shell(cmd, chdir: @path, exception: exception)
|
231
|
+
rescue StandardError => e
|
232
|
+
error e
|
233
|
+
raise
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def run_s(cmd)
|
238
|
+
run(cmd, exception: @workspace.exception, banner: verbose?) if cmd.is_a?(::String)
|
239
|
+
end
|
240
|
+
|
241
|
+
def env(key, suffix: nil, equals: nil, strict: false)
|
242
|
+
val = "#{key}_#{@name.upcase}"
|
243
|
+
ret = if suffix
|
244
|
+
ENV.fetch(([val] + as_a(suffix)).join('_'), '')
|
245
|
+
else
|
246
|
+
ENV.fetch(val, strict ? '' : ENV[key]).to_s
|
247
|
+
end
|
248
|
+
return ret == equals.to_s unless equals.nil?
|
249
|
+
|
250
|
+
ret unless ret.empty? || ret == '0'
|
251
|
+
end
|
252
|
+
|
253
|
+
def session(*cmd, options: nil)
|
254
|
+
if (val = ENV["#{(options || cmd.first).upcase}_OPTIONS"])
|
255
|
+
split_escape(val).each do |opt|
|
256
|
+
cmd << (if opt.start_with?('-')
|
257
|
+
shell_escape(opt).sub('\\=', '=')
|
258
|
+
else
|
259
|
+
opt.size == 1 || /^[a-z]\d+$/i.match?(opt) ? "-#{opt}" : "--#{opt}"
|
260
|
+
end)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
@session = JoinSet.new(cmd)
|
264
|
+
end
|
265
|
+
|
266
|
+
def close_session(cmd)
|
267
|
+
return cmd unless cmd.respond_to?(:done)
|
268
|
+
raise ArgumentError, message('none were provided', hint: @name) if cmd.empty?
|
269
|
+
|
270
|
+
@session = nil if cmd == @session
|
271
|
+
cmd.done
|
272
|
+
end
|
273
|
+
|
274
|
+
def print_item(*val)
|
275
|
+
puts unless @@print_order == 0 || pipe?
|
276
|
+
@@print_order += 1
|
277
|
+
puts val unless val.empty? || (val.size == 1 && val.first.nil?)
|
278
|
+
end
|
279
|
+
|
280
|
+
def format_desc(action, flag, opts = nil, req: 'opts*')
|
281
|
+
opts = "#{req}=#{opts.join(',')}" if opts.is_a?(::Array)
|
282
|
+
message(@name, action, opts ? "#{flag}[#{opts}]" : flag)
|
283
|
+
end
|
284
|
+
|
285
|
+
def format_banner(cmd, banner: true, multitask: false)
|
286
|
+
return unless banner
|
287
|
+
|
288
|
+
if verbose?
|
289
|
+
pad = 0
|
290
|
+
if (args = styles[:banner])
|
291
|
+
if args.any? { |s| s.to_s.end_with?('!') }
|
292
|
+
pad = 1
|
293
|
+
elsif args.size <= 1
|
294
|
+
args = [:bold] + args
|
295
|
+
end
|
296
|
+
end
|
297
|
+
Project.banner(cmd.sub(/^\S+/, &:upcase), @path.to_s, styles: args, pad: pad, first: pad == 0)
|
298
|
+
elsif multitask && @workspace.multitask?
|
299
|
+
"## #{@path} ##"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def empty_status(msg, title, obj)
|
304
|
+
"#{msg}#{!obj || obj == 0 || obj.to_s.empty? ? '' : message(hint: message(title, obj.to_s))}"
|
305
|
+
end
|
306
|
+
|
307
|
+
def append_nocolor
|
308
|
+
@session << (!ENV.fetch('NO_COLOR', '').empty? || pipe? ? '--no-color' : '')
|
309
|
+
end
|
310
|
+
|
311
|
+
def guard_params(action, flag, args: nil, key: nil, pat: nil)
|
312
|
+
if args && key
|
313
|
+
return unless (val = args[key]).nil? || (pat && !pat.match?(val))
|
314
|
+
|
315
|
+
@session = nil
|
316
|
+
raise ArgumentError, message(action, "#{flag}[#{key}]", hint: val.nil? ? 'missing' : 'invalid')
|
317
|
+
elsif args.is_a?(::Array)
|
318
|
+
return unless args.empty?
|
319
|
+
|
320
|
+
@session = nil
|
321
|
+
raise ArgumentError, message(action, "#{flag}[]", hint: 'empty')
|
322
|
+
end
|
323
|
+
return unless action.to_s.empty?
|
324
|
+
|
325
|
+
@session = nil
|
326
|
+
raise ArgumentError, message('parameter', flag, hint: 'missing')
|
327
|
+
end
|
328
|
+
|
329
|
+
def pipe?
|
330
|
+
@workspace.pipe?
|
331
|
+
end
|
332
|
+
|
333
|
+
def invoked_sync?(action)
|
334
|
+
return true if invoked?(group = "#{action}:sync")
|
335
|
+
|
336
|
+
missing = ->(val) { ::Rake::Task.tasks.none? { |item| item.name == val } }
|
337
|
+
check = lambda do |val|
|
338
|
+
if invoked?(val)
|
339
|
+
missing.("#{val}:sync")
|
340
|
+
elsif invoked?("#{val}:sync")
|
341
|
+
true
|
342
|
+
end
|
343
|
+
end
|
344
|
+
if @group
|
345
|
+
ret = check.("#{action}:#{@group}")
|
346
|
+
return ret unless ret.nil?
|
347
|
+
end
|
348
|
+
if (base = @workspace.find_base(self))
|
349
|
+
ret = check.("#{action}:#{base.to_sym}")
|
350
|
+
return ret unless ret.nil?
|
351
|
+
end
|
352
|
+
invoked?("#{@name}:#{action}") && (!invoked?(action) || missing.(group))
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|