squared 0.4.2 → 0.4.4
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 +65 -0
- data/README.ruby.md +131 -61
- data/lib/squared/common/base.rb +2 -1
- data/lib/squared/common/format.rb +2 -6
- data/lib/squared/common/shell.rb +2 -1
- data/lib/squared/common/utils.rb +8 -5
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +10 -1
- data/lib/squared/workspace/project/base.rb +114 -46
- data/lib/squared/workspace/project/docker.rb +123 -53
- data/lib/squared/workspace/project/git.rb +72 -66
- data/lib/squared/workspace/project/node.rb +25 -27
- data/lib/squared/workspace/project/python.rb +213 -79
- data/lib/squared/workspace/project/ruby.rb +96 -40
- data/lib/squared/workspace/repo.rb +1 -1
- metadata +2 -2
@@ -63,6 +63,10 @@ module Squared
|
|
63
63
|
list: %w[a|all digests no-trunc f|filter=q format=q].freeze,
|
64
64
|
push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
|
65
65
|
rm: %w[f|force no-prune].freeze
|
66
|
+
}.freeze,
|
67
|
+
network: {
|
68
|
+
connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=b link=b link-local-ip=b].freeze,
|
69
|
+
disconnect: %w[f|force].freeze
|
66
70
|
}.freeze
|
67
71
|
}.freeze
|
68
72
|
VAL_DOCKER = {
|
@@ -85,13 +89,14 @@ module Squared
|
|
85
89
|
end
|
86
90
|
end
|
87
91
|
|
88
|
-
|
92
|
+
subtasks({
|
89
93
|
'build' => %i[tag context bake].freeze,
|
90
94
|
'compose' => %i[build run exec up].freeze,
|
91
95
|
'image' => %i[list rm push].freeze,
|
92
96
|
'container' => %i[run exec update commit inspect diff start stop restart pause unpause top stats kill
|
93
|
-
rm].freeze
|
94
|
-
|
97
|
+
rm].freeze,
|
98
|
+
'network' => %i[connect disconnect].freeze
|
99
|
+
})
|
95
100
|
|
96
101
|
attr_reader :context
|
97
102
|
attr_accessor :tag
|
@@ -101,7 +106,7 @@ module Squared
|
|
101
106
|
return unless dockerfile(file).exist?
|
102
107
|
|
103
108
|
@context = context
|
104
|
-
@tag = tag || "#{@project}
|
109
|
+
@tag = tag || tagname("#{@project}:#{@version || 'latest'}")
|
105
110
|
@mounts = mounts
|
106
111
|
@secrets = secrets
|
107
112
|
@registry = [registry, kwargs[:username]].compact.join('/')
|
@@ -132,7 +137,7 @@ module Squared
|
|
132
137
|
format_desc(action, flag, 'opts*', before: flag == :tag ? 'name' : 'dir')
|
133
138
|
task flag, [flag] do |_, args|
|
134
139
|
param = param_guard(action, flag, args: args, key: flag)
|
135
|
-
buildx(:build, args.
|
140
|
+
buildx(:build, args.extras, "#{flag}": param)
|
136
141
|
end
|
137
142
|
when :bake
|
138
143
|
format_desc action, flag, 'opts*,target*,context?'
|
@@ -152,7 +157,7 @@ module Squared
|
|
152
157
|
format_desc action, flag, "service,command#{flag == :exec ? '' : '?'},args*,opts*"
|
153
158
|
task flag, [:service] do |_, args|
|
154
159
|
service = param_guard(action, flag, args: args, key: :service)
|
155
|
-
composex(flag, args.
|
160
|
+
composex(flag, args.extras, service: service)
|
156
161
|
end
|
157
162
|
end
|
158
163
|
when 'container'
|
@@ -161,13 +166,13 @@ module Squared
|
|
161
166
|
format_desc(action, flag, 'id/name,opts*', after: flag == :exec ? 'args+' : 'tag?')
|
162
167
|
task flag, [:id] do |_, args|
|
163
168
|
id = param_guard(action, flag, args: args, key: :id)
|
164
|
-
container(flag, args.
|
169
|
+
container(flag, args.extras, id: id)
|
165
170
|
end
|
166
171
|
when :run
|
167
172
|
format_desc action, flag, 'image,opts*,args*'
|
168
173
|
task flag, [:image] do |_, args|
|
169
174
|
image = param_guard(action, flag, args: args, key: :image)
|
170
|
-
container(flag, args.
|
175
|
+
container(flag, args.extras, id: image)
|
171
176
|
end
|
172
177
|
else
|
173
178
|
format_desc(action, flag, 'opts*', after: "id/name#{flag == :update ? '+' : '*'}")
|
@@ -181,7 +186,7 @@ module Squared
|
|
181
186
|
format_desc action, flag, 'tag,registry/username?,opts*'
|
182
187
|
task flag, [:tag] do |_, args|
|
183
188
|
tag = param_guard(action, flag, args: args, key: :tag)
|
184
|
-
image(flag, args.
|
189
|
+
image(flag, args.extras, id: tag)
|
185
190
|
end
|
186
191
|
else
|
187
192
|
format_desc(action, flag, flag == :rm ? 'id*,opts*' : 'opts*,args*')
|
@@ -189,6 +194,12 @@ module Squared
|
|
189
194
|
image flag, args.to_a
|
190
195
|
end
|
191
196
|
end
|
197
|
+
when 'network'
|
198
|
+
format_desc action, flag, 'target,opts*'
|
199
|
+
task flag, [:target] do |_, args|
|
200
|
+
target = param_guard(action, flag, args: args, key: :target)
|
201
|
+
network(flag, args.extras, target: target)
|
202
|
+
end
|
192
203
|
end
|
193
204
|
end
|
194
205
|
end
|
@@ -251,9 +262,10 @@ module Squared
|
|
251
262
|
as_a(@secrets).each { |arg| ret << quote_option('secret', arg) }
|
252
263
|
end
|
253
264
|
if (val = option('tag', ignore: false))
|
254
|
-
|
265
|
+
append_tag val
|
266
|
+
ret << basic_option('tag', tagname(val))
|
255
267
|
elsif !session_arg?('t', 'tag')
|
256
|
-
|
268
|
+
append_tag tag
|
257
269
|
end
|
258
270
|
append_context
|
259
271
|
when :bake, :compose
|
@@ -268,10 +280,11 @@ module Squared
|
|
268
280
|
cmd, opts = docker_session('buildx', opts: opts)
|
269
281
|
opts = option_sanitize(opts, OPT_DOCKER[:buildx][:common]).first
|
270
282
|
cmd << flag
|
271
|
-
|
283
|
+
list = OPT_DOCKER[:buildx][flag == :bake ? :bake : :build] + OPT_DOCKER[:buildx][:shared]
|
284
|
+
out = option_sanitize(opts, list).first
|
272
285
|
case flag
|
273
|
-
when :build
|
274
|
-
|
286
|
+
when :build, :context
|
287
|
+
append_tag(tag || option('tag', ignore: false) || @tag)
|
275
288
|
append_context context
|
276
289
|
when :bake
|
277
290
|
unless out.empty?
|
@@ -288,7 +301,7 @@ module Squared
|
|
288
301
|
contextdir(context) if context
|
289
302
|
end
|
290
303
|
end
|
291
|
-
option_clear
|
304
|
+
option_clear(out, pass: false)
|
292
305
|
run(from: :"buildx:#{flag}")
|
293
306
|
end
|
294
307
|
|
@@ -297,7 +310,7 @@ module Squared
|
|
297
310
|
opts = option_sanitize(opts, OPT_DOCKER[:compose][:common]).first
|
298
311
|
append_file filetype unless session_arg?('f', 'file')
|
299
312
|
cmd << flag
|
300
|
-
out = option_sanitize(opts, OPT_DOCKER[:compose][flag]
|
313
|
+
out = option_sanitize(opts, OPT_DOCKER[:compose][flag]).first
|
301
314
|
from = :"compose:#{flag}"
|
302
315
|
case flag
|
303
316
|
when :build, :up
|
@@ -312,7 +325,7 @@ module Squared
|
|
312
325
|
cmd, opts = docker_session('container', flag, opts: opts)
|
313
326
|
list = OPT_DOCKER[:container].fetch(flag, [])
|
314
327
|
list += OPT_DOCKER[:container][:update] if flag == :run
|
315
|
-
out = option_sanitize(opts, list,
|
328
|
+
out = option_sanitize(opts, list, args: flag == :run || flag == :exec).first
|
316
329
|
from = :"container:#{flag}"
|
317
330
|
case flag
|
318
331
|
when :run, :exec
|
@@ -348,12 +361,12 @@ module Squared
|
|
348
361
|
cmd << "--mount type=#{tmpfs ? 'tmpfs' : 'bind'},#{args.join(',')}"
|
349
362
|
end
|
350
363
|
end
|
351
|
-
append_command(flag, id.to_s.empty? ?
|
364
|
+
append_command(flag, id.to_s.empty? ? tagmain : id, out, from: from)
|
352
365
|
when :update
|
353
366
|
raise_error('missing container', hint: from) if out.empty?
|
354
367
|
append_value(out, escape: true)
|
355
368
|
when :commit
|
356
|
-
latest = out.shift ||
|
369
|
+
latest = out.shift || tagmain
|
357
370
|
cmd << id << latest
|
358
371
|
raise_error("unknown args: #{out.join(', ')}", hint: from) unless out.empty?
|
359
372
|
return unless confirm_command(cmd.to_s, title: from, target: id, as: latest)
|
@@ -364,17 +377,16 @@ module Squared
|
|
364
377
|
|
365
378
|
opts = []
|
366
379
|
append_option('platform', target: opts, equals: true)
|
367
|
-
case option('disable-content-trust', ignore: false)
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
380
|
+
opts << case option('disable-content-trust', ignore: false)
|
381
|
+
when 'false', '0'
|
382
|
+
'--disable-content-trust=false'
|
383
|
+
else
|
384
|
+
'--disable-content-trust'
|
385
|
+
end
|
373
386
|
opts << '--quiet' unless verbose
|
374
387
|
return image(:push, opts, id: latest, registry: registry)
|
375
388
|
else
|
376
389
|
if out.empty?
|
377
|
-
ps = docker_output 'ps', '-a'
|
378
390
|
status = []
|
379
391
|
no = true
|
380
392
|
case flag
|
@@ -399,7 +411,7 @@ module Squared
|
|
399
411
|
when :rm
|
400
412
|
status = %w[created exited dead]
|
401
413
|
end
|
402
|
-
ps
|
414
|
+
ps = docker_output('ps -a', *status.map { |s| "--filter=\"status=#{s}\"" })
|
403
415
|
list_image(flag, ps, no: no, hint: "status: #{status.join(', ')}", from: from) do |img|
|
404
416
|
run(cmd.temp(img), from: from)
|
405
417
|
end
|
@@ -412,7 +424,7 @@ module Squared
|
|
412
424
|
end
|
413
425
|
|
414
426
|
def image(flag, opts = [], sync: true, id: nil, registry: nil)
|
415
|
-
cmd, opts = docker_session('image', flag
|
427
|
+
cmd, opts = docker_session('image', flag, opts: opts)
|
416
428
|
out = option_sanitize(opts, OPT_DOCKER[:image][flag]).first
|
417
429
|
from = :"image:#{flag}"
|
418
430
|
case flag
|
@@ -420,13 +432,8 @@ module Squared
|
|
420
432
|
if opts.size == out.size
|
421
433
|
index = 0
|
422
434
|
name = nil
|
423
|
-
opts.
|
424
|
-
|
425
|
-
opts.delete(opt)
|
426
|
-
break
|
427
|
-
end
|
428
|
-
end
|
429
|
-
list_image(flag, cmd << '-a', from: from) do |val|
|
435
|
+
opts.reverse_each { |opt| break opts.delete(opt) if (name = opt[/^name=["']?(.+?)["']?$/, 1]) }
|
436
|
+
list_image(:run, cmd << '-a', from: from) do |val|
|
430
437
|
container(:run, if name
|
431
438
|
opts.dup << "name=#{index == 0 ? name : "#{name}-#{index}"}"
|
432
439
|
else
|
@@ -451,8 +458,8 @@ module Squared
|
|
451
458
|
return
|
452
459
|
end
|
453
460
|
when :push
|
454
|
-
id ||= tag
|
455
|
-
registry ||= out.shift || @registry
|
461
|
+
id ||= option('tag', ignore: false) || tagmain
|
462
|
+
registry ||= out.shift || option('registry') || @registry
|
456
463
|
raise_error(id ? "unknown args: #{out.join(', ')}" : 'no id/tag given', hint: from) unless id && out.empty?
|
457
464
|
raise_error('username/registry not provided', hint: from) unless registry
|
458
465
|
registry.chomp!('/')
|
@@ -466,6 +473,15 @@ module Squared
|
|
466
473
|
run(sync: sync, from: from)
|
467
474
|
end
|
468
475
|
|
476
|
+
def network(flag, opts = [], target: nil)
|
477
|
+
cmd, opts = docker_session('network', flag, opts: opts)
|
478
|
+
option_sanitize(opts, OPT_DOCKER[:network][flag]).first
|
479
|
+
from = :"network:#{flag}"
|
480
|
+
list_image(flag, docker_output('ps -a'), from: from) do |img|
|
481
|
+
puts 'Success' if run(cmd.temp(target, img), from: from) == true && stdout? && banner?
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
469
485
|
def build?
|
470
486
|
@output[0] != false && dockerfile.exist?
|
471
487
|
end
|
@@ -494,9 +510,10 @@ module Squared
|
|
494
510
|
def docker_session(*cmd, opts: nil)
|
495
511
|
return session('docker', *cmd) unless opts
|
496
512
|
|
497
|
-
|
498
|
-
opts = option_sanitize(opts, OPT_DOCKER[:common]).first
|
499
|
-
|
513
|
+
out = []
|
514
|
+
opts = option_sanitize(opts, OPT_DOCKER[:common], target: out).first
|
515
|
+
ret = session('docker', *out, *cmd)
|
516
|
+
[ret, opts]
|
500
517
|
end
|
501
518
|
|
502
519
|
def docker_output(*cmd, **kwargs)
|
@@ -504,7 +521,15 @@ module Squared
|
|
504
521
|
end
|
505
522
|
|
506
523
|
def append_command(flag, val, list, target: @session, from: nil)
|
507
|
-
|
524
|
+
case flag
|
525
|
+
when :run
|
526
|
+
unless session_arg?('name')
|
527
|
+
require 'random/formatter'
|
528
|
+
target << basic_option('name', dnsname("#{name}_#{Random.new.alphanumeric(6)}"))
|
529
|
+
end
|
530
|
+
when :exec
|
531
|
+
raise_error('no command args', hint: from) if list.empty?
|
532
|
+
end
|
508
533
|
target << val << list.shift
|
509
534
|
target << shell_quote(list.join(' && '), double: true, option: false) unless list.empty?
|
510
535
|
end
|
@@ -522,25 +547,43 @@ module Squared
|
|
522
547
|
target << contextdir(ctx || context)
|
523
548
|
end
|
524
549
|
|
550
|
+
def append_tag(val, target: @session)
|
551
|
+
ver = option('version', ignore: false)
|
552
|
+
list = case val
|
553
|
+
when String
|
554
|
+
val.split(',')
|
555
|
+
when Array
|
556
|
+
val
|
557
|
+
else
|
558
|
+
[]
|
559
|
+
end
|
560
|
+
list.each do |s|
|
561
|
+
s = "#{s.sub(/:latest$/, '')}:#{ver}" if ver && (!s.include?(':') || s.end_with?(':latest'))
|
562
|
+
target << basic_option('tag', tagname(s))
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
525
566
|
def list_image(flag, cmd, hint: nil, from: nil, no: true, &blk)
|
526
567
|
pwd_set do
|
527
568
|
found = false
|
528
|
-
|
569
|
+
index = 0
|
570
|
+
pat = /^(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':').first})(?:[_.,:-]|$)/
|
571
|
+
IO.popen(session_done(cmd << '--format=json')).each do |line|
|
529
572
|
data = JSON.parse(line)
|
530
573
|
id = data['ID']
|
531
574
|
rt = [data['Repository'], data['Tag']].reject { |val| val == '<none>' }.join(':')
|
532
575
|
rt = nil if rt.empty?
|
533
|
-
aa = if data['
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
576
|
+
aa = data['Names'] || (if rt && data['Repository']
|
577
|
+
dd = true
|
578
|
+
data['Repository']
|
579
|
+
else
|
580
|
+
id
|
581
|
+
end)
|
582
|
+
ee = data['Image'] || rt || aa
|
583
|
+
next unless ee.match?(pat) || aa.match?(pat)
|
584
|
+
|
541
585
|
bb = index.succ.to_s
|
542
586
|
cc = bb.size + 1
|
543
|
-
ee = data['Image'] || rt || aa
|
544
587
|
a = sub_style(ee, styles: theme[:inline])
|
545
588
|
b = "Execute #{sub_style(flag, styles: theme[:active])} on #{a}#{ee == id ? '' : " (#{id})"}"
|
546
589
|
c, d = no ? ['y/N', 'N'] : ['Y/n', 'Y']
|
@@ -550,14 +593,22 @@ module Squared
|
|
550
593
|
h = "#{sub_style(bb.rjust(cc), styles: theme[:current])} #{f} "
|
551
594
|
puts unless index == 0
|
552
595
|
puts "#{h + sub_style(aa, styles: theme[:subject])} (created #{e} ago)"
|
553
|
-
%w[Tag Status Ports
|
596
|
+
cols = %w[Tag Status Ports]
|
597
|
+
cols << case flag
|
598
|
+
when :connect, :disconnect
|
599
|
+
'Networks'
|
600
|
+
else
|
601
|
+
'Size'
|
602
|
+
end
|
603
|
+
cols.each do |key|
|
554
604
|
next if (key == 'Tag' && !dd) || (key == 'Size' && data[key] == '0B')
|
555
605
|
|
556
606
|
puts "#{g + f} #{key}: #{as_a(data[key]).join(', ')}" unless data[key].to_s.empty?
|
557
607
|
end
|
558
|
-
w = 9 + flag.to_s.size + 4 +
|
608
|
+
w = 9 + flag.to_s.size + 4 + ee.size
|
559
609
|
puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), styles: theme[:inline])
|
560
610
|
found = true
|
611
|
+
index += 1
|
561
612
|
next unless confirm("#{h + b}? [#{c}] ", d, timeout: 60)
|
562
613
|
|
563
614
|
puts if @@print_order == 0
|
@@ -608,6 +659,25 @@ module Squared
|
|
608
659
|
val && projectpath?(val) ? shell_quote(basepath(val)) : '.'
|
609
660
|
end
|
610
661
|
|
662
|
+
def tagname(val)
|
663
|
+
val = val.split(':').map { |s| charname(s.sub(/^\W+/, '')) }
|
664
|
+
ret = val.join(':')
|
665
|
+
ret = val.first if val.size > 1 && ret.size > 128
|
666
|
+
ret[0..127]
|
667
|
+
end
|
668
|
+
|
669
|
+
def dnsname(val)
|
670
|
+
charname(val[/^[^a-z\d]*(.*?)[^a-z\d]*$/i, 1].gsub(/-{2,}/, '-'))[0..62].downcase
|
671
|
+
end
|
672
|
+
|
673
|
+
def charname(val)
|
674
|
+
val.gsub(/[^\w.-]+/, '_')
|
675
|
+
end
|
676
|
+
|
677
|
+
def tagmain
|
678
|
+
tag.is_a?(Array) ? tag.first : tag
|
679
|
+
end
|
680
|
+
|
611
681
|
def compose?(file)
|
612
682
|
COMPOSEFILE.include?(File.basename(file))
|
613
683
|
end
|