squared 0.8.3 → 0.8.5
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 +112 -6
- data/README.md +8 -9
- data/lib/squared/common/base.rb +1 -0
- data/lib/squared/common/format.rb +2 -1
- data/lib/squared/common/host.rb +39 -0
- data/lib/squared/common/prompt.rb +4 -3
- data/lib/squared/common/shell.rb +13 -12
- data/lib/squared/common/utils.rb +7 -4
- data/lib/squared/common.rb +1 -0
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +5 -27
- data/lib/squared/workspace/project/base.rb +41 -17
- data/lib/squared/workspace/project/docker.rb +215 -147
- data/lib/squared/workspace/project/git.rb +125 -113
- data/lib/squared/workspace/project/node.rb +69 -36
- data/lib/squared/workspace/project/python.rb +13 -9
- data/lib/squared/workspace/project/ruby.rb +13 -12
- data/lib/squared/workspace/project/support/optionpartition.rb +108 -53
- data/lib/squared/workspace/repo.rb +3 -2
- data/lib/squared/workspace/series.rb +2 -2
- metadata +3 -2
|
@@ -16,19 +16,19 @@ module Squared
|
|
|
16
16
|
cgroup-parent=b iidfile=p label=q network=b no-cache-filter=b o|output=q platform=q q|quiet
|
|
17
17
|
resource=q secret=qq shm-size=b ssh=qq t|tag=b target=b ulimit=q].freeze,
|
|
18
18
|
bake: %w[print list=q set=qq var=qq].freeze,
|
|
19
|
-
shared: %w[check load no-cache pull push allow=
|
|
20
|
-
|
|
19
|
+
shared: %w[check load no-cache provenance pull push sbom allow=qq call=b? f|file=p metadata-file=p policy=q
|
|
20
|
+
progress=b].freeze
|
|
21
21
|
}.freeze,
|
|
22
22
|
compose: {
|
|
23
|
-
common: %w[all-resources
|
|
24
|
-
project-directory=p p|project-name=
|
|
23
|
+
common: %w[1 all-resources compatibility dry-run ansi=b env-file=p f|file=p parallel=n profile=b progress=b
|
|
24
|
+
project-directory=p p|project-name=b].freeze,
|
|
25
25
|
build: %w[check no-cache print pull push with-dependencies q|quiet build-arg=qq builder=b m|memory=b
|
|
26
26
|
provenance=q sbom=q ssh=qq].freeze,
|
|
27
27
|
create: %w[build force-recreate no-build no-recreate quiet-pull remove-orphans y|yes pull=b scale=i].freeze,
|
|
28
|
-
exec: %w[d|detach privileged e|env=qq index=i T|no-
|
|
28
|
+
exec: %w[d|detach privileged e|env=qq index=i T|no-tty user=b w|workdir=q].freeze,
|
|
29
29
|
run: %w[build d|detach no-deps q|quiet quiet-build quiet-pull remove-orphans rm P|service-ports use-aliases
|
|
30
|
-
cap-add=b cap-drop=b entrypoint=q e|env=qq env-from-file=p i|interactive
|
|
31
|
-
|
|
30
|
+
cap-add=b cap-drop=b entrypoint=q e|env=qq env-from-file=p i|interactive l|label=q name=b T|no-tty
|
|
31
|
+
p|publish=q pull=b u|user=b v|volume=qq w|workdir=q].freeze,
|
|
32
32
|
up: %w[abort-on-container-exit abort-on-container-failure always-recreate-deps attach-dependencies build
|
|
33
33
|
d|detach force-recreate menu no-build no-color no-deps no-log-prefix no-recreate no-start quiet-build
|
|
34
34
|
quiet-pull remove-orphans V|renew-anon-volumes timestamps wait w|watch y|yes attach=b
|
|
@@ -39,9 +39,9 @@ module Squared
|
|
|
39
39
|
}.freeze,
|
|
40
40
|
container: {
|
|
41
41
|
create: %w[init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet read-only
|
|
42
|
-
rm t|tty use-api-socket add-host=q annotation=q a|attach=b blkio-weight=i
|
|
43
|
-
cap-add=b cap-drop=b cgroup-parent=b cgroupns=b cidfile=p device=q
|
|
44
|
-
device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
|
|
42
|
+
rm t|tty use-api-socket add-host=q annotation=q a|attach=b blkio-weight=i
|
|
43
|
+
blkio-weight-device=i cap-add=b cap-drop=b cgroup-parent=b cgroupns=b cidfile=p device=q
|
|
44
|
+
device-cgroup-rule=q device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
|
|
45
45
|
dns=q dns-option=q dns-search=q domainname=b entrypoint=q e|env=qq env-file=p expose=q gpus=q
|
|
46
46
|
group-add=b health-cmd=q health-interval=b health-retries=i health-start-interval=q
|
|
47
47
|
health-start-period=q health-timeout=q hostname=q io-maxbandwidth=b io-maxiops=b ip=b ip6=q ipc=b
|
|
@@ -49,13 +49,14 @@ module Squared
|
|
|
49
49
|
m|memory=b memory-reservation=b memory-swap=n memory-swappiness=n mount=qq name=b network=b
|
|
50
50
|
network-alias=b oom-score-adj=b pid=b pids-limit=n platform=q p|publish=q pull=b restart=b
|
|
51
51
|
runtime=b security-opt=q shm-size=b stop-signal=b stop-timeout=i storage-opt=q sysctl=q tmpfs=q
|
|
52
|
-
ulimit=q u|user=b userns=b uts=b v|volume=
|
|
53
|
-
run: %w[d|detach detach-keys=q sig-proxy
|
|
52
|
+
ulimit=q u|user=b userns=b uts=b v|volume=qq volume-driver=b volumes-from=b w|workdir=q].freeze,
|
|
53
|
+
run: %w[d|detach detach-keys=q sig-proxy].freeze,
|
|
54
54
|
update: %w[blkio-weight=i cpu-period=i cpu-quota=i cpu-rt-period=i cpu-rt-runtime=i c|cpu-shares=i cpus=f
|
|
55
55
|
cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=n pids-limit=n
|
|
56
56
|
restart=q].freeze,
|
|
57
|
-
exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p u|user=
|
|
57
|
+
exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p u|user=b
|
|
58
58
|
w|workdir=q].freeze,
|
|
59
|
+
rm: %w[f|force l|link v|volumes].freeze,
|
|
59
60
|
commit: %w[no-pause a|author=q c|change=q m|message=q].freeze,
|
|
60
61
|
logs: %w[details f|follow t|timestamps since=q n|tail=b until=q].freeze,
|
|
61
62
|
inspect: %w[s|size f|format=q].freeze,
|
|
@@ -64,12 +65,12 @@ module Squared
|
|
|
64
65
|
restart: %w[s|signal=b t|timeout=i].freeze,
|
|
65
66
|
kill: %w[s|signal=b].freeze,
|
|
66
67
|
stats: %w[a|all no-stream no-trunc format|q].freeze,
|
|
67
|
-
attach: %w[no-stdin detach-keys=q sig-proxy
|
|
68
|
+
attach: %w[no-stdin detach-keys=q sig-proxy].freeze
|
|
68
69
|
}.freeze,
|
|
69
70
|
image: {
|
|
70
71
|
common: %w[platform=q].freeze,
|
|
71
72
|
inspect: %w[f|format].freeze,
|
|
72
|
-
history: %w[f|format no-trunc q|quiet H|human
|
|
73
|
+
history: %w[f|format no-trunc q|quiet H|human].freeze,
|
|
73
74
|
ls: %w[a|all digests no-trunc q|quiet tree f|filter=q format=q].freeze,
|
|
74
75
|
pull: %w[a|all-tags q|quiet].freeze,
|
|
75
76
|
push: %w[a|all-tags q|quiet].freeze,
|
|
@@ -80,16 +81,17 @@ module Squared
|
|
|
80
81
|
connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=q link=b link-local-ip=q].freeze,
|
|
81
82
|
create: %w[attachable config-only gateway=q ingress internal ipv4 ipv6 aux-address=q config-from=b
|
|
82
83
|
d|driver=b ip-range=q ipam-driver=b ipam-opt=q label=q o|opt=q scope=b subnet=q].freeze,
|
|
83
|
-
disconnect: %w[f|force].freeze
|
|
84
|
+
disconnect: %w[f|force].freeze,
|
|
85
|
+
rm: %w[f|force].freeze
|
|
84
86
|
}.freeze
|
|
85
87
|
}.freeze
|
|
86
88
|
VAL_DOCKER = {
|
|
87
89
|
run: {
|
|
88
90
|
common: %w[source src destination dst target readonly ro].freeze,
|
|
89
|
-
bind: %w[bind-propagation].freeze,
|
|
91
|
+
bind: %w[bind-create-src bind-propagation].freeze,
|
|
90
92
|
volume: %w[volume-subpath volume-nocopy volume-opt].freeze,
|
|
91
93
|
tmpfs: %w[tmpfs-size tmpfs-mode].freeze,
|
|
92
|
-
image: %w[image-
|
|
94
|
+
image: %w[image-subpath].freeze
|
|
93
95
|
}.freeze,
|
|
94
96
|
ls: {
|
|
95
97
|
compose: %w[Name Image Command Service RunningFor Status Ports CreatedAt ExitCode Health ID Labels
|
|
@@ -108,6 +110,10 @@ module Squared
|
|
|
108
110
|
[].freeze
|
|
109
111
|
end
|
|
110
112
|
|
|
113
|
+
def bannerargs
|
|
114
|
+
%i[context registry tag].freeze
|
|
115
|
+
end
|
|
116
|
+
|
|
111
117
|
def config?(val)
|
|
112
118
|
return false unless (val = as_path(val))
|
|
113
119
|
|
|
@@ -120,41 +126,37 @@ module Squared
|
|
|
120
126
|
'compose' => %i[build create publish run exec up down logs service].freeze,
|
|
121
127
|
'bake' => %i[build compose check].freeze,
|
|
122
128
|
'image' => %i[ls rm pull push tag save inspect history].freeze,
|
|
123
|
-
'container' => %i[attach commit create diff exec
|
|
129
|
+
'container' => %i[attach commit create diff exec inspect kill logs pause restart rm run start stats stop top
|
|
124
130
|
unpause update].freeze,
|
|
125
|
-
'network' => %i[connect disconnect create].freeze,
|
|
131
|
+
'network' => %i[connect disconnect create rm].freeze,
|
|
126
132
|
'ls' => nil
|
|
127
133
|
})
|
|
128
134
|
|
|
129
135
|
attr_reader :context
|
|
130
|
-
attr_accessor :tag
|
|
136
|
+
attr_accessor :registry, :tag
|
|
131
137
|
|
|
132
|
-
def initialize(*,
|
|
138
|
+
def initialize(*, **kwargs)
|
|
133
139
|
super
|
|
134
140
|
self.global = nil
|
|
135
141
|
return unless dockerfile(kwargs[:file]).exist?
|
|
136
142
|
|
|
137
|
-
@tag = kwargs[:tag] || tagname(
|
|
143
|
+
@tag = env('TAG', strict: true, ignore: false) || kwargs[:tag] || tagname(@project, @version || 'latest')
|
|
138
144
|
@context = kwargs[:context]
|
|
139
|
-
@client =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
end
|
|
155
|
-
opts unless opts.empty?
|
|
156
|
-
end
|
|
157
|
-
@mounts = mounts
|
|
145
|
+
@client = kwargs[:client]&.each_with_object([]) do |(key, val), out|
|
|
146
|
+
val = case (key = key.to_s)
|
|
147
|
+
when 'config'
|
|
148
|
+
basepath!(val, type: 'd')
|
|
149
|
+
when 'tlscacert', 'tlscert', 'tlskey'
|
|
150
|
+
basepath!(val, type: 'f')
|
|
151
|
+
when 'context', 'host', 'log-level'
|
|
152
|
+
quote_option key, val
|
|
153
|
+
when 'debug', 'tls', 'tlsverify'
|
|
154
|
+
out << key
|
|
155
|
+
next
|
|
156
|
+
end
|
|
157
|
+
out << quote_option(key, val) if val
|
|
158
|
+
end
|
|
159
|
+
@mounts = Array(kwargs[:mounts])
|
|
158
160
|
@secrets = kwargs[:secrets]
|
|
159
161
|
@registry = tagjoin kwargs[:registry], kwargs[:username]
|
|
160
162
|
@oci = tagjoin kwargs[:oci], kwargs[:username]
|
|
@@ -229,7 +231,7 @@ module Squared
|
|
|
229
231
|
format_desc action, flag, 'opts*,target*,context?|:'
|
|
230
232
|
task flag do |_, args|
|
|
231
233
|
args = args.to_a
|
|
232
|
-
if args
|
|
234
|
+
if choice!(args)
|
|
233
235
|
choice_command :bake
|
|
234
236
|
else
|
|
235
237
|
buildx(:bake, args, from: (:'buildx:bake' if flag == :compose))
|
|
@@ -253,14 +255,14 @@ module Squared
|
|
|
253
255
|
compose_(flag, args.extras, service: service)
|
|
254
256
|
end
|
|
255
257
|
when :service
|
|
256
|
-
cmds = %w[down kill pause restart rm start stop top unpause watch].freeze
|
|
258
|
+
cmds = %w[down kill logs pause restart rm start stop top unpause watch].freeze
|
|
257
259
|
format_desc(action, flag, cmds, arg: nil, after: 'name+|:')
|
|
258
260
|
task flag, [:command] do |_, args|
|
|
259
261
|
cmd = param_guard(action, flag, args: args, key: :command)
|
|
260
262
|
raise ArgumentError, message('unrecognized command', hint: cmd) unless cmds.include?(cmd)
|
|
261
263
|
|
|
262
264
|
service = args.extras
|
|
263
|
-
if service
|
|
265
|
+
if choice!(service)
|
|
264
266
|
choice_command flag, cmd
|
|
265
267
|
else
|
|
266
268
|
compose_(flag, [cmd], service: service.empty? || service)
|
|
@@ -313,11 +315,13 @@ module Squared
|
|
|
313
315
|
when 'image'
|
|
314
316
|
case flag
|
|
315
317
|
when :push
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
format_desc action, flag, 'tag?,registry/username?,opts*'
|
|
318
|
+
format_desc action, flag, 'tag?,registry/username?|:,opts*'
|
|
319
319
|
task flag do |_, args|
|
|
320
|
-
|
|
320
|
+
args = args.to_a
|
|
321
|
+
if choice!(args, first: false) || !self.registry
|
|
322
|
+
registry = readline('Enter registry/username', force: true)
|
|
323
|
+
end
|
|
324
|
+
image(flag, args, id: id, registry: registry)
|
|
321
325
|
end
|
|
322
326
|
when :pull
|
|
323
327
|
format_desc action, flag, 'tag,registry/username?,opts*'
|
|
@@ -355,11 +359,15 @@ module Squared
|
|
|
355
359
|
end
|
|
356
360
|
end
|
|
357
361
|
when 'network'
|
|
358
|
-
format_desc action, flag, '
|
|
359
|
-
task flag, [:
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
362
|
+
format_desc action, flag, 'name,opts*'
|
|
363
|
+
task flag, [:name] do |_, args|
|
|
364
|
+
name = if flag == :create || flag == :rm
|
|
365
|
+
param_guard(action, flag, args: args, key: :name)
|
|
366
|
+
else
|
|
367
|
+
args.name
|
|
368
|
+
end
|
|
369
|
+
if name
|
|
370
|
+
network(flag, args.extras, name: name)
|
|
363
371
|
else
|
|
364
372
|
choice_command flag
|
|
365
373
|
end
|
|
@@ -423,11 +431,7 @@ module Squared
|
|
|
423
431
|
else
|
|
424
432
|
ret.merge(Array(@secrets).map { |arg| quote_option('secret', arg) })
|
|
425
433
|
end
|
|
426
|
-
|
|
427
|
-
append_tag val
|
|
428
|
-
elsif !session_arg?('t', 'tag')
|
|
429
|
-
append_tag tag
|
|
430
|
-
end
|
|
434
|
+
append_tag option('tag', ignore: false) or session_arg?('t', 'tag') ? nil : tag
|
|
431
435
|
append_context
|
|
432
436
|
when :bake, :compose
|
|
433
437
|
option(from == :bake ? 'target' : 'service', ignore: false) { |val| ret.merge(split_escape(val).quote!) }
|
|
@@ -436,15 +440,15 @@ module Squared
|
|
|
436
440
|
end
|
|
437
441
|
|
|
438
442
|
def buildx(flag, opts = [], tag: nil, context: nil, from: nil)
|
|
443
|
+
bx = OPT_DOCKER[:buildx]
|
|
439
444
|
if flag == :bake && context&.match?(%r{^["']?(https?|git)://}i)
|
|
440
|
-
shared =
|
|
445
|
+
shared = bx[:shared].dup.push('f|file=q')
|
|
441
446
|
shared.delete('f|file=p')
|
|
442
447
|
end
|
|
443
448
|
cmd, opts = docker_session('buildx', opts: opts)
|
|
444
|
-
|
|
445
|
-
op = OptionPartition.new(opts, data[:common], cmd, project: self, strict: strict?)
|
|
449
|
+
op = OptionPartition.new(opts, bx[:common], cmd, **dockerinit)
|
|
446
450
|
op.append(flag, quote: false)
|
|
447
|
-
.parse(
|
|
451
|
+
.parse(bx[flag == :bake ? :bake : :build] + (shared || bx[:shared]))
|
|
448
452
|
case flag
|
|
449
453
|
when :build, :context
|
|
450
454
|
append_tag tag || option('tag', ignore: false) || self.tag
|
|
@@ -455,6 +459,10 @@ module Squared
|
|
|
455
459
|
end
|
|
456
460
|
when :bake
|
|
457
461
|
append_file(0, index: 3) unless from || op.arg?('f', 'file') || !anypath?(*COMPOSEFILE)
|
|
462
|
+
if op.first == '-'
|
|
463
|
+
op.shift
|
|
464
|
+
op << @output[4] if @output[4]
|
|
465
|
+
end
|
|
458
466
|
unless op.empty?
|
|
459
467
|
if !context && op.size > 1 && exist?(op.last, type: 'd')
|
|
460
468
|
pat = /\btarget\s+"#{Regexp.escape(op.last)}"/
|
|
@@ -476,7 +484,7 @@ module Squared
|
|
|
476
484
|
if flag == :service
|
|
477
485
|
command = opts.first
|
|
478
486
|
if service == true
|
|
479
|
-
cmd, status = filter_ps
|
|
487
|
+
cmd, status = filter_ps(command, from: from)
|
|
480
488
|
lines = IO.popen(cmd.temp('--services')).map(&:strip).reject(&:empty?)
|
|
481
489
|
return list_empty(hint: status) if lines.empty?
|
|
482
490
|
|
|
@@ -485,10 +493,11 @@ module Squared
|
|
|
485
493
|
docker_session('compose', command, '--', *service)
|
|
486
494
|
else
|
|
487
495
|
cmd, opts = docker_session('compose', opts: opts)
|
|
488
|
-
|
|
496
|
+
cp = OPT_DOCKER[:compose]
|
|
497
|
+
op = OptionPartition.new(opts, cp[:common], cmd, **dockerinit)
|
|
489
498
|
append_file(filetype, force: flag == :publish) unless op.arg?('f', 'file')
|
|
490
499
|
op << flag
|
|
491
|
-
op.parse(
|
|
500
|
+
op.parse(cp.fetch(flag, []))
|
|
492
501
|
if flag == :publish
|
|
493
502
|
id ||= option('tag', ignore: false) || op.shift || tagmain
|
|
494
503
|
registry ||= option('registry') || op.shift || @oci
|
|
@@ -519,18 +528,20 @@ module Squared
|
|
|
519
528
|
|
|
520
529
|
def container(flag, opts = [], id: nil)
|
|
521
530
|
cmd, opts = docker_session('container', flag, opts: opts)
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
list
|
|
525
|
-
list +=
|
|
526
|
-
|
|
531
|
+
rc = flag == :run
|
|
532
|
+
ct = OPT_DOCKER[:container]
|
|
533
|
+
list = ct.fetch(flag, [])
|
|
534
|
+
list += ct[:create] if rc
|
|
535
|
+
list += ct[:update] if rc ||= flag == :create
|
|
536
|
+
op = OptionPartition.new(opts, list, cmd, args: rc || flag == :exec, **dockerinit)
|
|
537
|
+
option('entrypoint') { |val| op.add_option('entrypoint', val, escape: false, double: true) } if flag == :run
|
|
527
538
|
from = symjoin 'container', flag
|
|
528
539
|
case flag
|
|
529
540
|
when :run, :create, :exec
|
|
530
541
|
if rc && !op.arg?('mount')
|
|
531
542
|
all = collect_hash VAL_DOCKER[:run]
|
|
532
543
|
delim = Regexp.new(",\\s*(?=#{all.join('|')})")
|
|
533
|
-
|
|
544
|
+
@mounts.each do |val|
|
|
534
545
|
type = nil
|
|
535
546
|
args = val.split(delim).each_with_object([]) do |opt, out|
|
|
536
547
|
k, v, q = OptionPartition.parse_option(opt)
|
|
@@ -551,23 +562,35 @@ module Squared
|
|
|
551
562
|
end
|
|
552
563
|
end
|
|
553
564
|
case k
|
|
554
|
-
when 'readonly', 'ro'
|
|
555
|
-
out << k
|
|
565
|
+
when 'readonly', 'ro', 'volume-nocopy', 'bind-create-src'
|
|
566
|
+
out << k if %w[bind volume].include?(type)
|
|
556
567
|
next
|
|
557
|
-
when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-
|
|
568
|
+
when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-subpath'
|
|
558
569
|
raise ArgumentError, message('no path value', k, hint: flag) unless v
|
|
559
570
|
|
|
560
|
-
|
|
571
|
+
case k
|
|
572
|
+
when 'source', 'src'
|
|
573
|
+
if type == 'bind'
|
|
574
|
+
v = basepath v
|
|
575
|
+
elsif type == 'tmpfs'
|
|
576
|
+
next
|
|
577
|
+
end
|
|
578
|
+
when 'destination', 'dst', 'target'
|
|
579
|
+
unless v.start_with?('/')
|
|
580
|
+
print_error(Logger::ERROR, "path is not absolute: #{v}", subject: from, hint: k)
|
|
581
|
+
next
|
|
582
|
+
end
|
|
583
|
+
end
|
|
561
584
|
v = shell_quote(v, option: false, force: false) if q == ''
|
|
562
585
|
end
|
|
563
586
|
out << "#{k}=#{q}#{v}#{q}"
|
|
564
587
|
elsif !silent?
|
|
565
|
-
log_message('unrecognized option', subject: from, hint: k)
|
|
588
|
+
puts log_message('unrecognized option', subject: from, hint: k)
|
|
566
589
|
end
|
|
567
590
|
end
|
|
568
591
|
raise TypeError, message('none specified', hint: flag) unless type
|
|
569
592
|
|
|
570
|
-
|
|
593
|
+
op << "--mount type=#{type},#{args.join(',')}"
|
|
571
594
|
end
|
|
572
595
|
end
|
|
573
596
|
append_command(flag, id || tagmain, op.extras)
|
|
@@ -577,11 +600,11 @@ module Squared
|
|
|
577
600
|
op.append(escape: true, strip: ':')
|
|
578
601
|
when :commit
|
|
579
602
|
latest = op.shift || tagmain
|
|
580
|
-
|
|
603
|
+
op << id << latest
|
|
581
604
|
raise ArgumentError, message('unrecognized args', op.join(', '), hint: flag) unless op.empty?
|
|
582
605
|
return unless confirm_command(cmd.to_s, title: from, target: id, as: latest)
|
|
583
606
|
|
|
584
|
-
registry = option('registry') ||
|
|
607
|
+
registry = option('registry') || self.registry
|
|
585
608
|
run(from: from, exception: registry.nil? ? exception? : true)
|
|
586
609
|
return unless registry
|
|
587
610
|
|
|
@@ -597,8 +620,8 @@ module Squared
|
|
|
597
620
|
return image(:push, opts, id: latest, registry: registry)
|
|
598
621
|
else
|
|
599
622
|
if op.empty?
|
|
600
|
-
ps, status, no = filter_ps
|
|
601
|
-
|
|
623
|
+
ps, status, no = filter_ps(flag, from: from)
|
|
624
|
+
op << '--no-stream' if flag == :stats
|
|
602
625
|
list_image(flag, ps, no: no, hint: status, multiple: flag != :attach, from: from) do |img|
|
|
603
626
|
run(cmd.temp(img), from: from)
|
|
604
627
|
end
|
|
@@ -621,16 +644,18 @@ module Squared
|
|
|
621
644
|
|
|
622
645
|
def image(flag, opts = [], sync: true, id: nil, registry: nil, filter: nil)
|
|
623
646
|
cmd, opts = docker_session('image', flag, opts: opts)
|
|
624
|
-
|
|
625
|
-
list
|
|
626
|
-
|
|
647
|
+
im = OPT_DOCKER[:image]
|
|
648
|
+
list = im.fetch(flag, [])
|
|
649
|
+
list += im[:common] unless flag == :ls || flag == :tag
|
|
650
|
+
op = OptionPartition.new(opts, list, cmd, **dockerinit)
|
|
627
651
|
exception = exception?
|
|
628
652
|
banner = true
|
|
653
|
+
status = false
|
|
629
654
|
from = symjoin 'image', flag
|
|
630
655
|
case flag
|
|
631
656
|
when :ls
|
|
632
657
|
if opts.size == op.size
|
|
633
|
-
index =
|
|
658
|
+
index = -1
|
|
634
659
|
name = nil
|
|
635
660
|
opts.reverse_each do |val|
|
|
636
661
|
next unless (arg = OptionPartition.parse_arg!('name', val))
|
|
@@ -640,12 +665,19 @@ module Squared
|
|
|
640
665
|
break
|
|
641
666
|
end
|
|
642
667
|
list_image(:run, filter: filter, from: from) do |val|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
668
|
+
args = if name
|
|
669
|
+
index += 1
|
|
670
|
+
opts.dup << "name=#{index == 0 ? name : "#{name}-#{index}"}"
|
|
671
|
+
elsif choice!(opts, only: true)
|
|
672
|
+
OptionPartition.strip(readline('Enter arguments', force: false))
|
|
673
|
+
else
|
|
674
|
+
opts
|
|
675
|
+
end
|
|
676
|
+
if args.none?(/^d(etach)?$/)
|
|
677
|
+
Thread.new(args.dup, val) { |a, id| container(:run, a, id: id) }.join
|
|
678
|
+
break
|
|
679
|
+
end
|
|
680
|
+
container(:run, args, id: val)
|
|
649
681
|
end
|
|
650
682
|
return
|
|
651
683
|
end
|
|
@@ -665,41 +697,40 @@ module Squared
|
|
|
665
697
|
banner = false
|
|
666
698
|
end
|
|
667
699
|
when :tag, :save
|
|
668
|
-
found = false
|
|
669
700
|
list_image(flag, filter: filter, from: from) do |val|
|
|
670
701
|
op << val
|
|
671
|
-
|
|
702
|
+
status = true
|
|
672
703
|
if flag == :tag
|
|
673
|
-
op << tagname(
|
|
704
|
+
op << tagname(project, op.first)
|
|
674
705
|
break
|
|
675
706
|
end
|
|
676
707
|
end
|
|
677
|
-
raise ArgumentError, message('target not specified', hint: flag) unless
|
|
708
|
+
raise ArgumentError, message('target not specified', hint: flag) unless status
|
|
678
709
|
when :pull
|
|
679
710
|
if !id
|
|
680
711
|
id = tagmain
|
|
681
712
|
elsif !op.arg?('a', 'all-tags') && !id.include?(':')
|
|
682
|
-
id =
|
|
713
|
+
id = tagname project, id
|
|
683
714
|
end
|
|
684
715
|
unless registry
|
|
685
716
|
registry = op.shift
|
|
686
|
-
registry ||= option('registry') ||
|
|
717
|
+
registry ||= option('registry') || self.registry unless id.include?('/')
|
|
687
718
|
end
|
|
688
|
-
|
|
719
|
+
op << shell_quote(tagjoin(registry, id))
|
|
689
720
|
when :push
|
|
690
721
|
id ||= option('tag', ignore: false) || op.shift || tagmain
|
|
691
|
-
registry ||= option('registry') || op.shift ||
|
|
722
|
+
registry ||= option('registry') || op.shift || self.registry
|
|
692
723
|
emptyargs op, flag
|
|
693
724
|
raise ArgumentError, message('username/registry not specified', hint: flag) unless registry
|
|
694
725
|
|
|
695
726
|
uri = shell_quote tagjoin(registry, id)
|
|
696
727
|
op << uri
|
|
697
|
-
img = docker_output 'image
|
|
728
|
+
img = docker_output 'image tag', id, uri
|
|
698
729
|
return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
|
|
699
730
|
|
|
700
731
|
@session = img
|
|
701
732
|
sync = false
|
|
702
|
-
exception
|
|
733
|
+
exception = true
|
|
703
734
|
banner = false
|
|
704
735
|
when :inspect, :history
|
|
705
736
|
raise ArgumentError, message('missing image', hint: flag) if op.empty?
|
|
@@ -711,16 +742,17 @@ module Squared
|
|
|
711
742
|
op.append(escape: true, strip: ':')
|
|
712
743
|
end
|
|
713
744
|
end
|
|
714
|
-
success?(run(sync: sync, exception: exception, banner: banner, from: from),
|
|
745
|
+
success?(run(sync: sync, exception: exception, banner: banner, from: from), status)
|
|
715
746
|
end
|
|
716
747
|
|
|
717
|
-
def network(flag, opts = [], target: nil)
|
|
748
|
+
def network(flag, opts = [], target: nil, name: target)
|
|
718
749
|
cmd, opts = docker_session('network', flag, opts: opts)
|
|
719
|
-
op = OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd,
|
|
750
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, **dockerinit)
|
|
720
751
|
.clear
|
|
721
752
|
from = symjoin 'network', flag
|
|
722
|
-
|
|
723
|
-
|
|
753
|
+
case flag
|
|
754
|
+
when :create, :rm
|
|
755
|
+
op.add_quote(name)
|
|
724
756
|
run(from: from)
|
|
725
757
|
else
|
|
726
758
|
list_image(flag, docker_output('ps -a'), from: from) do |id|
|
|
@@ -738,13 +770,13 @@ module Squared
|
|
|
738
770
|
end
|
|
739
771
|
|
|
740
772
|
def compose?(file = dockerfile)
|
|
741
|
-
return file
|
|
773
|
+
return file.between?(3, 4) if file.is_a?(Numeric)
|
|
742
774
|
|
|
743
775
|
COMPOSEFILE.include?(File.basename(file))
|
|
744
776
|
end
|
|
745
777
|
|
|
746
778
|
def bake?(file = dockerfile)
|
|
747
|
-
return file
|
|
779
|
+
return file.between?(1, 2) if file.is_a?(Numeric)
|
|
748
780
|
|
|
749
781
|
BAKEFILE.include?(File.basename(file))
|
|
750
782
|
end
|
|
@@ -756,18 +788,18 @@ module Squared
|
|
|
756
788
|
val.size > 1 ? val : val.first
|
|
757
789
|
elsif val == true
|
|
758
790
|
DIR_DOCKER.find { |file| exist?(file) }
|
|
759
|
-
elsif val !=
|
|
791
|
+
elsif val != DIR_DOCKER.last
|
|
760
792
|
val
|
|
761
793
|
end
|
|
762
794
|
end
|
|
763
|
-
basepath((@file.is_a?(Array) ? @file.first : @file) ||
|
|
795
|
+
basepath((@file.is_a?(Array) ? @file.first : @file) || DIR_DOCKER.last)
|
|
764
796
|
end
|
|
765
797
|
|
|
766
798
|
private
|
|
767
799
|
|
|
768
800
|
def read_composefile(*keys, target: nil)
|
|
769
801
|
require 'yaml'
|
|
770
|
-
target = ENV['COMPOSE_FILE']&.split(
|
|
802
|
+
target = ENV['COMPOSE_FILE']&.split(windows? ? ';' : ':') unless target && !target.empty?
|
|
771
803
|
Array(target || dockerfile).each do |val|
|
|
772
804
|
doc = YAML.load_file(basepath(val))
|
|
773
805
|
if keys.empty?
|
|
@@ -776,7 +808,7 @@ module Squared
|
|
|
776
808
|
yield data
|
|
777
809
|
end
|
|
778
810
|
rescue => e
|
|
779
|
-
|
|
811
|
+
print_error(e, subject: name, hint: 'compose')
|
|
780
812
|
end
|
|
781
813
|
end
|
|
782
814
|
|
|
@@ -784,7 +816,7 @@ module Squared
|
|
|
784
816
|
opts = OptionPartition.union(Array(opts), @client) if @client
|
|
785
817
|
return session('docker', *cmd) unless opts
|
|
786
818
|
|
|
787
|
-
op = OptionPartition.new(opts, OPT_DOCKER[:common],
|
|
819
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:common], **dockerinit)
|
|
788
820
|
[session('docker', *op.to_a, *cmd), op.extras]
|
|
789
821
|
end
|
|
790
822
|
|
|
@@ -792,12 +824,12 @@ module Squared
|
|
|
792
824
|
session('docker', *cmd, main: false, options: false, **kwargs)
|
|
793
825
|
end
|
|
794
826
|
|
|
795
|
-
def append_command(flag,
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
827
|
+
def append_command(flag, id, list, target: @session, prompt: ':')
|
|
828
|
+
if list.delete(prompt)
|
|
829
|
+
list << readline('Enter command [args]', force: flag == :exec)
|
|
830
|
+
elsif (val = env('DOCKER_ARGS'))
|
|
831
|
+
list << val
|
|
832
|
+
end
|
|
801
833
|
case flag
|
|
802
834
|
when :run
|
|
803
835
|
unless session_arg?('name', target: target)
|
|
@@ -806,8 +838,22 @@ module Squared
|
|
|
806
838
|
when :exec
|
|
807
839
|
raise ArgumentError, message('nothing to execute', hint: flag) if list.empty?
|
|
808
840
|
end
|
|
809
|
-
target <<
|
|
810
|
-
|
|
841
|
+
target << id
|
|
842
|
+
case list.first
|
|
843
|
+
when '--'
|
|
844
|
+
target.merge(list.drop(1))
|
|
845
|
+
when /\A(--|&&)(\s|\z)/
|
|
846
|
+
target.merge(list)
|
|
847
|
+
else
|
|
848
|
+
target << list.shift
|
|
849
|
+
unless list.empty?
|
|
850
|
+
if list.first.start_with?('-')
|
|
851
|
+
target.merge(list)
|
|
852
|
+
else
|
|
853
|
+
target << list.join(' && ')
|
|
854
|
+
end
|
|
855
|
+
end
|
|
856
|
+
end
|
|
811
857
|
end
|
|
812
858
|
|
|
813
859
|
def append_file(type, target: @session, index: 2, force: false)
|
|
@@ -839,13 +885,13 @@ module Squared
|
|
|
839
885
|
else
|
|
840
886
|
Array(val)
|
|
841
887
|
end.each do |s|
|
|
842
|
-
s =
|
|
888
|
+
s = tagname s, ver if ver && (!s.include?(':') || s.delete_suffix!(':latest'))
|
|
843
889
|
target << basic_option('tag', tagname(s))
|
|
844
890
|
end
|
|
845
891
|
target
|
|
846
892
|
end
|
|
847
893
|
|
|
848
|
-
def filter_ps(flag,
|
|
894
|
+
def filter_ps(flag, meth = nil, column: nil, from: meth || :'container:ps')
|
|
849
895
|
no = false
|
|
850
896
|
status = case flag.to_sym
|
|
851
897
|
when :start
|
|
@@ -871,6 +917,8 @@ module Squared
|
|
|
871
917
|
end
|
|
872
918
|
cmd = docker_output("#{from.to_s.split(':').first} ps -a",
|
|
873
919
|
*status.map { |s| quote_option('filter', "status=#{s}") })
|
|
920
|
+
return IO.popen(cmd.temp("--format=\"{{.#{column}}}\"")).map(&:chomp) if column
|
|
921
|
+
|
|
874
922
|
[cmd, status, no]
|
|
875
923
|
end
|
|
876
924
|
|
|
@@ -879,15 +927,15 @@ module Squared
|
|
|
879
927
|
pwd_set(from: from) do
|
|
880
928
|
index = 1
|
|
881
929
|
all = option('all', prefix: 'docker')
|
|
882
|
-
y = from
|
|
930
|
+
y = from.to_s.end_with?(':rm') && option('y', prefix: 'docker')
|
|
883
931
|
filter = env('DOCKER_FILTER', filter).to_s
|
|
884
932
|
pat = if OptionPartition.pattern?(filter)
|
|
885
933
|
Regexp.new(filter)
|
|
886
934
|
elsif filter.match?(/[:_-]$/)
|
|
887
935
|
/\b#{Regexp.escape(filter)}/
|
|
888
936
|
else
|
|
889
|
-
|
|
890
|
-
/\b(#{
|
|
937
|
+
names = [dnsname(name), subdomainname(name), tagname(project), tagmain.split(':').first]
|
|
938
|
+
/\b(#{names.join('|')})#{filter.empty? ? '([:_-]|$)' : "[:_-]#{filter}"}/
|
|
891
939
|
end
|
|
892
940
|
IO.popen(cmd.temp('--format=json')).each do |line|
|
|
893
941
|
data = JSON.parse(line)
|
|
@@ -915,17 +963,17 @@ module Squared
|
|
|
915
963
|
puts unless index == 1
|
|
916
964
|
puts (h + sub_style(aa, theme[:subject])).subhint("created #{e} ago")
|
|
917
965
|
cols = %w[Tag Status Ports]
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
966
|
+
.push(case flag
|
|
967
|
+
when :connect, :disconnect
|
|
968
|
+
'Networks'
|
|
969
|
+
else
|
|
970
|
+
'Size'
|
|
971
|
+
end)
|
|
972
|
+
.reject do |key|
|
|
973
|
+
(key == 'Tag' && !dd) || (key == 'Size' && data[key] == '0B') || data[key].to_s.empty?
|
|
974
|
+
end
|
|
975
|
+
pad = cols.map(&:size).max
|
|
976
|
+
cols.each { |key| puts "#{g + f} #{key.rjust(pad)}: #{Array(data[key]).join(', ')}" }
|
|
929
977
|
w = 9 + flag.to_s.size + 4 + ee.size
|
|
930
978
|
puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), theme[:inline])
|
|
931
979
|
index += 1
|
|
@@ -1008,7 +1056,7 @@ module Squared
|
|
|
1008
1056
|
cmd = docker_output(ctx, *action)
|
|
1009
1057
|
case flag
|
|
1010
1058
|
when :tag
|
|
1011
|
-
args = tagjoin
|
|
1059
|
+
args = tagjoin registry, tag
|
|
1012
1060
|
when :save
|
|
1013
1061
|
opts = opts.sub_ext('.tar')
|
|
1014
1062
|
cmd << quote_option('output', File.expand_path(opts))
|
|
@@ -1020,8 +1068,7 @@ module Squared
|
|
|
1020
1068
|
cmd << opts << '--'
|
|
1021
1069
|
end
|
|
1022
1070
|
cmd.merge(Array(out).map { |val| val.split(/\s+/, 2).first })
|
|
1023
|
-
cmd << args
|
|
1024
|
-
success?(run(cmd), ctx.start_with?('network', 'tag', 'save'))
|
|
1071
|
+
success?(run(cmd << args, { 'NO_COLOR' => 'true' }), ctx.start_with?('network', 'tag', 'save'))
|
|
1025
1072
|
end
|
|
1026
1073
|
|
|
1027
1074
|
def filetype(val = dockerfile)
|
|
@@ -1048,15 +1095,32 @@ module Squared
|
|
|
1048
1095
|
args.map { |val| val.chomp(char) }.join(char) unless args.empty?
|
|
1049
1096
|
end
|
|
1050
1097
|
|
|
1051
|
-
def tagname(val)
|
|
1052
|
-
val = val.
|
|
1098
|
+
def tagname(*val, max: 128)
|
|
1099
|
+
val = val.compact
|
|
1100
|
+
.join(':')
|
|
1101
|
+
.split(':')
|
|
1102
|
+
.map { |s| subdomainname(s) }
|
|
1053
1103
|
ret = val.join(':')
|
|
1054
|
-
ret
|
|
1055
|
-
|
|
1104
|
+
return ret unless ret.size > max
|
|
1105
|
+
|
|
1106
|
+
while val.size > 1
|
|
1107
|
+
val.pop
|
|
1108
|
+
s = val.join(':')
|
|
1109
|
+
return s unless s.size > max
|
|
1110
|
+
end
|
|
1111
|
+
ret[0...max]
|
|
1056
1112
|
end
|
|
1057
1113
|
|
|
1058
|
-
def dnsname(val)
|
|
1059
|
-
|
|
1114
|
+
def dnsname(val, max: 63, char: nil)
|
|
1115
|
+
ret = val.sub(/^[^a-z]+/i, '')
|
|
1116
|
+
.sub(/[^a-z\d]+$/i, '')
|
|
1117
|
+
.gsub(/[^a-z\d#{char}]+/i, '-')
|
|
1118
|
+
.gsub(/-{2,}/, '-')[0...max]
|
|
1119
|
+
char ? ret : ret.downcase
|
|
1120
|
+
end
|
|
1121
|
+
|
|
1122
|
+
def subdomainname(val)
|
|
1123
|
+
dnsname(val, max: 128, char: '.').gsub(/\.{2,}/, '.')
|
|
1060
1124
|
end
|
|
1061
1125
|
|
|
1062
1126
|
def charname(val)
|
|
@@ -1067,6 +1131,10 @@ module Squared
|
|
|
1067
1131
|
tag.is_a?(Array) ? tag.first : tag
|
|
1068
1132
|
end
|
|
1069
1133
|
|
|
1134
|
+
def dockerinit
|
|
1135
|
+
{ project: self, strict: strict?, explicit: '=', bool: %w[1 0 true false] }
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1070
1138
|
def emptyargs(list, hint = nil)
|
|
1071
1139
|
raise ArgumentError, message('unrecognized args', list.join(', '), hint: hint) unless list.empty?
|
|
1072
1140
|
end
|