squared 0.6.20 → 0.7.0
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 +70 -249
- data/README.md +242 -205
- data/lib/squared/common/base.rb +0 -8
- data/lib/squared/common/format.rb +7 -10
- data/lib/squared/common/prompt.rb +25 -30
- data/lib/squared/common/shell.rb +27 -35
- data/lib/squared/common/system.rb +29 -21
- data/lib/squared/common/utils.rb +45 -64
- data/lib/squared/config.rb +17 -16
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +292 -186
- data/lib/squared/workspace/project/base.rb +569 -500
- data/lib/squared/workspace/project/docker.rb +198 -219
- data/lib/squared/workspace/project/git.rb +244 -215
- data/lib/squared/workspace/project/node.rb +125 -161
- data/lib/squared/workspace/project/python.rb +250 -163
- data/lib/squared/workspace/project/ruby.rb +408 -298
- data/lib/squared/workspace/project/support/class.rb +13 -7
- data/lib/squared/workspace/project/support/optionpartition.rb +80 -90
- data/lib/squared/workspace/project/support/utils.rb +68 -0
- data/lib/squared/workspace/project.rb +0 -7
- data/lib/squared/workspace/repo.rb +235 -170
- data/lib/squared/workspace/series.rb +93 -89
- data/lib/squared/workspace/support/base.rb +15 -1
- metadata +3 -2
|
@@ -16,30 +16,31 @@ module Squared
|
|
|
16
16
|
cgroup-parent=b iidfile=p label=q network=b no-cache-filter=b o|output=q platform=q
|
|
17
17
|
q|quiet secret=qq shm-size=b ssh=qq t|tag=b target=b ulimit=q].freeze,
|
|
18
18
|
bake: %w[print list=q set=q].freeze,
|
|
19
|
-
shared: %w[check load no-cache pull push allow=
|
|
20
|
-
sbom=
|
|
19
|
+
shared: %w[check load no-cache pull push allow=q call=b? f|file=p metadata-file=p progress=b provenance=q
|
|
20
|
+
sbom=q].freeze
|
|
21
21
|
}.freeze,
|
|
22
22
|
compose: {
|
|
23
|
-
common: %w[
|
|
24
|
-
project-directory=p p|project-name=
|
|
23
|
+
common: %w[all-resources ansi|b compatibility dry-run env-file=p f|file=p parallel=n profile=b progress=b
|
|
24
|
+
project-directory=p p|project-name=e].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=b? user=e 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
30
|
cap-add=b cap-drop=b entrypoint=q e|env=qq env-from-file=p i|interactive=b? l|label=q name=b
|
|
31
|
-
T|no-
|
|
31
|
+
T|no-TTY=b? p|publish=q pull=b u|user=e v|volume=q 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
|
|
35
35
|
exit-code-from=b no-attach=b pull=b scale=i t|timeout=i wait-timeout=i].freeze,
|
|
36
|
-
down: %w[remove-orphans v|volumes rmi=b t|timeout=i].freeze
|
|
36
|
+
down: %w[remove-orphans v|volumes rmi=b t|timeout=i].freeze,
|
|
37
|
+
publish: %w[app resolve-image-digests with-env y|yes oci-version=b].freeze
|
|
37
38
|
}.freeze,
|
|
38
39
|
container: {
|
|
39
40
|
create: %w[init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet read-only
|
|
40
|
-
rm t|tty
|
|
41
|
-
|
|
42
|
-
device-
|
|
41
|
+
rm t|tty use-api-socket add-host=q annotation=q a|attach=b blkio-weight=i blkio-weight-device=i
|
|
42
|
+
cap-add=b cap-drop=b cgroup-parent=b cgroupns=b cidfile=p device=q device-cgroup-rule=q
|
|
43
|
+
device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
|
|
43
44
|
dns=q dns-option=q dns-search=q domainname=b entrypoint=q e|env=qq env-file=p expose=q gpus=q
|
|
44
45
|
group-add=b health-cmd=q health-interval=b health-retries=i health-start-interval=q
|
|
45
46
|
health-start-period=q health-timeout=q hostname=q io-maxbandwidth=b io-maxiops=b ip=b ip6=q ipc=b
|
|
@@ -47,14 +48,13 @@ module Squared
|
|
|
47
48
|
m|memory=b memory-reservation=b memory-swap=n memory-swappiness=n mount=qq name=b network=b
|
|
48
49
|
network-alias=b oom-score-adj=b pid=b pids-limit=n platform=q p|publish=q pull=b restart=b
|
|
49
50
|
runtime=b security-opt=q shm-size=b stop-signal=b stop-timeout=i storage-opt=q sysctl=q tmpfs=q
|
|
50
|
-
ulimit=q u|user=b userns=b uts=b v|volume=
|
|
51
|
+
ulimit=q u|user=b userns=b uts=b v|volume=q volume-driver=b volumes-from=b w|workdir=q].freeze,
|
|
51
52
|
run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
|
|
52
53
|
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
|
|
53
54
|
cpuset-cpus=b cpuset-mems=b m|memory=b memory-reservation=b memory-swap=n pids-limit=n
|
|
54
55
|
restart=q].freeze,
|
|
55
|
-
exec: %w[d|detach i|interactive privileged t|tty
|
|
56
|
+
exec: %w[d|detach i|interactive privileged t|tty detach-keys=q e|env=qq env-file=p u|user=e
|
|
56
57
|
w|workdir=q].freeze,
|
|
57
|
-
rm: %w[f|force l|link v|volumes].freeze,
|
|
58
58
|
commit: %w[no-pause a|author=q c|change=q m|message=q pause=b?].freeze,
|
|
59
59
|
inspect: %w[s|size f|format=q type=b].freeze,
|
|
60
60
|
start: %w[a|attach i|interactive detach-keys=q].freeze,
|
|
@@ -65,6 +65,7 @@ module Squared
|
|
|
65
65
|
}.freeze,
|
|
66
66
|
image: {
|
|
67
67
|
ls: %w[a|all digests no-trunc q|quiet tree f|filter=q format=q].freeze,
|
|
68
|
+
pull: %w[a|all-tags platform=q q|quiet].freeze,
|
|
68
69
|
push: %w[a|all-tags platform=q q|quiet].freeze,
|
|
69
70
|
rm: %w[f|force no-prune platform=q].freeze,
|
|
70
71
|
save: %w[o|output=p platform=q].freeze
|
|
@@ -77,10 +78,10 @@ module Squared
|
|
|
77
78
|
VAL_DOCKER = {
|
|
78
79
|
run: {
|
|
79
80
|
common: %w[source src destination dst target readonly ro].freeze,
|
|
80
|
-
bind: %w[bind-
|
|
81
|
+
bind: %w[bind-propagation].freeze,
|
|
81
82
|
volume: %w[volume-subpath volume-nocopy volume-opt].freeze,
|
|
82
83
|
tmpfs: %w[tmpfs-size tmpfs-mode].freeze,
|
|
83
|
-
image: %w[image-
|
|
84
|
+
image: %w[image-path].freeze
|
|
84
85
|
}.freeze,
|
|
85
86
|
ls: {
|
|
86
87
|
compose: %w[Name Image Command Service RunningFor Status Ports CreatedAt ExitCode Health ID Labels
|
|
@@ -108,9 +109,9 @@ module Squared
|
|
|
108
109
|
|
|
109
110
|
subtasks({
|
|
110
111
|
'build' => %i[tag context].freeze,
|
|
111
|
-
'compose' => %i[build create run exec up down service].freeze,
|
|
112
|
-
'bake' => %i[build check].freeze,
|
|
113
|
-
'image' => %i[ls rm push tag save].freeze,
|
|
112
|
+
'compose' => %i[build create publish run exec up down service].freeze,
|
|
113
|
+
'bake' => %i[build compose check].freeze,
|
|
114
|
+
'image' => %i[ls rm pull push tag save].freeze,
|
|
114
115
|
'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
|
|
115
116
|
rm].freeze,
|
|
116
117
|
'network' => %i[connect disconnect].freeze,
|
|
@@ -120,17 +121,19 @@ module Squared
|
|
|
120
121
|
attr_reader :context
|
|
121
122
|
attr_accessor :tag
|
|
122
123
|
|
|
123
|
-
def initialize(*,
|
|
124
|
+
def initialize(*, mounts: [], **kwargs)
|
|
124
125
|
super
|
|
125
|
-
|
|
126
|
+
self.global = nil
|
|
127
|
+
return unless dockerfile(kwargs[:file]).exist?
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
self.tag = kwargs[:tag] || tagname("#{@project}:#{@version || 'latest'}")
|
|
130
|
+
@context = kwargs[:context]
|
|
129
131
|
@mounts = mounts
|
|
130
|
-
@secrets = secrets
|
|
131
|
-
@registry = tagjoin registry, kwargs[:username]
|
|
132
|
+
@secrets = kwargs[:secrets]
|
|
133
|
+
@registry = tagjoin kwargs[:registry], kwargs[:username]
|
|
134
|
+
@oci = tagjoin kwargs[:oci], kwargs[:username]
|
|
132
135
|
initialize_ref Docker.ref
|
|
133
|
-
initialize_logger
|
|
136
|
+
initialize_logger kwargs[:log]
|
|
134
137
|
initialize_env(**kwargs)
|
|
135
138
|
@output[4] = merge_opts(kwargs[:args], @output[4]) if kwargs[:args]
|
|
136
139
|
end
|
|
@@ -164,25 +167,21 @@ module Squared
|
|
|
164
167
|
end
|
|
165
168
|
cmd << '-a' if has_value!(args, 'a', 'all') && command != 'network'
|
|
166
169
|
data = VAL_DOCKER[:ls][command.to_sym]
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
out.replace(choice_index('Select a column', data, multiple: true, attempts: 1))
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
cmd << quote_option('format', "table #{cols.map! { |val| "{{.#{val}}}" }.join("\t")}")
|
|
170
|
+
if has_value!(args, 's', 'standard')
|
|
171
|
+
cols = data.first(data.index('CreatedAt'))
|
|
172
|
+
else
|
|
173
|
+
cols = args.each_with_object([]) do |val, out|
|
|
174
|
+
if val =~ /^(\d+)$/
|
|
175
|
+
out << data[$1.to_i.pred]
|
|
176
|
+
elsif val =~ /^(\d+)(-|\.{2,3})(\d+)$/
|
|
177
|
+
j = $1.to_i.pred
|
|
178
|
+
k = $3.to_i - ($2 == '..' ? 2 : 1)
|
|
179
|
+
out.concat(data[j..k]) if k > j
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
cols = choice_index('Select a column', data, multiple: true, attempts: 1) if cols.empty?
|
|
183
|
+
end
|
|
184
|
+
cmd << quote_option('format', "table #{cols.map { |val| "{{.#{val}}}" }.join("\t")}")
|
|
186
185
|
run(cmd, banner: false, from: :ls)
|
|
187
186
|
end
|
|
188
187
|
end
|
|
@@ -200,14 +199,14 @@ module Squared
|
|
|
200
199
|
break unless bake?
|
|
201
200
|
|
|
202
201
|
case flag
|
|
203
|
-
when :build
|
|
204
|
-
format_desc action, flag, 'opts*,target*,context
|
|
202
|
+
when :build, :compose
|
|
203
|
+
format_desc action, flag, 'opts*,target*,context?|:'
|
|
205
204
|
task flag do |_, args|
|
|
206
205
|
args = args.to_a
|
|
207
206
|
if args.first == ':'
|
|
208
207
|
choice_command :bake
|
|
209
208
|
else
|
|
210
|
-
buildx
|
|
209
|
+
buildx(:bake, args, from: (:'buildx:bake' if flag == :compose))
|
|
211
210
|
end
|
|
212
211
|
end
|
|
213
212
|
when :check
|
|
@@ -240,6 +239,13 @@ module Squared
|
|
|
240
239
|
compose!(flag, [command], service: service.empty? || service)
|
|
241
240
|
end
|
|
242
241
|
end
|
|
242
|
+
when :publish
|
|
243
|
+
next unless @oci
|
|
244
|
+
|
|
245
|
+
format_desc action, flag, 'tag?,repository?,opts*'
|
|
246
|
+
task flag, [:tag] do |_, args|
|
|
247
|
+
compose! flag, args.to_a
|
|
248
|
+
end
|
|
243
249
|
else
|
|
244
250
|
format_desc action, flag, 'opts*,service*|:'
|
|
245
251
|
task flag do |_, args|
|
|
@@ -268,7 +274,7 @@ module Squared
|
|
|
268
274
|
end
|
|
269
275
|
end
|
|
270
276
|
else
|
|
271
|
-
format_desc action, flag, "opts*,id/name#{flag == :update
|
|
277
|
+
format_desc action, flag, "opts*,id/name#{flag == :update ? '+' : '*'}"
|
|
272
278
|
task flag do |_, args|
|
|
273
279
|
container flag, args.to_a
|
|
274
280
|
end
|
|
@@ -276,6 +282,13 @@ module Squared
|
|
|
276
282
|
when 'image'
|
|
277
283
|
case flag
|
|
278
284
|
when :push
|
|
285
|
+
next unless @registry
|
|
286
|
+
|
|
287
|
+
format_desc action, flag, 'tag?,registry/username?,opts*'
|
|
288
|
+
task flag, [:tag] do |_, args|
|
|
289
|
+
image flag, args.to_a
|
|
290
|
+
end
|
|
291
|
+
when :pull
|
|
279
292
|
format_desc action, flag, 'tag,registry/username?,opts*'
|
|
280
293
|
task flag, [:tag] do |_, args|
|
|
281
294
|
id = param_guard(action, flag, args: args, key: :tag)
|
|
@@ -283,18 +296,14 @@ module Squared
|
|
|
283
296
|
end
|
|
284
297
|
else
|
|
285
298
|
format_desc(action, flag, case flag
|
|
286
|
-
when :rm, :save then 'id
|
|
287
|
-
when :tag then 'version'
|
|
299
|
+
when :rm, :save then 'id*,opts*'
|
|
300
|
+
when :tag then 'version?'
|
|
288
301
|
else 'opts*,args*'
|
|
289
|
-
end
|
|
302
|
+
end)
|
|
290
303
|
task flag do |_, args|
|
|
291
304
|
args = args.to_a
|
|
292
|
-
n = args.size
|
|
293
|
-
if (n > 1 || (flag == :ls && n > 0)) && OptionPartition.pattern?(args.first)
|
|
294
|
-
filter = args.shift
|
|
295
|
-
end
|
|
296
305
|
if !args.empty? || flag == :ls
|
|
297
|
-
image
|
|
306
|
+
image flag, args
|
|
298
307
|
else
|
|
299
308
|
choice_command flag
|
|
300
309
|
end
|
|
@@ -320,8 +329,8 @@ module Squared
|
|
|
320
329
|
def clean(*, sync: invoked_sync?('clean'), **)
|
|
321
330
|
if runnable?(@clean)
|
|
322
331
|
super
|
|
323
|
-
|
|
324
|
-
image :
|
|
332
|
+
else
|
|
333
|
+
image(:rm, sync: sync)
|
|
325
334
|
end
|
|
326
335
|
end
|
|
327
336
|
|
|
@@ -354,7 +363,7 @@ module Squared
|
|
|
354
363
|
[args, flags].each_with_index do |item, i|
|
|
355
364
|
next unless item && (data = append_any(item, target: []))
|
|
356
365
|
|
|
357
|
-
ret.merge(data.map
|
|
366
|
+
ret.merge(data.map { |arg| i == 0 ? fill_option(arg) : quote_option('build-arg', arg) })
|
|
358
367
|
end
|
|
359
368
|
case from
|
|
360
369
|
when :run
|
|
@@ -362,13 +371,13 @@ module Squared
|
|
|
362
371
|
when String
|
|
363
372
|
ret << quote_option('secret', @secrets, double: true)
|
|
364
373
|
when Hash
|
|
365
|
-
append = lambda do |
|
|
366
|
-
Array(@secrets[
|
|
374
|
+
append = lambda do |key|
|
|
375
|
+
ret.merge(Array(@secrets[key]).map { |arg| quote_option('secret', "type=#{key},#{arg}", double: true) })
|
|
367
376
|
end
|
|
368
377
|
append.call(:file)
|
|
369
378
|
append.call(:env)
|
|
370
379
|
else
|
|
371
|
-
Array(@secrets).
|
|
380
|
+
ret.merge(Array(@secrets).map { |arg| quote_option('secret', arg) })
|
|
372
381
|
end
|
|
373
382
|
if (val = option('tag', ignore: false))
|
|
374
383
|
append_tag val
|
|
@@ -382,40 +391,38 @@ module Squared
|
|
|
382
391
|
ret
|
|
383
392
|
end
|
|
384
393
|
|
|
385
|
-
def buildx(flag, opts = [], tag: nil, context: nil)
|
|
394
|
+
def buildx(flag, opts = [], tag: nil, context: nil, from: nil)
|
|
386
395
|
cmd, opts = docker_session('buildx', opts: opts)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
end
|
|
396
|
+
data = OPT_DOCKER[:buildx]
|
|
397
|
+
op = OptionPartition.new(opts, data[:common], cmd, project: self)
|
|
398
|
+
op.append(flag, quote: false)
|
|
399
|
+
.parse(data[flag == :bake ? :bake : :build] + data[:shared])
|
|
392
400
|
case flag
|
|
393
401
|
when :build, :context
|
|
394
402
|
append_tag(tag || option('tag', ignore: false) || self.tag)
|
|
395
403
|
append_context context
|
|
396
404
|
when :bake
|
|
397
|
-
|
|
398
|
-
op.shift
|
|
399
|
-
op << @output[4] if @output[4]
|
|
400
|
-
end
|
|
405
|
+
append_file(0, index: 3) unless from || op.arg?('f', 'file') || !anypath?(*COMPOSEFILE)
|
|
401
406
|
unless op.empty?
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
407
|
+
args = op.dup
|
|
408
|
+
op.reset
|
|
409
|
+
if Dir.exist?(args.last)
|
|
410
|
+
if projectpath?(val = args.pop)
|
|
411
|
+
context = val
|
|
412
|
+
else
|
|
413
|
+
op.push(val)
|
|
414
|
+
end
|
|
409
415
|
end
|
|
410
|
-
op.append(escape: true, strip:
|
|
416
|
+
op.append(args, escape: true, strip: /^:/)
|
|
417
|
+
contextdir context if context
|
|
411
418
|
end
|
|
412
419
|
end
|
|
413
420
|
op.clear(pass: false)
|
|
414
|
-
run(from:
|
|
421
|
+
run(from: from || symjoin('buildx', flag))
|
|
415
422
|
end
|
|
416
423
|
|
|
417
|
-
def compose!(flag, opts = [], service: nil, multiple: false)
|
|
418
|
-
from =
|
|
424
|
+
def compose!(flag, opts = [], id: nil, service: nil, multiple: false)
|
|
425
|
+
from = symjoin 'compose', flag
|
|
419
426
|
if flag == :service
|
|
420
427
|
command = opts.first
|
|
421
428
|
if service == true
|
|
@@ -429,99 +436,94 @@ module Squared
|
|
|
429
436
|
else
|
|
430
437
|
cmd, opts = docker_session('compose', opts: opts)
|
|
431
438
|
op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self)
|
|
432
|
-
append_file
|
|
439
|
+
append_file(filetype, force: flag == :publish) unless op.arg?('f', 'file')
|
|
433
440
|
op << flag
|
|
434
441
|
op.parse(OPT_DOCKER[:compose].fetch(flag, []))
|
|
435
|
-
if
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
end
|
|
442
|
-
end
|
|
443
|
-
if multiple
|
|
444
|
-
op.concat(service) if service
|
|
445
|
-
op.append(delim: true, escape: true, strip: /^:/)
|
|
442
|
+
if flag == :publish
|
|
443
|
+
id ||= option('tag', ignore: false) || op.shift || tagmain
|
|
444
|
+
registry ||= option('registry') || op.shift || @oci
|
|
445
|
+
emptyargs op, flag
|
|
446
|
+
op << shell_quote(tagjoin(registry, id))
|
|
447
|
+
return unless confirm_command(op.to_s, title: from, target: id)
|
|
446
448
|
else
|
|
447
|
-
|
|
448
|
-
|
|
449
|
+
if op.remove(':') || service == ':'
|
|
450
|
+
keys = Set.new
|
|
451
|
+
read_composefile('services', target: op.values_of('f', 'file')) { |data| keys.merge(data.keys) }
|
|
452
|
+
service = unless keys.empty?
|
|
453
|
+
choice_index('Add services', keys, multiple: multiple, force: !multiple,
|
|
454
|
+
attempts: multiple ? 1 : 3)
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
if multiple
|
|
458
|
+
op.concat(service) if service
|
|
459
|
+
op.append(delim: true, escape: true, strip: /^:/)
|
|
460
|
+
else
|
|
461
|
+
raise_error ArgumentError, 'no service was selected', hint: flag unless service
|
|
462
|
+
append_command(flag, service, op.extras, prompt: '::')
|
|
463
|
+
end
|
|
449
464
|
end
|
|
450
465
|
end
|
|
451
466
|
run(from: from)
|
|
452
467
|
end
|
|
453
|
-
alias compose_ compose!
|
|
454
468
|
|
|
455
469
|
def container(flag, opts = [], id: nil)
|
|
456
470
|
cmd, opts = docker_session('container', flag, opts: opts)
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
end
|
|
464
|
-
from = :"container:#{flag}"
|
|
471
|
+
data = OPT_DOCKER[:container]
|
|
472
|
+
list = data.fetch(flag, [])
|
|
473
|
+
list += data[:create] if (rc = flag == :run)
|
|
474
|
+
list += data[:update] if rc ||= flag == :create
|
|
475
|
+
op = OptionPartition.new(opts, list, cmd, project: self, args: rc || flag == :exec)
|
|
476
|
+
from = symjoin 'container', flag
|
|
465
477
|
case flag
|
|
466
478
|
when :run, :create, :exec
|
|
467
479
|
if rc && !op.arg?('mount')
|
|
468
480
|
all = collect_hash VAL_DOCKER[:run]
|
|
469
481
|
delim = Regexp.new(",\\s*(?=#{all.join('|')})")
|
|
470
482
|
Array(@mounts).each do |val|
|
|
471
|
-
args = []
|
|
472
483
|
type = nil
|
|
473
|
-
val.split(delim).
|
|
474
|
-
k, v, q =
|
|
484
|
+
args = val.split(delim).each_with_object([]) do |opt, out|
|
|
485
|
+
k, v, q = OptionPartition.parse_option(opt)
|
|
475
486
|
if k == 'type'
|
|
476
487
|
case v
|
|
477
488
|
when 'bind', 'volume', 'image', 'tmpfs'
|
|
478
489
|
type = v
|
|
479
490
|
else
|
|
480
|
-
raise_error TypeError, "unknown: #{v}", hint: flag
|
|
491
|
+
raise_error TypeError, "unknown: #{v || "''"}", hint: flag
|
|
481
492
|
end
|
|
482
493
|
elsif all.include?(k)
|
|
483
494
|
unless type
|
|
484
|
-
VAL_DOCKER[:run].each_pair do |key,
|
|
485
|
-
next unless
|
|
495
|
+
VAL_DOCKER[:run].each_pair do |key, items|
|
|
496
|
+
next unless items.include?(k)
|
|
486
497
|
|
|
487
498
|
type = key.to_s unless key == :common
|
|
488
499
|
break
|
|
489
500
|
end
|
|
490
501
|
end
|
|
491
502
|
case k
|
|
492
|
-
when 'readonly', 'ro'
|
|
493
|
-
|
|
503
|
+
when 'readonly', 'ro'
|
|
504
|
+
out << k
|
|
494
505
|
next
|
|
495
|
-
when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-
|
|
496
|
-
raise_error ArgumentError, "no path value
|
|
497
|
-
|
|
498
|
-
when 'source', 'src'
|
|
499
|
-
if type == 'bind'
|
|
500
|
-
v = basepath v
|
|
501
|
-
elsif type == 'tmpfs'
|
|
502
|
-
next
|
|
503
|
-
end
|
|
504
|
-
when 'destination', 'dst', 'target'
|
|
505
|
-
puts log_message("path is not absolute: #{v}", subject: from, hint: k) unless v.start_with?('/')
|
|
506
|
-
end
|
|
506
|
+
when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-path'
|
|
507
|
+
raise_error ArgumentError, "#{k}: no path value", hint: flag unless v
|
|
508
|
+
v = basepath v
|
|
507
509
|
v = shell_quote(v, option: false, force: false) if q == ''
|
|
508
510
|
end
|
|
509
|
-
|
|
511
|
+
out << "#{k}=#{q}#{v}#{q}"
|
|
510
512
|
elsif !silent?
|
|
511
|
-
|
|
513
|
+
log_message('unrecognized option', subject: from, hint: k)
|
|
512
514
|
end
|
|
513
515
|
end
|
|
514
516
|
raise_error TypeError, 'none specified', hint: flag unless type
|
|
515
|
-
|
|
517
|
+
cmd << "--mount type=#{type},#{args.join(',')}"
|
|
516
518
|
end
|
|
517
519
|
end
|
|
518
520
|
append_command(flag, id || tagmain, op.extras)
|
|
519
|
-
when :update
|
|
521
|
+
when :update
|
|
520
522
|
raise_error ArgumentError, 'missing container', hint: flag if op.empty?
|
|
521
523
|
op.append(escape: true, strip: /^:/)
|
|
522
524
|
when :commit
|
|
523
525
|
latest = op.shift || tagmain
|
|
524
|
-
|
|
526
|
+
cmd << id << latest
|
|
525
527
|
raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
|
|
526
528
|
return unless confirm_command(cmd.to_s, title: from, target: id, as: latest)
|
|
527
529
|
|
|
@@ -532,7 +534,7 @@ module Squared
|
|
|
532
534
|
opts = []
|
|
533
535
|
append_option('platform', target: opts, equals: true)
|
|
534
536
|
opts << case option('disable-content-trust', ignore: false)
|
|
535
|
-
when '
|
|
537
|
+
when '0', 'false'
|
|
536
538
|
'--disable-content-trust=false'
|
|
537
539
|
else
|
|
538
540
|
'--disable-content-trust'
|
|
@@ -542,7 +544,7 @@ module Squared
|
|
|
542
544
|
else
|
|
543
545
|
if op.empty?
|
|
544
546
|
ps, status, no = filter_ps flag, from
|
|
545
|
-
|
|
547
|
+
cmd << '--no-stream' if flag == :stats
|
|
546
548
|
list_image(flag, ps, no: no, hint: status, from: from) { |img| run(cmd.temp(img), from: from) }
|
|
547
549
|
return
|
|
548
550
|
end
|
|
@@ -551,12 +553,12 @@ module Squared
|
|
|
551
553
|
run(from: from)
|
|
552
554
|
end
|
|
553
555
|
|
|
554
|
-
def image(flag, opts = [], sync: true, id: nil, registry: nil
|
|
556
|
+
def image(flag, opts = [], sync: true, id: nil, registry: nil)
|
|
555
557
|
cmd, opts = docker_session('image', flag, opts: opts)
|
|
556
558
|
op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
|
|
557
|
-
exception =
|
|
559
|
+
exception = exception?
|
|
558
560
|
banner = true
|
|
559
|
-
from =
|
|
561
|
+
from = symjoin 'image', flag
|
|
560
562
|
case flag
|
|
561
563
|
when :ls
|
|
562
564
|
if opts.size == op.size
|
|
@@ -569,7 +571,7 @@ module Squared
|
|
|
569
571
|
opts.delete(val)
|
|
570
572
|
break
|
|
571
573
|
end
|
|
572
|
-
list_image(:run,
|
|
574
|
+
list_image(:run, from: from) do |val|
|
|
573
575
|
container(:run, if name
|
|
574
576
|
opts.dup << "name=#{index == 0 ? name : "#{name}-#{index}"}"
|
|
575
577
|
else
|
|
@@ -583,7 +585,7 @@ module Squared
|
|
|
583
585
|
when :rm
|
|
584
586
|
unless id
|
|
585
587
|
if op.empty?
|
|
586
|
-
list_image(:rm,
|
|
588
|
+
list_image(:rm, from: from) { |val| image(:rm, opts, sync: sync, id: val) }
|
|
587
589
|
else
|
|
588
590
|
op.each { |val| run(cmd.temp(val), sync: sync, from: from) }
|
|
589
591
|
end
|
|
@@ -595,51 +597,48 @@ module Squared
|
|
|
595
597
|
banner = false
|
|
596
598
|
end
|
|
597
599
|
when :tag, :save
|
|
598
|
-
|
|
599
|
-
list_image(flag, filter: filter, from: from) do |val|
|
|
600
|
+
list_image(flag, from: from) do |val|
|
|
600
601
|
op << val
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
end
|
|
602
|
+
next unless flag == :tag
|
|
603
|
+
|
|
604
|
+
op << tagname("#{project}:#{op.first}")
|
|
605
|
+
break
|
|
606
606
|
end
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
end
|
|
607
|
+
when :pull
|
|
608
|
+
if !id
|
|
609
|
+
id = tagmain
|
|
610
|
+
elsif !op.arg?('a', 'all-tags') && !id.include?(':')
|
|
611
|
+
id = "#{project}:#{id}"
|
|
612
|
+
end
|
|
613
|
+
unless registry
|
|
614
|
+
registry = op.shift
|
|
615
|
+
registry ||= option('registry') || @registry unless id.include?('/')
|
|
617
616
|
end
|
|
617
|
+
cmd << shell_quote(tagjoin(registry, id))
|
|
618
|
+
when :push
|
|
619
|
+
id ||= option('tag', ignore: false) || op.shift || tagmain
|
|
620
|
+
registry ||= option('registry') || op.shift || @registry
|
|
621
|
+
emptyargs op, flag
|
|
618
622
|
raise_error ArgumentError, 'username/registry not specified', hint: flag unless registry
|
|
619
|
-
registry
|
|
620
|
-
uri = shell_quote "#{registry}/#{id}"
|
|
623
|
+
uri = shell_quote tagjoin(registry, id)
|
|
621
624
|
op << uri
|
|
622
625
|
img = docker_output 'image', 'tag', id, uri
|
|
623
626
|
return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
|
|
624
627
|
|
|
625
|
-
|
|
628
|
+
@session = img
|
|
626
629
|
sync = false
|
|
627
|
-
exception
|
|
630
|
+
exception ||= true
|
|
628
631
|
banner = false
|
|
629
632
|
end
|
|
630
|
-
run(
|
|
631
|
-
success?(ret, flag == :tag || flag == :save)
|
|
632
|
-
end
|
|
633
|
+
success?(run(sync: sync, exception: exception, banner: banner, from: from), flag == :tag || flag == :save)
|
|
633
634
|
end
|
|
634
635
|
|
|
635
636
|
def network(flag, opts = [], target: nil)
|
|
636
637
|
cmd, opts = docker_session('network', flag, opts: opts)
|
|
637
638
|
OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
|
|
638
639
|
.clear
|
|
639
|
-
from =
|
|
640
|
-
list_image(flag, docker_output('ps -a'), from: from)
|
|
641
|
-
success?(run(cmd.temp(target, img), from: from))
|
|
642
|
-
end
|
|
640
|
+
from = symjoin 'network', flag
|
|
641
|
+
list_image(flag, docker_output('ps -a'), from: from) { |id| success?(run(cmd.temp(target, id), from: from)) }
|
|
643
642
|
end
|
|
644
643
|
|
|
645
644
|
def build?
|
|
@@ -688,7 +687,7 @@ module Squared
|
|
|
688
687
|
elsif (data = doc.dig(*keys))
|
|
689
688
|
yield data
|
|
690
689
|
end
|
|
691
|
-
rescue
|
|
690
|
+
rescue => e
|
|
692
691
|
log.debug e
|
|
693
692
|
end
|
|
694
693
|
end
|
|
@@ -705,11 +704,11 @@ module Squared
|
|
|
705
704
|
end
|
|
706
705
|
|
|
707
706
|
def append_command(flag, val, list, target: @session, prompt: ':')
|
|
708
|
-
if list.delete(prompt)
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
707
|
+
list << if list.delete(prompt)
|
|
708
|
+
readline('Enter command [args]', force: flag == :exec)
|
|
709
|
+
else
|
|
710
|
+
env('DOCKER_ARGS')
|
|
711
|
+
end
|
|
713
712
|
case flag
|
|
714
713
|
when :run
|
|
715
714
|
unless session_arg?('name', target: target)
|
|
@@ -718,28 +717,14 @@ module Squared
|
|
|
718
717
|
when :exec
|
|
719
718
|
raise_error ArgumentError, 'nothing to execute', hint: flag if list.empty?
|
|
720
719
|
end
|
|
721
|
-
target << val
|
|
722
|
-
|
|
723
|
-
when '--'
|
|
724
|
-
target.merge(list.drop(1))
|
|
725
|
-
when /\A(--|&&)(\s|\z)/
|
|
726
|
-
target.merge(list)
|
|
727
|
-
else
|
|
728
|
-
target << list.shift
|
|
729
|
-
unless list.empty?
|
|
730
|
-
if list.first.start_with?('-')
|
|
731
|
-
target.merge(list)
|
|
732
|
-
else
|
|
733
|
-
target << list.join(' && ')
|
|
734
|
-
end
|
|
735
|
-
end
|
|
736
|
-
end
|
|
720
|
+
target << val << list.shift
|
|
721
|
+
target << list.join(' && ') unless list.empty?
|
|
737
722
|
end
|
|
738
723
|
|
|
739
|
-
def append_file(type, target: @session, index: 2)
|
|
740
|
-
return
|
|
724
|
+
def append_file(type, target: @session, index: 2, force: false)
|
|
725
|
+
return unless @file && !(ENV['COMPOSE_FILE'] && compose?(type))
|
|
741
726
|
|
|
742
|
-
unless @file.is_a?(Array)
|
|
727
|
+
unless @file.is_a?(Array) || force
|
|
743
728
|
case type
|
|
744
729
|
when 2, 4
|
|
745
730
|
return
|
|
@@ -747,14 +732,7 @@ module Squared
|
|
|
747
732
|
return unless COMPOSEFILE.select { |val| basepath!(val) }.size > 1
|
|
748
733
|
end
|
|
749
734
|
end
|
|
750
|
-
|
|
751
|
-
if target.is_a?(Set)
|
|
752
|
-
opts = target.to_a.insert(index, *files)
|
|
753
|
-
target.clear
|
|
754
|
-
.merge(opts)
|
|
755
|
-
else
|
|
756
|
-
target.insert(index, *files)
|
|
757
|
-
end
|
|
735
|
+
target.insert(index, *Array(@file).map { |val| quote_option('file', basepath(val)) })
|
|
758
736
|
end
|
|
759
737
|
|
|
760
738
|
def append_context(ctx = nil, target: @session)
|
|
@@ -768,7 +746,7 @@ module Squared
|
|
|
768
746
|
ver = option('version', target: target, ignore: false)
|
|
769
747
|
case val
|
|
770
748
|
when String
|
|
771
|
-
|
|
749
|
+
val.split(',')
|
|
772
750
|
else
|
|
773
751
|
Array(val)
|
|
774
752
|
end.each do |s|
|
|
@@ -807,20 +785,12 @@ module Squared
|
|
|
807
785
|
[cmd, status, no]
|
|
808
786
|
end
|
|
809
787
|
|
|
810
|
-
def list_image(flag, cmd = docker_output('image ls -a'),
|
|
788
|
+
def list_image(flag, cmd = docker_output('image ls -a'), hint: nil, no: true, from: nil)
|
|
811
789
|
pwd_set(from: from) do
|
|
812
790
|
index = 1
|
|
813
791
|
all = option('all', prefix: 'docker')
|
|
814
792
|
y = from == :'image:rm' && option('y', prefix: 'docker')
|
|
815
|
-
|
|
816
|
-
pat = if OptionPartition.pattern?(filter)
|
|
817
|
-
Regexp.new(filter)
|
|
818
|
-
elsif filter.match?(/[:_-]$/)
|
|
819
|
-
/\b#{Regexp.escape(filter)}/
|
|
820
|
-
else
|
|
821
|
-
filter = filter.empty? ? '(?:[:_-]|$)' : "[:_-]#{filter}"
|
|
822
|
-
/\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})#{filter}/
|
|
823
|
-
end
|
|
793
|
+
pat = /\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})\b/
|
|
824
794
|
IO.popen(cmd.temp('--format=json')).each do |line|
|
|
825
795
|
data = JSON.parse(line)
|
|
826
796
|
id = data['ID']
|
|
@@ -869,7 +839,7 @@ module Squared
|
|
|
869
839
|
end
|
|
870
840
|
list_empty(hint: hint || from) if index == 1 && !y
|
|
871
841
|
end
|
|
872
|
-
rescue
|
|
842
|
+
rescue => e
|
|
873
843
|
on_error e, from
|
|
874
844
|
end
|
|
875
845
|
|
|
@@ -903,11 +873,11 @@ module Squared
|
|
|
903
873
|
when :service
|
|
904
874
|
['Choose a service',
|
|
905
875
|
'compose ps -a ' \
|
|
906
|
-
|
|
876
|
+
"--format='table {{.Service}}\t{{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Status}}\t{{.Ports}}'"]
|
|
907
877
|
else
|
|
908
878
|
['Choose an image',
|
|
909
879
|
'images -a ' \
|
|
910
|
-
|
|
880
|
+
"--format='table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}\t{{.Size}}'"]
|
|
911
881
|
end
|
|
912
882
|
lines = `#{docker_output(cmd)}`.lines
|
|
913
883
|
if lines.size <= 1
|
|
@@ -941,18 +911,18 @@ module Squared
|
|
|
941
911
|
when :tag
|
|
942
912
|
args = tagjoin @registry, tag
|
|
943
913
|
when :save
|
|
944
|
-
opts =
|
|
914
|
+
opts = opts.sub_ext('.tar')
|
|
945
915
|
cmd << quote_option('output', File.expand_path(opts))
|
|
946
916
|
if args
|
|
947
|
-
cmd <<
|
|
917
|
+
cmd << quote_option('platform', args)
|
|
948
918
|
args = nil
|
|
949
919
|
end
|
|
950
920
|
else
|
|
951
921
|
cmd << opts << '--'
|
|
952
922
|
end
|
|
953
|
-
cmd.merge(Array(out).map
|
|
923
|
+
cmd.merge(Array(out).map { |val| val.split(/\s+/, 2).first })
|
|
954
924
|
cmd << args
|
|
955
|
-
success?(run(cmd
|
|
925
|
+
success?(run(cmd), ctx.start_with?('network', 'tag', 'save'))
|
|
956
926
|
end
|
|
957
927
|
|
|
958
928
|
def filetype(val = dockerfile)
|
|
@@ -975,11 +945,12 @@ module Squared
|
|
|
975
945
|
end
|
|
976
946
|
|
|
977
947
|
def tagjoin(*args, char: '/')
|
|
978
|
-
args.
|
|
948
|
+
args.compact!
|
|
949
|
+
args.map { |val| val.chomp(char) }.join(char) unless args.empty?
|
|
979
950
|
end
|
|
980
951
|
|
|
981
952
|
def tagname(val)
|
|
982
|
-
val = val.split(':').map
|
|
953
|
+
val = val.split(':').map { |s| charname(s.sub(/^\W+/, '')) }
|
|
983
954
|
ret = val.join(':')
|
|
984
955
|
ret = val.first if val.size > 1 && ret.size > 128
|
|
985
956
|
ret[0..127]
|
|
@@ -996,6 +967,14 @@ module Squared
|
|
|
996
967
|
def tagmain
|
|
997
968
|
tag.is_a?(Array) ? tag.first : tag
|
|
998
969
|
end
|
|
970
|
+
|
|
971
|
+
def emptyargs(list, hint = nil)
|
|
972
|
+
raise_error ArgumentError, "unrecognized args: #{list.join(', ')}", hint: hint unless list.empty?
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
def anypath?(*args)
|
|
976
|
+
args.any? { |val| basepath!(val) }
|
|
977
|
+
end
|
|
999
978
|
end
|
|
1000
979
|
|
|
1001
980
|
Application.implement Docker
|