squared 0.4.6 → 0.4.8
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/CHANGELOG.md +77 -0
- data/README.ruby.md +52 -32
- data/lib/squared/common/base.rb +1 -0
- data/lib/squared/common/class.rb +20 -5
- data/lib/squared/common/format.rb +30 -22
- data/lib/squared/common/prompt.rb +39 -1
- data/lib/squared/common/shell.rb +14 -10
- data/lib/squared/common/system.rb +3 -3
- data/lib/squared/common/utils.rb +17 -10
- data/lib/squared/config.rb +1 -2
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +39 -23
- data/lib/squared/workspace/project/base.rb +242 -243
- data/lib/squared/workspace/project/docker.rb +119 -72
- data/lib/squared/workspace/project/git.rb +252 -214
- data/lib/squared/workspace/project/node.rb +65 -67
- data/lib/squared/workspace/project/python.rb +69 -57
- data/lib/squared/workspace/project/ruby.rb +297 -98
- data/lib/squared/workspace/project/support/class.rb +199 -0
- data/lib/squared/workspace/project/support.rb +3 -0
- data/lib/squared/workspace/project.rb +1 -0
- data/lib/squared/workspace/repo.rb +6 -8
- data/lib/squared/workspace/series.rb +7 -6
- data/lib/squared/workspace.rb +1 -8
- metadata +3 -1
@@ -125,7 +125,7 @@ module Squared
|
|
125
125
|
return unless ref?(Docker.ref)
|
126
126
|
|
127
127
|
namespace name do
|
128
|
-
|
128
|
+
Docker.subtasks do |action, flags|
|
129
129
|
next if @pass.include?(action)
|
130
130
|
|
131
131
|
namespace action do
|
@@ -151,31 +151,38 @@ module Squared
|
|
151
151
|
when :build, :up
|
152
152
|
format_desc action, flag, 'opts*,service*'
|
153
153
|
task flag do |_, args|
|
154
|
-
|
154
|
+
compose! flag, args.to_a
|
155
155
|
end
|
156
156
|
when :exec, :run
|
157
157
|
format_desc action, flag, "service,command#{flag == :exec ? '' : '?'},args*,opts*"
|
158
158
|
task flag, [:service] do |_, args|
|
159
159
|
service = param_guard(action, flag, args: args, key: :service)
|
160
|
-
|
160
|
+
compose!(flag, args.extras, service: service)
|
161
161
|
end
|
162
162
|
end
|
163
163
|
when 'container'
|
164
164
|
case flag
|
165
165
|
when :exec, :commit
|
166
|
-
format_desc(action, flag,
|
166
|
+
format_desc(action, flag, flag == :exec ? 'id/name?,opts*,args+' : 'id/name,tag?,opts*')
|
167
167
|
task flag, [:id] do |_, args|
|
168
|
-
|
169
|
-
|
168
|
+
if flag == :exec && !args.id
|
169
|
+
choice_command flag
|
170
|
+
else
|
171
|
+
id = param_guard(action, flag, args: args, key: :id)
|
172
|
+
container(flag, args.extras, id: id)
|
173
|
+
end
|
170
174
|
end
|
171
175
|
when :run
|
172
|
-
format_desc action, flag, 'image
|
176
|
+
format_desc action, flag, 'image?,opts*,args*'
|
173
177
|
task flag, [:image] do |_, args|
|
174
|
-
|
175
|
-
|
178
|
+
if args.image
|
179
|
+
container(flag, args.extras, id: args.image)
|
180
|
+
else
|
181
|
+
choice_command flag
|
182
|
+
end
|
176
183
|
end
|
177
184
|
else
|
178
|
-
format_desc(action, flag, 'opts
|
185
|
+
format_desc(action, flag, 'opts*,id/name', after: flag == :update ? '+' : '*')
|
179
186
|
task flag do |_, args|
|
180
187
|
container flag, args.to_a
|
181
188
|
end
|
@@ -188,7 +195,7 @@ module Squared
|
|
188
195
|
tag = param_guard(action, flag, args: args, key: :tag)
|
189
196
|
image(flag, args.extras, id: tag)
|
190
197
|
end
|
191
|
-
|
198
|
+
when :list, :rm
|
192
199
|
format_desc(action, flag, flag == :rm ? 'id*,opts*' : 'opts*,args*')
|
193
200
|
task flag do |_, args|
|
194
201
|
image flag, args.to_a
|
@@ -197,8 +204,11 @@ module Squared
|
|
197
204
|
when 'network'
|
198
205
|
format_desc action, flag, 'target,opts*'
|
199
206
|
task flag, [:target] do |_, args|
|
200
|
-
target =
|
201
|
-
|
207
|
+
if (target = args.target)
|
208
|
+
network(flag, args.extras, target: target)
|
209
|
+
else
|
210
|
+
choice_command flag
|
211
|
+
end
|
202
212
|
end
|
203
213
|
end
|
204
214
|
end
|
@@ -251,13 +261,13 @@ module Squared
|
|
251
261
|
when :build
|
252
262
|
case @secrets
|
253
263
|
when String
|
254
|
-
quote_option('secret', @secrets, double: true)
|
264
|
+
ret << quote_option('secret', @secrets, double: true)
|
255
265
|
when Hash
|
256
266
|
append = lambda do |type|
|
257
267
|
as_a(@secrets[type]).each { |arg| ret << quote_option('secret', "type=#{type},#{arg}", double: true) }
|
258
268
|
end
|
259
|
-
append.(:file)
|
260
|
-
append.(:env)
|
269
|
+
append.call(:file)
|
270
|
+
append.call(:env)
|
261
271
|
else
|
262
272
|
as_a(@secrets).each { |arg| ret << quote_option('secret', arg) }
|
263
273
|
end
|
@@ -278,45 +288,44 @@ module Squared
|
|
278
288
|
|
279
289
|
def buildx(flag, opts = [], tag: nil, context: nil)
|
280
290
|
cmd, opts = docker_session('buildx', opts: opts)
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
out = option_sanitize(opts, list).first
|
291
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:buildx][:common], cmd, project: self)
|
292
|
+
op << flag
|
293
|
+
op.parse(OPT_DOCKER[:buildx][flag == :bake ? :bake : :build] + OPT_DOCKER[:buildx][:shared])
|
285
294
|
case flag
|
286
295
|
when :build, :context
|
287
296
|
append_tag(tag || option('tag', ignore: false) || @tag)
|
288
297
|
append_context context
|
289
298
|
when :bake
|
290
|
-
unless
|
291
|
-
args =
|
292
|
-
|
299
|
+
unless op.empty?
|
300
|
+
args = op.dup
|
301
|
+
op.extras.clear
|
293
302
|
if Dir.exist?(args.last)
|
294
303
|
if projectpath?(val = args.pop)
|
295
304
|
context = val
|
296
305
|
else
|
297
|
-
|
306
|
+
op.extras << val
|
298
307
|
end
|
299
308
|
end
|
300
|
-
|
309
|
+
op.append(args, escape: true)
|
301
310
|
contextdir(context) if context
|
302
311
|
end
|
303
312
|
end
|
304
|
-
|
313
|
+
op.clear(pass: false)
|
305
314
|
run(from: :"buildx:#{flag}")
|
306
315
|
end
|
307
316
|
|
308
|
-
def
|
317
|
+
def compose!(flag, opts = [], service: nil)
|
309
318
|
cmd, opts = docker_session('compose', opts: opts)
|
310
|
-
|
311
|
-
append_file filetype unless
|
312
|
-
|
313
|
-
|
319
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self)
|
320
|
+
append_file filetype unless op.arg?('f', 'file')
|
321
|
+
op << flag
|
322
|
+
op.parse(OPT_DOCKER[:compose][flag])
|
314
323
|
from = :"compose:#{flag}"
|
315
324
|
case flag
|
316
325
|
when :build, :up
|
317
|
-
|
326
|
+
op.append(escape: true)
|
318
327
|
when :exec, :run
|
319
|
-
append_command(flag, service,
|
328
|
+
append_command(flag, service, op.extras, from: from)
|
320
329
|
end
|
321
330
|
run(from: from)
|
322
331
|
end
|
@@ -324,12 +333,12 @@ module Squared
|
|
324
333
|
def container(flag, opts = [], id: nil)
|
325
334
|
cmd, opts = docker_session('container', flag, opts: opts)
|
326
335
|
list = OPT_DOCKER[:container].fetch(flag, [])
|
327
|
-
list
|
328
|
-
|
336
|
+
list.concat(OPT_DOCKER[:container][:update]) if flag == :run
|
337
|
+
op = OptionPartition.new(opts, list, cmd, project: self, args: flag == :run || flag == :exec)
|
329
338
|
from = :"container:#{flag}"
|
330
339
|
case flag
|
331
340
|
when :run, :exec
|
332
|
-
if flag == :run && !
|
341
|
+
if flag == :run && !op.arg?('mount')
|
333
342
|
run = VAL_DOCKER[:run]
|
334
343
|
both = run[:bind] + run[:tmpfs]
|
335
344
|
diff = run[:bind].reject { |val| run[:tmpfs].include?(val) }
|
@@ -361,14 +370,14 @@ module Squared
|
|
361
370
|
cmd << "--mount type=#{tmpfs ? 'tmpfs' : 'bind'},#{args.join(',')}"
|
362
371
|
end
|
363
372
|
end
|
364
|
-
append_command(flag, id.to_s.empty? ? tagmain : id,
|
373
|
+
append_command(flag, id.to_s.empty? ? tagmain : id, op.extras, from: from)
|
365
374
|
when :update
|
366
|
-
raise_error('missing container', hint: from) if
|
367
|
-
|
375
|
+
raise_error('missing container', hint: from) if op.empty?
|
376
|
+
op.append(escape: true)
|
368
377
|
when :commit
|
369
|
-
latest =
|
378
|
+
latest = op.shift || tagmain
|
370
379
|
cmd << id << latest
|
371
|
-
raise_error("unknown args: #{
|
380
|
+
raise_error("unknown args: #{op.join(', ')}", hint: from) unless op.empty?
|
372
381
|
return unless confirm_command(cmd.to_s, title: from, target: id, as: latest)
|
373
382
|
|
374
383
|
registry = option('registry') || @registry
|
@@ -386,7 +395,7 @@ module Squared
|
|
386
395
|
opts << '--quiet' unless verbose
|
387
396
|
return image(:push, opts, id: latest, registry: registry)
|
388
397
|
else
|
389
|
-
if
|
398
|
+
if op.empty?
|
390
399
|
status = []
|
391
400
|
no = true
|
392
401
|
case flag
|
@@ -411,13 +420,13 @@ module Squared
|
|
411
420
|
when :rm
|
412
421
|
status = %w[created exited dead]
|
413
422
|
end
|
414
|
-
ps = docker_output('ps -a', *status.map
|
423
|
+
ps = docker_output('ps -a', *status.map { |s| "--filter='status=#{s}'" })
|
415
424
|
list_image(flag, ps, no: no, hint: "status: #{status.join(', ')}", from: from) do |img|
|
416
425
|
run(cmd.temp(img), from: from)
|
417
426
|
end
|
418
427
|
return
|
419
428
|
else
|
420
|
-
|
429
|
+
op.append(escape: true)
|
421
430
|
end
|
422
431
|
end
|
423
432
|
run(from: from)
|
@@ -425,11 +434,11 @@ module Squared
|
|
425
434
|
|
426
435
|
def image(flag, opts = [], sync: true, id: nil, registry: nil)
|
427
436
|
cmd, opts = docker_session('image', flag, opts: opts)
|
428
|
-
|
437
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:image][flag], cmd, project: self)
|
429
438
|
from = :"image:#{flag}"
|
430
439
|
case flag
|
431
440
|
when :list
|
432
|
-
if opts.size ==
|
441
|
+
if opts.size == op.size
|
433
442
|
index = 0
|
434
443
|
name = nil
|
435
444
|
opts.reverse_each { |opt| break opts.delete(opt) if (name = opt[/^name=["']?(.+?)["']?$/, 1]) }
|
@@ -443,28 +452,29 @@ module Squared
|
|
443
452
|
end
|
444
453
|
return
|
445
454
|
else
|
446
|
-
|
455
|
+
op.clear
|
447
456
|
end
|
448
457
|
when :rm
|
449
458
|
if id
|
450
|
-
|
451
|
-
elsif !out.empty?
|
452
|
-
out.each { |val| run(cmd.temp(val), sync: sync, from: from) }
|
453
|
-
return
|
459
|
+
op << id
|
454
460
|
else
|
455
|
-
|
456
|
-
|
461
|
+
if op.empty?
|
462
|
+
list_image(flag, docker_output('image ls -a'), from: from) do |val|
|
463
|
+
image(:rm, opts, sync: sync, id: val)
|
464
|
+
end
|
465
|
+
else
|
466
|
+
op.each { |val| run(cmd.temp(val), sync: sync, from: from) }
|
457
467
|
end
|
458
468
|
return
|
459
469
|
end
|
460
470
|
when :push
|
461
471
|
id ||= option('tag', ignore: false) || tagmain
|
462
|
-
registry ||=
|
463
|
-
raise_error(id ? "unknown args: #{
|
472
|
+
registry ||= op.shift || option('registry') || @registry
|
473
|
+
raise_error(id ? "unknown args: #{op.join(', ')}" : 'no id/tag given', hint: from) unless id && op.empty?
|
464
474
|
raise_error('username/registry not provided', hint: from) unless registry
|
465
475
|
registry.chomp!('/')
|
466
476
|
uri = shell_quote("#{registry}/#{id}")
|
467
|
-
|
477
|
+
op << uri
|
468
478
|
img = docker_output 'image', 'tag', id, uri
|
469
479
|
return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
|
470
480
|
|
@@ -475,7 +485,8 @@ module Squared
|
|
475
485
|
|
476
486
|
def network(flag, opts = [], target: nil)
|
477
487
|
cmd, opts = docker_session('network', flag, opts: opts)
|
478
|
-
|
488
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:network][flag], cmd, project: self)
|
489
|
+
op.clear
|
479
490
|
from = :"network:#{flag}"
|
480
491
|
list_image(flag, docker_output('ps -a'), from: from) do |img|
|
481
492
|
puts 'Success' if run(cmd.temp(target, img), from: from) == true && stdout? && banner?
|
@@ -510,10 +521,9 @@ module Squared
|
|
510
521
|
def docker_session(*cmd, opts: nil)
|
511
522
|
return session('docker', *cmd) unless opts
|
512
523
|
|
513
|
-
|
514
|
-
|
515
|
-
ret
|
516
|
-
[ret, opts]
|
524
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:common], project: self)
|
525
|
+
ret = session('docker', *op.to_a, *cmd)
|
526
|
+
[ret, op.extras]
|
517
527
|
end
|
518
528
|
|
519
529
|
def docker_output(*cmd, **kwargs)
|
@@ -521,9 +531,12 @@ module Squared
|
|
521
531
|
end
|
522
532
|
|
523
533
|
def append_command(flag, val, list, target: @session, from: nil)
|
534
|
+
if (args = env('DOCKER_ARGS'))
|
535
|
+
list << args
|
536
|
+
end
|
524
537
|
case flag
|
525
538
|
when :run
|
526
|
-
unless session_arg?('name')
|
539
|
+
unless session_arg?('name', target: target)
|
527
540
|
require 'random/formatter'
|
528
541
|
target << basic_option('name', dnsname("#{name}_#{Random.new.alphanumeric(6)}"))
|
529
542
|
end
|
@@ -531,24 +544,29 @@ module Squared
|
|
531
544
|
raise_error('no command args', hint: from) if list.empty?
|
532
545
|
end
|
533
546
|
target << val << list.shift
|
534
|
-
target <<
|
547
|
+
target << list.join(' && ') unless list.empty?
|
535
548
|
end
|
536
549
|
|
537
550
|
def append_file(type, target: @session)
|
538
551
|
return unless type == 2 || type == 4 || @file.is_a?(Array)
|
539
552
|
|
540
|
-
|
553
|
+
files = as_a(@file).map { |val| quote_option('file', basepath(val)) }
|
554
|
+
if target.is_a?(Set)
|
555
|
+
target.merge(files)
|
556
|
+
else
|
557
|
+
target.concat(files)
|
558
|
+
end
|
541
559
|
end
|
542
560
|
|
543
561
|
def append_context(ctx = nil, target: @session)
|
544
|
-
if @file.is_a?(String) && !session_arg?('f', 'file') && !bake?
|
562
|
+
if @file.is_a?(String) && !session_arg?('f', 'file', target: target) && !bake? && !compose?
|
545
563
|
target << quote_option('file', dockerfile)
|
546
564
|
end
|
547
565
|
target << contextdir(ctx || context)
|
548
566
|
end
|
549
567
|
|
550
568
|
def append_tag(val, target: @session)
|
551
|
-
ver = option('version', ignore: false)
|
569
|
+
ver = option('version', target: target, ignore: false)
|
552
570
|
list = case val
|
553
571
|
when String
|
554
572
|
val.split(',')
|
@@ -563,7 +581,7 @@ module Squared
|
|
563
581
|
end
|
564
582
|
end
|
565
583
|
|
566
|
-
def list_image(flag, cmd, hint: nil, from: nil, no: true
|
584
|
+
def list_image(flag, cmd, hint: nil, from: nil, no: true)
|
567
585
|
pwd_set do
|
568
586
|
found = false
|
569
587
|
index = 0
|
@@ -580,14 +598,14 @@ module Squared
|
|
580
598
|
id
|
581
599
|
end)
|
582
600
|
ee = data['Image'] || rt || aa
|
583
|
-
next unless ee.match?(pat) || aa.match?(pat)
|
601
|
+
next unless option('all') || ee.match?(pat) || aa.match?(pat)
|
584
602
|
|
585
603
|
bb = index.succ.to_s
|
586
604
|
cc = bb.size + 1
|
587
605
|
a = sub_style(ee, styles: theme[:inline])
|
588
606
|
b = "Execute #{sub_style(flag, styles: theme[:active])} on #{a}#{ee == id ? '' : " (#{id})"}"
|
589
607
|
c, d = no ? ['y/N', 'N'] : ['Y/n', 'Y']
|
590
|
-
e = time_format(
|
608
|
+
e = time_format(time_since(data['CreatedAt']), pass: ['ms'])
|
591
609
|
f = sub_style(ARG[:BORDER][0], styles: theme[:inline])
|
592
610
|
g = ' ' * (cc + 1)
|
593
611
|
h = "#{sub_style(bb.rjust(cc), styles: theme[:current])} #{f} "
|
@@ -612,7 +630,7 @@ module Squared
|
|
612
630
|
next unless confirm("#{h + b}? [#{c}] ", d, timeout: 60)
|
613
631
|
|
614
632
|
puts if @@print_order == 0
|
615
|
-
|
633
|
+
yield id
|
616
634
|
end
|
617
635
|
puts log_message(Logger::INFO, 'none detected', subject: "#{name}:#{from}", hint: hint) unless found
|
618
636
|
end
|
@@ -640,6 +658,35 @@ module Squared
|
|
640
658
|
confirm("#{a} #{b}#{c ? " as #{c}" : ''}? [y/N] ", 'N', timeout: 60)
|
641
659
|
end
|
642
660
|
|
661
|
+
def choice_command(flag)
|
662
|
+
msg, cmd, index = case flag
|
663
|
+
when :run
|
664
|
+
['Choose an image', 'images -a', 2]
|
665
|
+
when :exec
|
666
|
+
['Choose a container', 'ps -a', 0]
|
667
|
+
else
|
668
|
+
['Choose a network', 'network ls', 0]
|
669
|
+
end
|
670
|
+
lines = `#{docker_output(cmd)}`.lines
|
671
|
+
header = lines.shift
|
672
|
+
if lines.empty?
|
673
|
+
puts log_message(Logger::INFO, 'none found', subject: name, hint: "docker #{cmd}")
|
674
|
+
else
|
675
|
+
puts " # #{header}"
|
676
|
+
case flag
|
677
|
+
when :run, :exec
|
678
|
+
values = [['Options', flag == :run], ['Command arguments', flag == :exec]]
|
679
|
+
cmd = flag.to_s
|
680
|
+
else
|
681
|
+
values = [['Options', false], ['Container', true]]
|
682
|
+
cmd = "network #{flag}"
|
683
|
+
end
|
684
|
+
out, opts, args = choice_index(msg, lines, values: values)
|
685
|
+
ret = run docker_output(cmd, opts, '--', out.split(/\s+/)[index], args)
|
686
|
+
puts 'Success' if ret && cmd.start_with?('network')
|
687
|
+
end
|
688
|
+
end
|
689
|
+
|
643
690
|
def filetype(val = dockerfile)
|
644
691
|
case File.extname(val)
|
645
692
|
when '.hcl', '.json'
|
@@ -678,11 +725,11 @@ module Squared
|
|
678
725
|
tag.is_a?(Array) ? tag.first : tag
|
679
726
|
end
|
680
727
|
|
681
|
-
def compose?(file)
|
728
|
+
def compose?(file = dockerfile)
|
682
729
|
COMPOSEFILE.include?(File.basename(file))
|
683
730
|
end
|
684
731
|
|
685
|
-
def bake?(file)
|
732
|
+
def bake?(file = dockerfile)
|
686
733
|
BAKEFILE.include?(File.basename(file))
|
687
734
|
end
|
688
735
|
end
|