squared 0.6.15 → 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 -131
- data/README.md +242 -205
- data/lib/squared/common/format.rb +7 -10
- data/lib/squared/common/prompt.rb +23 -24
- data/lib/squared/common/shell.rb +27 -35
- data/lib/squared/common/system.rb +29 -21
- data/lib/squared/common/utils.rb +43 -54
- data/lib/squared/config.rb +17 -16
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +292 -180
- data/lib/squared/workspace/project/base.rb +564 -472
- data/lib/squared/workspace/project/docker.rb +175 -167
- data/lib/squared/workspace/project/git.rb +213 -184
- data/lib/squared/workspace/project/node.rb +114 -143
- data/lib/squared/workspace/project/python.rb +248 -161
- data/lib/squared/workspace/project/ruby.rb +405 -294
- data/lib/squared/workspace/project/support/class.rb +13 -7
- data/lib/squared/workspace/project/support/optionpartition.rb +72 -57
- 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 +91 -86
- data/lib/squared/workspace/support/base.rb +15 -1
- metadata +3 -2
|
@@ -20,7 +20,7 @@ module Squared
|
|
|
20
20
|
sbom=q].freeze
|
|
21
21
|
}.freeze,
|
|
22
22
|
compose: {
|
|
23
|
-
common: %w[all-resources ansi
|
|
23
|
+
common: %w[all-resources ansi|b compatibility dry-run env-file=p f|file=p parallel=n profile=b progress=b
|
|
24
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,
|
|
@@ -33,7 +33,8 @@ module Squared
|
|
|
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
|
|
@@ -64,6 +65,7 @@ module Squared
|
|
|
64
65
|
}.freeze,
|
|
65
66
|
image: {
|
|
66
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,
|
|
67
69
|
push: %w[a|all-tags platform=q q|quiet].freeze,
|
|
68
70
|
rm: %w[f|force no-prune platform=q].freeze,
|
|
69
71
|
save: %w[o|output=p platform=q].freeze
|
|
@@ -107,9 +109,9 @@ module Squared
|
|
|
107
109
|
|
|
108
110
|
subtasks({
|
|
109
111
|
'build' => %i[tag context].freeze,
|
|
110
|
-
'compose' => %i[build create run exec up down service].freeze,
|
|
111
|
-
'bake' => %i[build check].freeze,
|
|
112
|
-
'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,
|
|
113
115
|
'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
|
|
114
116
|
rm].freeze,
|
|
115
117
|
'network' => %i[connect disconnect].freeze,
|
|
@@ -119,17 +121,19 @@ module Squared
|
|
|
119
121
|
attr_reader :context
|
|
120
122
|
attr_accessor :tag
|
|
121
123
|
|
|
122
|
-
def initialize(*,
|
|
124
|
+
def initialize(*, mounts: [], **kwargs)
|
|
123
125
|
super
|
|
124
|
-
|
|
126
|
+
self.global = nil
|
|
127
|
+
return unless dockerfile(kwargs[:file]).exist?
|
|
125
128
|
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
self.tag = kwargs[:tag] || tagname("#{@project}:#{@version || 'latest'}")
|
|
130
|
+
@context = kwargs[:context]
|
|
128
131
|
@mounts = mounts
|
|
129
|
-
@secrets = secrets
|
|
130
|
-
@registry = tagjoin registry, kwargs[:username]
|
|
132
|
+
@secrets = kwargs[:secrets]
|
|
133
|
+
@registry = tagjoin kwargs[:registry], kwargs[:username]
|
|
134
|
+
@oci = tagjoin kwargs[:oci], kwargs[:username]
|
|
131
135
|
initialize_ref Docker.ref
|
|
132
|
-
initialize_logger
|
|
136
|
+
initialize_logger kwargs[:log]
|
|
133
137
|
initialize_env(**kwargs)
|
|
134
138
|
@output[4] = merge_opts(kwargs[:args], @output[4]) if kwargs[:args]
|
|
135
139
|
end
|
|
@@ -163,25 +167,21 @@ module Squared
|
|
|
163
167
|
end
|
|
164
168
|
cmd << '-a' if has_value!(args, 'a', 'all') && command != 'network'
|
|
165
169
|
data = VAL_DOCKER[:ls][command.to_sym]
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
out.replace(choice_index('Select a column', data, multiple: true, attempts: 1))
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
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")}")
|
|
185
185
|
run(cmd, banner: false, from: :ls)
|
|
186
186
|
end
|
|
187
187
|
end
|
|
@@ -199,14 +199,14 @@ module Squared
|
|
|
199
199
|
break unless bake?
|
|
200
200
|
|
|
201
201
|
case flag
|
|
202
|
-
when :build
|
|
203
|
-
format_desc action, flag, 'opts*,target*,context
|
|
202
|
+
when :build, :compose
|
|
203
|
+
format_desc action, flag, 'opts*,target*,context?|:'
|
|
204
204
|
task flag do |_, args|
|
|
205
205
|
args = args.to_a
|
|
206
206
|
if args.first == ':'
|
|
207
207
|
choice_command :bake
|
|
208
208
|
else
|
|
209
|
-
buildx
|
|
209
|
+
buildx(:bake, args, from: (:'buildx:bake' if flag == :compose))
|
|
210
210
|
end
|
|
211
211
|
end
|
|
212
212
|
when :check
|
|
@@ -239,6 +239,13 @@ module Squared
|
|
|
239
239
|
compose!(flag, [command], service: service.empty? || service)
|
|
240
240
|
end
|
|
241
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
|
|
242
249
|
else
|
|
243
250
|
format_desc action, flag, 'opts*,service*|:'
|
|
244
251
|
task flag do |_, args|
|
|
@@ -275,6 +282,13 @@ module Squared
|
|
|
275
282
|
when 'image'
|
|
276
283
|
case flag
|
|
277
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
|
|
278
292
|
format_desc action, flag, 'tag,registry/username?,opts*'
|
|
279
293
|
task flag, [:tag] do |_, args|
|
|
280
294
|
id = param_guard(action, flag, args: args, key: :tag)
|
|
@@ -282,18 +296,14 @@ module Squared
|
|
|
282
296
|
end
|
|
283
297
|
else
|
|
284
298
|
format_desc(action, flag, case flag
|
|
285
|
-
when :rm, :save then 'id
|
|
286
|
-
when :tag then 'version'
|
|
299
|
+
when :rm, :save then 'id*,opts*'
|
|
300
|
+
when :tag then 'version?'
|
|
287
301
|
else 'opts*,args*'
|
|
288
|
-
end
|
|
302
|
+
end)
|
|
289
303
|
task flag do |_, args|
|
|
290
304
|
args = args.to_a
|
|
291
|
-
n = args.size
|
|
292
|
-
if (n > 1 || (flag == :ls && n > 0)) && OptionPartition.pattern?(args.first)
|
|
293
|
-
filter = args.shift
|
|
294
|
-
end
|
|
295
305
|
if !args.empty? || flag == :ls
|
|
296
|
-
image
|
|
306
|
+
image flag, args
|
|
297
307
|
else
|
|
298
308
|
choice_command flag
|
|
299
309
|
end
|
|
@@ -319,8 +329,8 @@ module Squared
|
|
|
319
329
|
def clean(*, sync: invoked_sync?('clean'), **)
|
|
320
330
|
if runnable?(@clean)
|
|
321
331
|
super
|
|
322
|
-
|
|
323
|
-
image :
|
|
332
|
+
else
|
|
333
|
+
image(:rm, sync: sync)
|
|
324
334
|
end
|
|
325
335
|
end
|
|
326
336
|
|
|
@@ -353,7 +363,7 @@ module Squared
|
|
|
353
363
|
[args, flags].each_with_index do |item, i|
|
|
354
364
|
next unless item && (data = append_any(item, target: []))
|
|
355
365
|
|
|
356
|
-
ret.merge(data.map
|
|
366
|
+
ret.merge(data.map { |arg| i == 0 ? fill_option(arg) : quote_option('build-arg', arg) })
|
|
357
367
|
end
|
|
358
368
|
case from
|
|
359
369
|
when :run
|
|
@@ -361,13 +371,13 @@ module Squared
|
|
|
361
371
|
when String
|
|
362
372
|
ret << quote_option('secret', @secrets, double: true)
|
|
363
373
|
when Hash
|
|
364
|
-
append = lambda do |
|
|
365
|
-
Array(@secrets[
|
|
374
|
+
append = lambda do |key|
|
|
375
|
+
ret.merge(Array(@secrets[key]).map { |arg| quote_option('secret', "type=#{key},#{arg}", double: true) })
|
|
366
376
|
end
|
|
367
377
|
append.call(:file)
|
|
368
378
|
append.call(:env)
|
|
369
379
|
else
|
|
370
|
-
Array(@secrets).
|
|
380
|
+
ret.merge(Array(@secrets).map { |arg| quote_option('secret', arg) })
|
|
371
381
|
end
|
|
372
382
|
if (val = option('tag', ignore: false))
|
|
373
383
|
append_tag val
|
|
@@ -381,36 +391,38 @@ module Squared
|
|
|
381
391
|
ret
|
|
382
392
|
end
|
|
383
393
|
|
|
384
|
-
def buildx(flag, opts = [], tag: nil, context: nil)
|
|
394
|
+
def buildx(flag, opts = [], tag: nil, context: nil, from: nil)
|
|
385
395
|
cmd, opts = docker_session('buildx', opts: opts)
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
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])
|
|
391
400
|
case flag
|
|
392
401
|
when :build, :context
|
|
393
402
|
append_tag(tag || option('tag', ignore: false) || self.tag)
|
|
394
403
|
append_context context
|
|
395
404
|
when :bake
|
|
405
|
+
append_file(0, index: 3) unless from || op.arg?('f', 'file') || !anypath?(*COMPOSEFILE)
|
|
396
406
|
unless op.empty?
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
|
404
415
|
end
|
|
405
|
-
op.append(escape: true, strip:
|
|
416
|
+
op.append(args, escape: true, strip: /^:/)
|
|
417
|
+
contextdir context if context
|
|
406
418
|
end
|
|
407
419
|
end
|
|
408
420
|
op.clear(pass: false)
|
|
409
|
-
run(from:
|
|
421
|
+
run(from: from || symjoin('buildx', flag))
|
|
410
422
|
end
|
|
411
423
|
|
|
412
|
-
def compose!(flag, opts = [], service: nil, multiple: false)
|
|
413
|
-
from =
|
|
424
|
+
def compose!(flag, opts = [], id: nil, service: nil, multiple: false)
|
|
425
|
+
from = symjoin 'compose', flag
|
|
414
426
|
if flag == :service
|
|
415
427
|
command = opts.first
|
|
416
428
|
if service == true
|
|
@@ -424,60 +436,64 @@ module Squared
|
|
|
424
436
|
else
|
|
425
437
|
cmd, opts = docker_session('compose', opts: opts)
|
|
426
438
|
op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self)
|
|
427
|
-
append_file
|
|
439
|
+
append_file(filetype, force: flag == :publish) unless op.arg?('f', 'file')
|
|
428
440
|
op << flag
|
|
429
441
|
op.parse(OPT_DOCKER[:compose].fetch(flag, []))
|
|
430
|
-
if
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
end
|
|
437
|
-
end
|
|
438
|
-
if multiple
|
|
439
|
-
op.concat(service) if service
|
|
440
|
-
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)
|
|
441
448
|
else
|
|
442
|
-
|
|
443
|
-
|
|
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
|
|
444
464
|
end
|
|
445
465
|
end
|
|
446
466
|
run(from: from)
|
|
447
467
|
end
|
|
448
|
-
alias compose_ compose!
|
|
449
468
|
|
|
450
469
|
def container(flag, opts = [], id: nil)
|
|
451
470
|
cmd, opts = docker_session('container', flag, opts: opts)
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
end
|
|
459
|
-
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
|
|
460
477
|
case flag
|
|
461
478
|
when :run, :create, :exec
|
|
462
479
|
if rc && !op.arg?('mount')
|
|
463
480
|
all = collect_hash VAL_DOCKER[:run]
|
|
464
481
|
delim = Regexp.new(",\\s*(?=#{all.join('|')})")
|
|
465
482
|
Array(@mounts).each do |val|
|
|
466
|
-
args = []
|
|
467
483
|
type = nil
|
|
468
|
-
val.split(delim).
|
|
469
|
-
k, v, q =
|
|
484
|
+
args = val.split(delim).each_with_object([]) do |opt, out|
|
|
485
|
+
k, v, q = OptionPartition.parse_option(opt)
|
|
470
486
|
if k == 'type'
|
|
471
487
|
case v
|
|
472
488
|
when 'bind', 'volume', 'image', 'tmpfs'
|
|
473
489
|
type = v
|
|
474
490
|
else
|
|
475
|
-
raise_error TypeError, "unknown: #{v}", hint: flag
|
|
491
|
+
raise_error TypeError, "unknown: #{v || "''"}", hint: flag
|
|
476
492
|
end
|
|
477
493
|
elsif all.include?(k)
|
|
478
494
|
unless type
|
|
479
|
-
VAL_DOCKER[:run].each_pair do |key,
|
|
480
|
-
next unless
|
|
495
|
+
VAL_DOCKER[:run].each_pair do |key, items|
|
|
496
|
+
next unless items.include?(k)
|
|
481
497
|
|
|
482
498
|
type = key.to_s unless key == :common
|
|
483
499
|
break
|
|
@@ -485,13 +501,14 @@ module Squared
|
|
|
485
501
|
end
|
|
486
502
|
case k
|
|
487
503
|
when 'readonly', 'ro'
|
|
488
|
-
|
|
504
|
+
out << k
|
|
489
505
|
next
|
|
490
506
|
when 'source', 'src', 'destination', 'dst', 'target', 'volume-subpath', 'image-path'
|
|
507
|
+
raise_error ArgumentError, "#{k}: no path value", hint: flag unless v
|
|
491
508
|
v = basepath v
|
|
492
509
|
v = shell_quote(v, option: false, force: false) if q == ''
|
|
493
510
|
end
|
|
494
|
-
|
|
511
|
+
out << "#{k}=#{q}#{v}#{q}"
|
|
495
512
|
elsif !silent?
|
|
496
513
|
log_message('unrecognized option', subject: from, hint: k)
|
|
497
514
|
end
|
|
@@ -517,7 +534,7 @@ module Squared
|
|
|
517
534
|
opts = []
|
|
518
535
|
append_option('platform', target: opts, equals: true)
|
|
519
536
|
opts << case option('disable-content-trust', ignore: false)
|
|
520
|
-
when '
|
|
537
|
+
when '0', 'false'
|
|
521
538
|
'--disable-content-trust=false'
|
|
522
539
|
else
|
|
523
540
|
'--disable-content-trust'
|
|
@@ -536,12 +553,12 @@ module Squared
|
|
|
536
553
|
run(from: from)
|
|
537
554
|
end
|
|
538
555
|
|
|
539
|
-
def image(flag, opts = [], sync: true, id: nil, registry: nil
|
|
556
|
+
def image(flag, opts = [], sync: true, id: nil, registry: nil)
|
|
540
557
|
cmd, opts = docker_session('image', flag, opts: opts)
|
|
541
558
|
op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
|
|
542
|
-
exception =
|
|
559
|
+
exception = exception?
|
|
543
560
|
banner = true
|
|
544
|
-
from =
|
|
561
|
+
from = symjoin 'image', flag
|
|
545
562
|
case flag
|
|
546
563
|
when :ls
|
|
547
564
|
if opts.size == op.size
|
|
@@ -554,7 +571,7 @@ module Squared
|
|
|
554
571
|
opts.delete(val)
|
|
555
572
|
break
|
|
556
573
|
end
|
|
557
|
-
list_image(:run,
|
|
574
|
+
list_image(:run, from: from) do |val|
|
|
558
575
|
container(:run, if name
|
|
559
576
|
opts.dup << "name=#{index == 0 ? name : "#{name}-#{index}"}"
|
|
560
577
|
else
|
|
@@ -568,7 +585,7 @@ module Squared
|
|
|
568
585
|
when :rm
|
|
569
586
|
unless id
|
|
570
587
|
if op.empty?
|
|
571
|
-
list_image(:rm,
|
|
588
|
+
list_image(:rm, from: from) { |val| image(:rm, opts, sync: sync, id: val) }
|
|
572
589
|
else
|
|
573
590
|
op.each { |val| run(cmd.temp(val), sync: sync, from: from) }
|
|
574
591
|
end
|
|
@@ -580,51 +597,48 @@ module Squared
|
|
|
580
597
|
banner = false
|
|
581
598
|
end
|
|
582
599
|
when :tag, :save
|
|
583
|
-
|
|
584
|
-
list_image(flag, filter: filter, from: from) do |val|
|
|
600
|
+
list_image(flag, from: from) do |val|
|
|
585
601
|
op << val
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
end
|
|
602
|
+
next unless flag == :tag
|
|
603
|
+
|
|
604
|
+
op << tagname("#{project}:#{op.first}")
|
|
605
|
+
break
|
|
591
606
|
end
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
if id
|
|
598
|
-
raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag
|
|
599
|
-
else
|
|
600
|
-
raise_error 'no id/tag', hint: flag
|
|
601
|
-
end
|
|
607
|
+
when :pull
|
|
608
|
+
if !id
|
|
609
|
+
id = tagmain
|
|
610
|
+
elsif !op.arg?('a', 'all-tags') && !id.include?(':')
|
|
611
|
+
id = "#{project}:#{id}"
|
|
602
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
|
|
603
622
|
raise_error ArgumentError, 'username/registry not specified', hint: flag unless registry
|
|
604
|
-
registry
|
|
605
|
-
uri = shell_quote "#{registry}/#{id}"
|
|
623
|
+
uri = shell_quote tagjoin(registry, id)
|
|
606
624
|
op << uri
|
|
607
625
|
img = docker_output 'image', 'tag', id, uri
|
|
608
626
|
return unless confirm_command(img.to_s, cmd.to_s, target: id, as: registry, title: from)
|
|
609
627
|
|
|
610
|
-
|
|
628
|
+
@session = img
|
|
611
629
|
sync = false
|
|
612
|
-
exception
|
|
630
|
+
exception ||= true
|
|
613
631
|
banner = false
|
|
614
632
|
end
|
|
615
|
-
run(
|
|
616
|
-
success?(ret, flag == :tag || flag == :save)
|
|
617
|
-
end
|
|
633
|
+
success?(run(sync: sync, exception: exception, banner: banner, from: from), flag == :tag || flag == :save)
|
|
618
634
|
end
|
|
619
635
|
|
|
620
636
|
def network(flag, opts = [], target: nil)
|
|
621
637
|
cmd, opts = docker_session('network', flag, opts: opts)
|
|
622
638
|
OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
|
|
623
639
|
.clear
|
|
624
|
-
from =
|
|
625
|
-
list_image(flag, docker_output('ps -a'), from: from)
|
|
626
|
-
success?(run(cmd.temp(target, img), from: from))
|
|
627
|
-
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)) }
|
|
628
642
|
end
|
|
629
643
|
|
|
630
644
|
def build?
|
|
@@ -673,7 +687,7 @@ module Squared
|
|
|
673
687
|
elsif (data = doc.dig(*keys))
|
|
674
688
|
yield data
|
|
675
689
|
end
|
|
676
|
-
rescue
|
|
690
|
+
rescue => e
|
|
677
691
|
log.debug e
|
|
678
692
|
end
|
|
679
693
|
end
|
|
@@ -690,11 +704,11 @@ module Squared
|
|
|
690
704
|
end
|
|
691
705
|
|
|
692
706
|
def append_command(flag, val, list, target: @session, prompt: ':')
|
|
693
|
-
if list.delete(prompt)
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
707
|
+
list << if list.delete(prompt)
|
|
708
|
+
readline('Enter command [args]', force: flag == :exec)
|
|
709
|
+
else
|
|
710
|
+
env('DOCKER_ARGS')
|
|
711
|
+
end
|
|
698
712
|
case flag
|
|
699
713
|
when :run
|
|
700
714
|
unless session_arg?('name', target: target)
|
|
@@ -707,10 +721,10 @@ module Squared
|
|
|
707
721
|
target << list.join(' && ') unless list.empty?
|
|
708
722
|
end
|
|
709
723
|
|
|
710
|
-
def append_file(type, target: @session, index: 2)
|
|
711
|
-
return
|
|
724
|
+
def append_file(type, target: @session, index: 2, force: false)
|
|
725
|
+
return unless @file && !(ENV['COMPOSE_FILE'] && compose?(type))
|
|
712
726
|
|
|
713
|
-
unless @file.is_a?(Array)
|
|
727
|
+
unless @file.is_a?(Array) || force
|
|
714
728
|
case type
|
|
715
729
|
when 2, 4
|
|
716
730
|
return
|
|
@@ -718,14 +732,7 @@ module Squared
|
|
|
718
732
|
return unless COMPOSEFILE.select { |val| basepath!(val) }.size > 1
|
|
719
733
|
end
|
|
720
734
|
end
|
|
721
|
-
|
|
722
|
-
if target.is_a?(Set)
|
|
723
|
-
opts = target.to_a.insert(index, *files)
|
|
724
|
-
target.clear
|
|
725
|
-
.merge(opts)
|
|
726
|
-
else
|
|
727
|
-
target.insert(index, *files)
|
|
728
|
-
end
|
|
735
|
+
target.insert(index, *Array(@file).map { |val| quote_option('file', basepath(val)) })
|
|
729
736
|
end
|
|
730
737
|
|
|
731
738
|
def append_context(ctx = nil, target: @session)
|
|
@@ -739,7 +746,7 @@ module Squared
|
|
|
739
746
|
ver = option('version', target: target, ignore: false)
|
|
740
747
|
case val
|
|
741
748
|
when String
|
|
742
|
-
|
|
749
|
+
val.split(',')
|
|
743
750
|
else
|
|
744
751
|
Array(val)
|
|
745
752
|
end.each do |s|
|
|
@@ -778,20 +785,12 @@ module Squared
|
|
|
778
785
|
[cmd, status, no]
|
|
779
786
|
end
|
|
780
787
|
|
|
781
|
-
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)
|
|
782
789
|
pwd_set(from: from) do
|
|
783
790
|
index = 1
|
|
784
791
|
all = option('all', prefix: 'docker')
|
|
785
792
|
y = from == :'image:rm' && option('y', prefix: 'docker')
|
|
786
|
-
|
|
787
|
-
pat = if OptionPartition.pattern?(filter)
|
|
788
|
-
Regexp.new(filter)
|
|
789
|
-
elsif filter.match?(/[:_-]$/)
|
|
790
|
-
/\b#{Regexp.escape(filter)}/
|
|
791
|
-
else
|
|
792
|
-
filter = filter.empty? ? '(?:[:_-]|$)' : "[:_-]#{filter}"
|
|
793
|
-
/\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})#{filter}/
|
|
794
|
-
end
|
|
793
|
+
pat = /\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})\b/
|
|
795
794
|
IO.popen(cmd.temp('--format=json')).each do |line|
|
|
796
795
|
data = JSON.parse(line)
|
|
797
796
|
id = data['ID']
|
|
@@ -840,7 +839,7 @@ module Squared
|
|
|
840
839
|
end
|
|
841
840
|
list_empty(hint: hint || from) if index == 1 && !y
|
|
842
841
|
end
|
|
843
|
-
rescue
|
|
842
|
+
rescue => e
|
|
844
843
|
on_error e, from
|
|
845
844
|
end
|
|
846
845
|
|
|
@@ -874,11 +873,11 @@ module Squared
|
|
|
874
873
|
when :service
|
|
875
874
|
['Choose a service',
|
|
876
875
|
'compose ps -a ' \
|
|
877
|
-
|
|
876
|
+
"--format='table {{.Service}}\t{{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Status}}\t{{.Ports}}'"]
|
|
878
877
|
else
|
|
879
878
|
['Choose an image',
|
|
880
879
|
'images -a ' \
|
|
881
|
-
|
|
880
|
+
"--format='table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}\t{{.Size}}'"]
|
|
882
881
|
end
|
|
883
882
|
lines = `#{docker_output(cmd)}`.lines
|
|
884
883
|
if lines.size <= 1
|
|
@@ -912,18 +911,18 @@ module Squared
|
|
|
912
911
|
when :tag
|
|
913
912
|
args = tagjoin @registry, tag
|
|
914
913
|
when :save
|
|
915
|
-
opts =
|
|
914
|
+
opts = opts.sub_ext('.tar')
|
|
916
915
|
cmd << quote_option('output', File.expand_path(opts))
|
|
917
916
|
if args
|
|
918
|
-
cmd <<
|
|
917
|
+
cmd << quote_option('platform', args)
|
|
919
918
|
args = nil
|
|
920
919
|
end
|
|
921
920
|
else
|
|
922
921
|
cmd << opts << '--'
|
|
923
922
|
end
|
|
924
|
-
cmd.merge(Array(out).map
|
|
923
|
+
cmd.merge(Array(out).map { |val| val.split(/\s+/, 2).first })
|
|
925
924
|
cmd << args
|
|
926
|
-
success?(run(cmd), ctx.start_with?(
|
|
925
|
+
success?(run(cmd), ctx.start_with?('network', 'tag', 'save'))
|
|
927
926
|
end
|
|
928
927
|
|
|
929
928
|
def filetype(val = dockerfile)
|
|
@@ -946,11 +945,12 @@ module Squared
|
|
|
946
945
|
end
|
|
947
946
|
|
|
948
947
|
def tagjoin(*args, char: '/')
|
|
949
|
-
args.
|
|
948
|
+
args.compact!
|
|
949
|
+
args.map { |val| val.chomp(char) }.join(char) unless args.empty?
|
|
950
950
|
end
|
|
951
951
|
|
|
952
952
|
def tagname(val)
|
|
953
|
-
val = val.split(':').map
|
|
953
|
+
val = val.split(':').map { |s| charname(s.sub(/^\W+/, '')) }
|
|
954
954
|
ret = val.join(':')
|
|
955
955
|
ret = val.first if val.size > 1 && ret.size > 128
|
|
956
956
|
ret[0..127]
|
|
@@ -967,6 +967,14 @@ module Squared
|
|
|
967
967
|
def tagmain
|
|
968
968
|
tag.is_a?(Array) ? tag.first : tag
|
|
969
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
|
|
970
978
|
end
|
|
971
979
|
|
|
972
980
|
Application.implement Docker
|