squared 0.6.18 → 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 +69 -213
- 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 +26 -28
- 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 +187 -198
- data/lib/squared/workspace/project/git.rb +241 -212
- data/lib/squared/workspace/project/node.rb +119 -155
- 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 +76 -82
- data/lib/squared/workspace/project/support/utils.rb +68 -0
- data/lib/squared/workspace/project.rb +0 -7
- data/lib/squared/workspace/repo.rb +234 -169
- data/lib/squared/workspace/series.rb +93 -89
- data/lib/squared/workspace/support/base.rb +15 -1
- metadata +3 -2
|
@@ -20,26 +20,27 @@ module Squared
|
|
|
20
20
|
sbom=q].freeze
|
|
21
21
|
}.freeze,
|
|
22
22
|
compose: {
|
|
23
|
-
common: %w[all-resources ansi
|
|
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
|
|
@@ -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,60 +436,64 @@ 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
|
|
@@ -490,13 +501,14 @@ module Squared
|
|
|
490
501
|
end
|
|
491
502
|
case k
|
|
492
503
|
when 'readonly', 'ro'
|
|
493
|
-
|
|
504
|
+
out << k
|
|
494
505
|
next
|
|
495
506
|
when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-path'
|
|
507
|
+
raise_error ArgumentError, "#{k}: no path value", hint: flag unless v
|
|
496
508
|
v = basepath v
|
|
497
509
|
v = shell_quote(v, option: false, force: false) if q == ''
|
|
498
510
|
end
|
|
499
|
-
|
|
511
|
+
out << "#{k}=#{q}#{v}#{q}"
|
|
500
512
|
elsif !silent?
|
|
501
513
|
log_message('unrecognized option', subject: from, hint: k)
|
|
502
514
|
end
|
|
@@ -506,7 +518,7 @@ module Squared
|
|
|
506
518
|
end
|
|
507
519
|
end
|
|
508
520
|
append_command(flag, id || tagmain, op.extras)
|
|
509
|
-
when :update
|
|
521
|
+
when :update
|
|
510
522
|
raise_error ArgumentError, 'missing container', hint: flag if op.empty?
|
|
511
523
|
op.append(escape: true, strip: /^:/)
|
|
512
524
|
when :commit
|
|
@@ -522,7 +534,7 @@ module Squared
|
|
|
522
534
|
opts = []
|
|
523
535
|
append_option('platform', target: opts, equals: true)
|
|
524
536
|
opts << case option('disable-content-trust', ignore: false)
|
|
525
|
-
when '
|
|
537
|
+
when '0', 'false'
|
|
526
538
|
'--disable-content-trust=false'
|
|
527
539
|
else
|
|
528
540
|
'--disable-content-trust'
|
|
@@ -541,12 +553,12 @@ module Squared
|
|
|
541
553
|
run(from: from)
|
|
542
554
|
end
|
|
543
555
|
|
|
544
|
-
def image(flag, opts = [], sync: true, id: nil, registry: nil
|
|
556
|
+
def image(flag, opts = [], sync: true, id: nil, registry: nil)
|
|
545
557
|
cmd, opts = docker_session('image', flag, opts: opts)
|
|
546
558
|
op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
|
|
547
|
-
exception =
|
|
559
|
+
exception = exception?
|
|
548
560
|
banner = true
|
|
549
|
-
from =
|
|
561
|
+
from = symjoin 'image', flag
|
|
550
562
|
case flag
|
|
551
563
|
when :ls
|
|
552
564
|
if opts.size == op.size
|
|
@@ -559,7 +571,7 @@ module Squared
|
|
|
559
571
|
opts.delete(val)
|
|
560
572
|
break
|
|
561
573
|
end
|
|
562
|
-
list_image(:run,
|
|
574
|
+
list_image(:run, from: from) do |val|
|
|
563
575
|
container(:run, if name
|
|
564
576
|
opts.dup << "name=#{index == 0 ? name : "#{name}-#{index}"}"
|
|
565
577
|
else
|
|
@@ -573,7 +585,7 @@ module Squared
|
|
|
573
585
|
when :rm
|
|
574
586
|
unless id
|
|
575
587
|
if op.empty?
|
|
576
|
-
list_image(:rm,
|
|
588
|
+
list_image(:rm, from: from) { |val| image(:rm, opts, sync: sync, id: val) }
|
|
577
589
|
else
|
|
578
590
|
op.each { |val| run(cmd.temp(val), sync: sync, from: from) }
|
|
579
591
|
end
|
|
@@ -585,51 +597,48 @@ module Squared
|
|
|
585
597
|
banner = false
|
|
586
598
|
end
|
|
587
599
|
when :tag, :save
|
|
588
|
-
|
|
589
|
-
list_image(flag, filter: filter, from: from) do |val|
|
|
600
|
+
list_image(flag, from: from) do |val|
|
|
590
601
|
op << val
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
end
|
|
602
|
+
next unless flag == :tag
|
|
603
|
+
|
|
604
|
+
op << tagname("#{project}:#{op.first}")
|
|
605
|
+
break
|
|
596
606
|
end
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
if id
|
|
603
|
-
raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag
|
|
604
|
-
else
|
|
605
|
-
raise_error 'no id/tag', hint: flag
|
|
606
|
-
end
|
|
607
|
+
when :pull
|
|
608
|
+
if !id
|
|
609
|
+
id = tagmain
|
|
610
|
+
elsif !op.arg?('a', 'all-tags') && !id.include?(':')
|
|
611
|
+
id = "#{project}:#{id}"
|
|
607
612
|
end
|
|
613
|
+
unless registry
|
|
614
|
+
registry = op.shift
|
|
615
|
+
registry ||= option('registry') || @registry unless id.include?('/')
|
|
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
|
|
608
622
|
raise_error ArgumentError, 'username/registry not specified', hint: flag unless registry
|
|
609
|
-
registry
|
|
610
|
-
uri = shell_quote "#{registry}/#{id}"
|
|
623
|
+
uri = shell_quote tagjoin(registry, id)
|
|
611
624
|
op << uri
|
|
612
625
|
img = docker_output 'image', 'tag', id, uri
|
|
613
626
|
return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
|
|
614
627
|
|
|
615
|
-
|
|
628
|
+
@session = img
|
|
616
629
|
sync = false
|
|
617
|
-
exception
|
|
630
|
+
exception ||= true
|
|
618
631
|
banner = false
|
|
619
632
|
end
|
|
620
|
-
run(
|
|
621
|
-
success?(ret, flag == :tag || flag == :save)
|
|
622
|
-
end
|
|
633
|
+
success?(run(sync: sync, exception: exception, banner: banner, from: from), flag == :tag || flag == :save)
|
|
623
634
|
end
|
|
624
635
|
|
|
625
636
|
def network(flag, opts = [], target: nil)
|
|
626
637
|
cmd, opts = docker_session('network', flag, opts: opts)
|
|
627
638
|
OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
|
|
628
639
|
.clear
|
|
629
|
-
from =
|
|
630
|
-
list_image(flag, docker_output('ps -a'), from: from)
|
|
631
|
-
success?(run(cmd.temp(target, img), from: from))
|
|
632
|
-
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)) }
|
|
633
642
|
end
|
|
634
643
|
|
|
635
644
|
def build?
|
|
@@ -678,7 +687,7 @@ module Squared
|
|
|
678
687
|
elsif (data = doc.dig(*keys))
|
|
679
688
|
yield data
|
|
680
689
|
end
|
|
681
|
-
rescue
|
|
690
|
+
rescue => e
|
|
682
691
|
log.debug e
|
|
683
692
|
end
|
|
684
693
|
end
|
|
@@ -695,11 +704,11 @@ module Squared
|
|
|
695
704
|
end
|
|
696
705
|
|
|
697
706
|
def append_command(flag, val, list, target: @session, prompt: ':')
|
|
698
|
-
if list.delete(prompt)
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
707
|
+
list << if list.delete(prompt)
|
|
708
|
+
readline('Enter command [args]', force: flag == :exec)
|
|
709
|
+
else
|
|
710
|
+
env('DOCKER_ARGS')
|
|
711
|
+
end
|
|
703
712
|
case flag
|
|
704
713
|
when :run
|
|
705
714
|
unless session_arg?('name', target: target)
|
|
@@ -708,28 +717,14 @@ module Squared
|
|
|
708
717
|
when :exec
|
|
709
718
|
raise_error ArgumentError, 'nothing to execute', hint: flag if list.empty?
|
|
710
719
|
end
|
|
711
|
-
target << val
|
|
712
|
-
|
|
713
|
-
when '--'
|
|
714
|
-
target.merge(list.drop(1))
|
|
715
|
-
when /\A(--|&&)(\s|\z)/
|
|
716
|
-
target.merge(list)
|
|
717
|
-
else
|
|
718
|
-
target << list.shift
|
|
719
|
-
unless list.empty?
|
|
720
|
-
if list.first.start_with?('-')
|
|
721
|
-
target.merge(list)
|
|
722
|
-
else
|
|
723
|
-
target << list.join(' && ')
|
|
724
|
-
end
|
|
725
|
-
end
|
|
726
|
-
end
|
|
720
|
+
target << val << list.shift
|
|
721
|
+
target << list.join(' && ') unless list.empty?
|
|
727
722
|
end
|
|
728
723
|
|
|
729
|
-
def append_file(type, target: @session, index: 2)
|
|
730
|
-
return
|
|
724
|
+
def append_file(type, target: @session, index: 2, force: false)
|
|
725
|
+
return unless @file && !(ENV['COMPOSE_FILE'] && compose?(type))
|
|
731
726
|
|
|
732
|
-
unless @file.is_a?(Array)
|
|
727
|
+
unless @file.is_a?(Array) || force
|
|
733
728
|
case type
|
|
734
729
|
when 2, 4
|
|
735
730
|
return
|
|
@@ -737,14 +732,7 @@ module Squared
|
|
|
737
732
|
return unless COMPOSEFILE.select { |val| basepath!(val) }.size > 1
|
|
738
733
|
end
|
|
739
734
|
end
|
|
740
|
-
|
|
741
|
-
if target.is_a?(Set)
|
|
742
|
-
opts = target.to_a.insert(index, *files)
|
|
743
|
-
target.clear
|
|
744
|
-
.merge(opts)
|
|
745
|
-
else
|
|
746
|
-
target.insert(index, *files)
|
|
747
|
-
end
|
|
735
|
+
target.insert(index, *Array(@file).map { |val| quote_option('file', basepath(val)) })
|
|
748
736
|
end
|
|
749
737
|
|
|
750
738
|
def append_context(ctx = nil, target: @session)
|
|
@@ -758,7 +746,7 @@ module Squared
|
|
|
758
746
|
ver = option('version', target: target, ignore: false)
|
|
759
747
|
case val
|
|
760
748
|
when String
|
|
761
|
-
|
|
749
|
+
val.split(',')
|
|
762
750
|
else
|
|
763
751
|
Array(val)
|
|
764
752
|
end.each do |s|
|
|
@@ -797,20 +785,12 @@ module Squared
|
|
|
797
785
|
[cmd, status, no]
|
|
798
786
|
end
|
|
799
787
|
|
|
800
|
-
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)
|
|
801
789
|
pwd_set(from: from) do
|
|
802
790
|
index = 1
|
|
803
791
|
all = option('all', prefix: 'docker')
|
|
804
792
|
y = from == :'image:rm' && option('y', prefix: 'docker')
|
|
805
|
-
|
|
806
|
-
pat = if OptionPartition.pattern?(filter)
|
|
807
|
-
Regexp.new(filter)
|
|
808
|
-
elsif filter.match?(/[:_-]$/)
|
|
809
|
-
/\b#{Regexp.escape(filter)}/
|
|
810
|
-
else
|
|
811
|
-
filter = filter.empty? ? '(?:[:_-]|$)' : "[:_-]#{filter}"
|
|
812
|
-
/\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})#{filter}/
|
|
813
|
-
end
|
|
793
|
+
pat = /\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})\b/
|
|
814
794
|
IO.popen(cmd.temp('--format=json')).each do |line|
|
|
815
795
|
data = JSON.parse(line)
|
|
816
796
|
id = data['ID']
|
|
@@ -859,7 +839,7 @@ module Squared
|
|
|
859
839
|
end
|
|
860
840
|
list_empty(hint: hint || from) if index == 1 && !y
|
|
861
841
|
end
|
|
862
|
-
rescue
|
|
842
|
+
rescue => e
|
|
863
843
|
on_error e, from
|
|
864
844
|
end
|
|
865
845
|
|
|
@@ -893,11 +873,11 @@ module Squared
|
|
|
893
873
|
when :service
|
|
894
874
|
['Choose a service',
|
|
895
875
|
'compose ps -a ' \
|
|
896
|
-
|
|
876
|
+
"--format='table {{.Service}}\t{{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Status}}\t{{.Ports}}'"]
|
|
897
877
|
else
|
|
898
878
|
['Choose an image',
|
|
899
879
|
'images -a ' \
|
|
900
|
-
|
|
880
|
+
"--format='table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}\t{{.Size}}'"]
|
|
901
881
|
end
|
|
902
882
|
lines = `#{docker_output(cmd)}`.lines
|
|
903
883
|
if lines.size <= 1
|
|
@@ -931,18 +911,18 @@ module Squared
|
|
|
931
911
|
when :tag
|
|
932
912
|
args = tagjoin @registry, tag
|
|
933
913
|
when :save
|
|
934
|
-
opts =
|
|
914
|
+
opts = opts.sub_ext('.tar')
|
|
935
915
|
cmd << quote_option('output', File.expand_path(opts))
|
|
936
916
|
if args
|
|
937
|
-
cmd <<
|
|
917
|
+
cmd << quote_option('platform', args)
|
|
938
918
|
args = nil
|
|
939
919
|
end
|
|
940
920
|
else
|
|
941
921
|
cmd << opts << '--'
|
|
942
922
|
end
|
|
943
|
-
cmd.merge(Array(out).map
|
|
923
|
+
cmd.merge(Array(out).map { |val| val.split(/\s+/, 2).first })
|
|
944
924
|
cmd << args
|
|
945
|
-
success?(run(cmd
|
|
925
|
+
success?(run(cmd), ctx.start_with?('network', 'tag', 'save'))
|
|
946
926
|
end
|
|
947
927
|
|
|
948
928
|
def filetype(val = dockerfile)
|
|
@@ -965,11 +945,12 @@ module Squared
|
|
|
965
945
|
end
|
|
966
946
|
|
|
967
947
|
def tagjoin(*args, char: '/')
|
|
968
|
-
args.
|
|
948
|
+
args.compact!
|
|
949
|
+
args.map { |val| val.chomp(char) }.join(char) unless args.empty?
|
|
969
950
|
end
|
|
970
951
|
|
|
971
952
|
def tagname(val)
|
|
972
|
-
val = val.split(':').map
|
|
953
|
+
val = val.split(':').map { |s| charname(s.sub(/^\W+/, '')) }
|
|
973
954
|
ret = val.join(':')
|
|
974
955
|
ret = val.first if val.size > 1 && ret.size > 128
|
|
975
956
|
ret[0..127]
|
|
@@ -986,6 +967,14 @@ module Squared
|
|
|
986
967
|
def tagmain
|
|
987
968
|
tag.is_a?(Array) ? tag.first : tag
|
|
988
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
|
|
989
978
|
end
|
|
990
979
|
|
|
991
980
|
Application.implement Docker
|