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,394 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Squared
|
4
|
+
module Repo
|
5
|
+
module Project
|
6
|
+
class Node < Git
|
7
|
+
class << self
|
8
|
+
def tasks
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_sym
|
13
|
+
:node
|
14
|
+
end
|
15
|
+
|
16
|
+
def prod?
|
17
|
+
ENV['NODE_ENV'] == 'production'
|
18
|
+
end
|
19
|
+
|
20
|
+
def is_a?(path)
|
21
|
+
if path.is_a?(::String) || path.is_a?(::Pathname)
|
22
|
+
Pathname.new(path).join('package.json').exist?
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@@tasks[:node] = {
|
30
|
+
install: %i[force dedupe],
|
31
|
+
outdated: %i[major minor patch]
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
def initialize(name, project, workspace, **kwargs)
|
35
|
+
super
|
36
|
+
@clean ||= ['build/']
|
37
|
+
@dev = workspace.bool_match(env('BUILD', suffix: 'DEV'), kwargs.delete(:dev))
|
38
|
+
@prod = workspace.bool_match(env('BUILD', suffix: 'PROD'), kwargs.delete(:prod))
|
39
|
+
@package = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def populate(*)
|
43
|
+
super
|
44
|
+
return unless outdated?
|
45
|
+
|
46
|
+
namespace @name do
|
47
|
+
@@tasks[:node].each do |action, flags|
|
48
|
+
namespace action do
|
49
|
+
flags.each do |flag|
|
50
|
+
case action
|
51
|
+
when :install
|
52
|
+
desc format_desc(action, flag)
|
53
|
+
task flag do
|
54
|
+
install flag
|
55
|
+
end
|
56
|
+
when :outdated
|
57
|
+
desc format_desc(action, flag, %w[prune dry-run], req: 'opts?')
|
58
|
+
task flag, [:opts] do |_, args|
|
59
|
+
opts = collect_args(args, :opts)
|
60
|
+
outdated(flag, opts: opts)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def copy(from: 'build', glob: '**/*', basedir: 'node_modules', into: nil, also: [], override: false)
|
70
|
+
if @copy && !override
|
71
|
+
return super if @copy.is_a?(::String)
|
72
|
+
|
73
|
+
from = @copy[:from] if @copy.key?(:from)
|
74
|
+
glob = @copy[:glob] if @copy.key?(:glob)
|
75
|
+
basedir = @copy[:basedir] if @copy.key?(:basedir)
|
76
|
+
into = @copy[:into] if @copy.key?(:into)
|
77
|
+
also = @copy[:also] if @copy.key?(:also)
|
78
|
+
end
|
79
|
+
return unless basedir
|
80
|
+
|
81
|
+
items = [@project == @workspace.main ? nil : @workspace.home].concat(as_a(also))
|
82
|
+
items.each_with_index do |dir, i|
|
83
|
+
if i == 0
|
84
|
+
next unless dev? & !doc?
|
85
|
+
|
86
|
+
dest = dir
|
87
|
+
elsif dir.is_a?(::String)
|
88
|
+
dest = @workspace.root_path(dir)
|
89
|
+
elsif dir.is_a?(::Symbol)
|
90
|
+
dest = Repo.resolve(dir)&.path
|
91
|
+
elsif dir.is_a?(Project)
|
92
|
+
dest = dir.path
|
93
|
+
elsif dir.is_a?(::Hash)
|
94
|
+
into = nil
|
95
|
+
glob = nil
|
96
|
+
dir.each do |key, val|
|
97
|
+
case key.to_sym
|
98
|
+
when :from
|
99
|
+
from = val
|
100
|
+
when :glob
|
101
|
+
glob = val
|
102
|
+
when :basedir
|
103
|
+
basedir = val
|
104
|
+
when :into
|
105
|
+
into = val
|
106
|
+
end
|
107
|
+
end
|
108
|
+
next unless into
|
109
|
+
end
|
110
|
+
next unless dest&.directory?
|
111
|
+
|
112
|
+
from = base_path(from)
|
113
|
+
dest = dest.join(basedir, into || @project)
|
114
|
+
warn "cp #{from.join(glob)} #{dest}"
|
115
|
+
copy_d(from, dest, glob: glob, verbose: verbose?)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def depend(flag = nil)
|
120
|
+
if @depend
|
121
|
+
super
|
122
|
+
else
|
123
|
+
force = flag == :force
|
124
|
+
dedupe = flag == :dedupe
|
125
|
+
if (yarn = install_type(:yarn)) > 0
|
126
|
+
cmd = session 'yarn', 'install'
|
127
|
+
if yarn > 1
|
128
|
+
cmd << '--check-cache' if force
|
129
|
+
else
|
130
|
+
cmd << '--production' if prod?
|
131
|
+
cmd << '--force' if force
|
132
|
+
cmd << '--ignore-engines' unless env('YARN_IGNORE_ENGINES', equals: '0')
|
133
|
+
end
|
134
|
+
elsif pnpm?
|
135
|
+
cmd = session 'pnpm'
|
136
|
+
if dedupe
|
137
|
+
cmd << 'dedupe'
|
138
|
+
else
|
139
|
+
cmd << 'install'
|
140
|
+
cmd << '--force' if force
|
141
|
+
end
|
142
|
+
cmd << '--prod' if prod?
|
143
|
+
append_nocolor
|
144
|
+
else
|
145
|
+
cmd = session 'npm'
|
146
|
+
cmd << (dedupe ? 'dedupe' : 'install')
|
147
|
+
cmd << '--omit=dev' if prod?
|
148
|
+
cmd << '--force' if force
|
149
|
+
append_nocolor
|
150
|
+
end
|
151
|
+
append_loglevel
|
152
|
+
run(exception: @workspace.exception)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def outdated(rev = nil, opts: [])
|
157
|
+
require 'json'
|
158
|
+
rev ||= prod? ? :patch : :minor
|
159
|
+
cmd = pnpm? ? 'pnpm outdated' : 'npm outdated'
|
160
|
+
print_item format_banner(message("#{cmd}#{opts.include?('dry-run') ? ' --dry-run' : ''}"), multitask: true)
|
161
|
+
pwd = Dir.pwd
|
162
|
+
Dir.chdir(@path)
|
163
|
+
info cmd
|
164
|
+
data = `#{cmd} --json --loglevel=error`
|
165
|
+
Dir.chdir(pwd)
|
166
|
+
target = base_path('package.json')
|
167
|
+
doc = File.read(target)
|
168
|
+
json = JSON.parse(doc)
|
169
|
+
dep1 = json['dependencies'] || {}
|
170
|
+
dep2 = json['devDependencies'] || {}
|
171
|
+
found = []
|
172
|
+
avail = []
|
173
|
+
if !data.empty?
|
174
|
+
validate = ->(ver) { /^\d+\.\d+\.\d+$/.match?(ver) }
|
175
|
+
JSON.parse(data).each_pair do |key, val|
|
176
|
+
file = dep1[key] || dep2[key]
|
177
|
+
ch = file[0]
|
178
|
+
if !/[~^]/.match?(ch)
|
179
|
+
avail << [key, file, val['latest'], true]
|
180
|
+
next
|
181
|
+
end
|
182
|
+
file = file[1..-1]
|
183
|
+
cur = val['current']
|
184
|
+
want = if rev == :major && validate.(val['latest'])
|
185
|
+
[val['latest'], val['wanted']].max { |a, b| a <=> b }
|
186
|
+
else
|
187
|
+
val['wanted']
|
188
|
+
end
|
189
|
+
next unless (cur != want || file != want) && (validate.(want) || !validate.(file))
|
190
|
+
|
191
|
+
a, b = file.split('.')
|
192
|
+
c, d = want.split('.')
|
193
|
+
upgrade = false
|
194
|
+
case rev
|
195
|
+
when :major
|
196
|
+
upgrade = a == '0' ? c == '0' : true
|
197
|
+
when :minor
|
198
|
+
upgrade = ch == '^' && (a == '0' ? c == '0' && b == d : a == c)
|
199
|
+
when :patch
|
200
|
+
upgrade = a == c && b == d
|
201
|
+
end
|
202
|
+
if upgrade
|
203
|
+
next if file == want
|
204
|
+
|
205
|
+
found << [key, file, want]
|
206
|
+
else
|
207
|
+
avail << [key, file, val['latest'], false]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
pending = 0
|
212
|
+
modified = 0
|
213
|
+
size_col = ->(items, i) { items.map { |a| a[i] }.max_by(&:size).size }
|
214
|
+
pad_ord = lambda do |val, ord|
|
215
|
+
ret = val.succ.to_s
|
216
|
+
ord.size > 9 ? ret.rjust(ord.size.to_s.size) : ret
|
217
|
+
end
|
218
|
+
footer = lambda do |val = 0|
|
219
|
+
next unless verbose?
|
220
|
+
|
221
|
+
msg, hint = if modified == -1
|
222
|
+
['Packages were updated', 'more possible']
|
223
|
+
else
|
224
|
+
['No packages were updated', 'possible']
|
225
|
+
end
|
226
|
+
puts Project.footer(empty_status(msg, hint, pending + val))
|
227
|
+
end
|
228
|
+
if !found.empty?
|
229
|
+
col1 = size_col.(found, 0)
|
230
|
+
col2 = size_col.(found, 1)
|
231
|
+
found.each_with_index do |item, i|
|
232
|
+
a, b, c = item
|
233
|
+
cur = modified
|
234
|
+
doc.sub!(/("#{Regexp.escape(a)}"\s*:\s*)"([~^])#{Regexp.escape(b)}"/) do |capture|
|
235
|
+
if $2 == '~' && rev != :patch
|
236
|
+
cur = -1
|
237
|
+
pending += 1
|
238
|
+
capture
|
239
|
+
else
|
240
|
+
modified += 1
|
241
|
+
"#{$1}\"#{$2 || ''}#{c}\""
|
242
|
+
end
|
243
|
+
end
|
244
|
+
hint = if cur == -1
|
245
|
+
'SKIP'
|
246
|
+
else
|
247
|
+
modified > cur ? c : 'FAIL'
|
248
|
+
end
|
249
|
+
puts "#{pad_ord.(i, found)}. #{a.ljust(col1)}\t#{b.ljust(col2)}\t#{hint}"
|
250
|
+
end
|
251
|
+
pending = avail.reduce(pending) { |a, b| a + (b[3] ? 0 : 1) }
|
252
|
+
if opts.include?('dry-run') || (modified == 0 && pending > 0)
|
253
|
+
footer.(modified)
|
254
|
+
elsif modified > 0
|
255
|
+
File.write(target, doc)
|
256
|
+
modified = -1
|
257
|
+
footer.()
|
258
|
+
commit(:add, 'package.json', pass: true)
|
259
|
+
install if opts.include?('prune')
|
260
|
+
end
|
261
|
+
elsif !avail.empty?
|
262
|
+
col1 = size_col.(avail, 0)
|
263
|
+
col2 = size_col.(avail, 2)
|
264
|
+
avail.each_with_index do |item, i|
|
265
|
+
a, b, c, d = item
|
266
|
+
puts "#{pad_ord.(i, avail)}. #{a.ljust(col1)}\t#{c.ljust(col2)}\t#{d ? 'locked at' : 'from'} #{b}"
|
267
|
+
pending += 1 unless d
|
268
|
+
end
|
269
|
+
footer.()
|
270
|
+
else
|
271
|
+
puts 'No updates were found'
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def compose(opts)
|
276
|
+
cmd = [if yarn?
|
277
|
+
'yarn'
|
278
|
+
else
|
279
|
+
pnpm? ? 'pnpm' : 'npm'
|
280
|
+
end]
|
281
|
+
cmd << 'run'
|
282
|
+
append_loglevel cmd
|
283
|
+
if opts.is_a?(::Array)
|
284
|
+
cmd += opts
|
285
|
+
else
|
286
|
+
cmd << (opts || @workspace.script)
|
287
|
+
end
|
288
|
+
cmd.join(' ')
|
289
|
+
end
|
290
|
+
|
291
|
+
def install_type(prog)
|
292
|
+
key = :"#{prog}?"
|
293
|
+
send(key) if respond_to?(key)
|
294
|
+
@package[prog.to_sym] || 0
|
295
|
+
end
|
296
|
+
|
297
|
+
def depend?
|
298
|
+
!@depend.nil? || outdated?
|
299
|
+
end
|
300
|
+
|
301
|
+
def copy?
|
302
|
+
!@copy.nil?
|
303
|
+
end
|
304
|
+
|
305
|
+
def outdated?
|
306
|
+
base_path('package.json').exist?
|
307
|
+
end
|
308
|
+
|
309
|
+
def yarn?
|
310
|
+
(@package[:yarn] ||= if File.exist?(base_path('yarn.lock'))
|
311
|
+
if File.exist?(rc = base_path('.yarnrc.yml'))
|
312
|
+
begin
|
313
|
+
require 'yaml'
|
314
|
+
doc = YAML.load_file(rc)
|
315
|
+
doc.nodeLinker == 'node-modules' ? 2 : 3
|
316
|
+
rescue StandardError
|
317
|
+
3
|
318
|
+
end
|
319
|
+
else
|
320
|
+
1
|
321
|
+
end
|
322
|
+
else
|
323
|
+
ver = env('NODE_INSTALL')
|
324
|
+
if ver&.start_with?('yarn')
|
325
|
+
ver.include?('@1') ? 1 : 3
|
326
|
+
else
|
327
|
+
0
|
328
|
+
end
|
329
|
+
end) > 0
|
330
|
+
end
|
331
|
+
|
332
|
+
def pnpm?
|
333
|
+
(@package[:pnpm] ||= if File.exist?(base_path('pnpm-lock.yaml'))
|
334
|
+
begin
|
335
|
+
require 'yaml'
|
336
|
+
doc = YAML.load_file(base_path('node_modules/.modules.yaml'))
|
337
|
+
case doc['nodeLinker']
|
338
|
+
when 'hoisted'
|
339
|
+
1
|
340
|
+
when 'pnp'
|
341
|
+
3
|
342
|
+
else
|
343
|
+
4
|
344
|
+
end
|
345
|
+
rescue StandardError
|
346
|
+
4
|
347
|
+
end
|
348
|
+
else
|
349
|
+
env('NODE_INSTALL')&.start_with?('pnpm') ? 4 : 0
|
350
|
+
end) > 0
|
351
|
+
end
|
352
|
+
|
353
|
+
def dev?
|
354
|
+
return false if Node.prod?
|
355
|
+
|
356
|
+
@workspace.dev?(@dev, script: @output.last)
|
357
|
+
end
|
358
|
+
|
359
|
+
def prod?
|
360
|
+
return true if Node.prod?
|
361
|
+
|
362
|
+
@workspace.prod?(@prod, script: @output.last)
|
363
|
+
end
|
364
|
+
|
365
|
+
protected
|
366
|
+
|
367
|
+
def append_loglevel(cmd = @session)
|
368
|
+
return unless (level = env('NODE_LOGLEVEL'))
|
369
|
+
|
370
|
+
if yarn?
|
371
|
+
if install_type(:yarn) == 1
|
372
|
+
case level
|
373
|
+
when 'silent', 'verbose'
|
374
|
+
cmd << "--#{level}"
|
375
|
+
end
|
376
|
+
end
|
377
|
+
elsif pnpm?
|
378
|
+
case level
|
379
|
+
when 'debug', 'info', 'warn', 'error'
|
380
|
+
cmd << "--loglevel=#{level}"
|
381
|
+
when 'silent'
|
382
|
+
cmd << '--reporter=silent'
|
383
|
+
end
|
384
|
+
else
|
385
|
+
case level
|
386
|
+
when 'silent', 'error', 'warn', 'notice', 'http', 'info', 'verbose', 'silly'
|
387
|
+
cmd << "--loglevel=#{level}"
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Squared
|
4
|
+
module Repo
|
5
|
+
module Project
|
6
|
+
class Python < Git
|
7
|
+
class << self
|
8
|
+
def tasks
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_sym
|
13
|
+
:python
|
14
|
+
end
|
15
|
+
|
16
|
+
def venv?
|
17
|
+
path = ENV['VIRTUAL_ENV']
|
18
|
+
!path.nil? && Dir.exist?(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_a?(path)
|
22
|
+
if path.is_a?(::String) || path.is_a?(::Pathname)
|
23
|
+
base = Pathname.new(path)
|
24
|
+
['setup.py', 'requirements.txt'].any? { |file| base.join(file).exist? }
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@@tasks[:python] = {
|
32
|
+
install: %i[upgrade force]
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
def populate(*)
|
36
|
+
super
|
37
|
+
return unless outdated?
|
38
|
+
|
39
|
+
namespace @name do
|
40
|
+
@@tasks[:python].each do |action, flags|
|
41
|
+
namespace action do
|
42
|
+
flags.each do |flag|
|
43
|
+
case action
|
44
|
+
when :install
|
45
|
+
options = lambda do |*opts|
|
46
|
+
opts += %w[pre venv no-cache dry-run]
|
47
|
+
format_desc(action, flag, opts)
|
48
|
+
end
|
49
|
+
case flag
|
50
|
+
when :upgrade
|
51
|
+
desc options.('eager')
|
52
|
+
when :force
|
53
|
+
desc options.()
|
54
|
+
end
|
55
|
+
task flag do |_, args|
|
56
|
+
install(flag, opts: collect_args(args, :opts))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def depend(flag = nil, opts: [])
|
66
|
+
if @depend
|
67
|
+
super
|
68
|
+
else
|
69
|
+
cmd = pip_session 'install -r requirements.txt'
|
70
|
+
case flag
|
71
|
+
when :upgrade
|
72
|
+
cmd << '--upgrade'
|
73
|
+
cmd << '--upgrade-strategy=eager' if opts.include?('eager')
|
74
|
+
when :force
|
75
|
+
cmd << '--force-reinstall'
|
76
|
+
end
|
77
|
+
cmd << '--pre' if opts.include?('pre')
|
78
|
+
cmd << '--require-virtualenv' if opts.include?('venv')
|
79
|
+
cmd << '--no-cache-dir' if opts.include?('no-cache')
|
80
|
+
cmd << '--dry-run' if opts.include?('dry-run')
|
81
|
+
append_nocolor
|
82
|
+
run(exception: @workspace.exception)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def outdated(*); end
|
87
|
+
|
88
|
+
def depend?
|
89
|
+
!@depend.nil? || outdated?
|
90
|
+
end
|
91
|
+
|
92
|
+
def outdated?
|
93
|
+
base_path('requirements.txt').exist?
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def pip_session(*cmd)
|
99
|
+
session('pip', *cmd)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|