squared 0.5.26 → 0.6.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 +89 -300
- data/README.md +104 -72
- data/lib/squared/common/base.rb +1 -30
- data/lib/squared/common/format.rb +40 -38
- data/lib/squared/common/prompt.rb +58 -40
- data/lib/squared/common/shell.rb +71 -63
- data/lib/squared/common/system.rb +70 -38
- data/lib/squared/common/utils.rb +31 -16
- data/lib/squared/config.rb +27 -34
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +87 -113
- data/lib/squared/workspace/project/base.rb +552 -427
- data/lib/squared/workspace/project/docker.rb +419 -320
- data/lib/squared/workspace/project/git.rb +379 -374
- data/lib/squared/workspace/project/node.rb +502 -285
- data/lib/squared/workspace/project/python.rb +375 -255
- data/lib/squared/workspace/project/ruby.rb +697 -397
- data/lib/squared/workspace/project/support/class.rb +213 -238
- data/lib/squared/workspace/repo.rb +47 -44
- data/lib/squared/workspace/series.rb +14 -23
- data/lib/squared/workspace/support/base.rb +3 -24
- data/lib/squared/workspace/support/variables.rb +48 -0
- data/lib/squared/workspace/support.rb +1 -1
- data/lib/squared/workspace.rb +4 -6
- metadata +3 -3
- data/lib/squared/workspace/support/data.rb +0 -11
|
@@ -13,21 +13,22 @@ module Squared
|
|
|
13
13
|
buildx: {
|
|
14
14
|
common: %w[builder=b D|debug],
|
|
15
15
|
build: %w[add-host=q annotation=q attest=q build-arg=qq build-context=qq cache-from=q cache-to=q
|
|
16
|
-
cgroup-parent=b iidfile=p label=q network=b no-cache-filter=b o|output=q platform=
|
|
16
|
+
cgroup-parent=b iidfile=p label=q a-file=p network=b no-cache-filter=b o|output=q platform=b
|
|
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
19
|
shared: %w[check load no-cache pull push allow=q call=b? f|file=p metadata-file=p progress=b provenance=q
|
|
20
20
|
sbom=q].freeze
|
|
21
21
|
}.freeze,
|
|
22
22
|
compose: {
|
|
23
|
-
common: %w[all-resources compatibility dry-run
|
|
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-TTY=b? user=e w|workdir=q].freeze,
|
|
28
29
|
run: %w[build d|detach no-deps q|quiet quiet-build quiet-pull remove-orphans rm P|service-ports use-aliases
|
|
29
30
|
cap-add=b cap-drop=b entrypoint=q e|env=qq env-from-file=p i|interactive=b? l|label=q name=b
|
|
30
|
-
T|no-
|
|
31
|
+
T|no-TTY=b? p|publish=q pull=b u|user=e v|volume=q w|workdir=q].freeze,
|
|
31
32
|
up: %w[abort-on-container-exit abort-on-container-failure always-recreate-deps attach-dependencies build
|
|
32
33
|
d|detach force-recreate menu no-build no-color no-deps no-log-prefix no-recreate no-start quiet-build
|
|
33
34
|
quiet-pull remove-orphans V|renew-anon-volumes timestamps wait w|watch y|yes attach=b
|
|
@@ -36,38 +37,37 @@ module Squared
|
|
|
36
37
|
}.freeze,
|
|
37
38
|
container: {
|
|
38
39
|
create: %w[init i|interactive no-healthcheck oom-kill-disable privileged P|publish-all q|quiet read-only
|
|
39
|
-
rm t|tty
|
|
40
|
-
|
|
41
|
-
device-
|
|
40
|
+
rm t|tty use-api-socket add-host=q annotation=q a|attach=b blkio-weight=i blkio-weight-device=i
|
|
41
|
+
cap-add=b cap-drop=b cgroup-parent=b cgroupns=b cidfile=p device=q device-cgroup-rule=q
|
|
42
|
+
device-read-bps=q device-read-iops=q device-write-bps=q device-write-iops=q
|
|
42
43
|
disable-content-trust=b? dns=q dns-option=q dns-search=q domainname=b entrypoint=q e|env=qq
|
|
43
44
|
env-file=p expose=q gpus=q group-add=b health-cmd=q health-interval=b health-retries=i
|
|
44
|
-
health-start-interval=q health-start-period=q health-timeout=q
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
memory-
|
|
48
|
-
|
|
49
|
-
stop-
|
|
50
|
-
|
|
51
|
-
run: %w[d|detach detach-keys=q sig-proxy=b?].freeze,
|
|
45
|
+
health-start-interval=q health-start-period=q health-timeout=q io-maxbandwidth=b io-maxiops=b
|
|
46
|
+
ip=b ip6=q ipc=b isolation=b kernel-memory=b l|label=q label-file=q link=b link-local-ip=q
|
|
47
|
+
log-driver=b log-opt=q mac-address=q m|memory=b memory-reservation=b memory-swap=n
|
|
48
|
+
memory-swappiness=n mount=qq name=b network=b network-alias=b oom-score-adj=b pid=b pids-limit=n
|
|
49
|
+
platform=b p|publish=q pull=b restart=b runtime=b security-opt=q shm-size=b stop-signal=b
|
|
50
|
+
stop-timeout=i storage-opt=q sysctl=q tmpfs=q ulimit=q u|user=b userns=b uts=b v|volume=q
|
|
51
|
+
volume-driver=b volumes-from=b w|workdir=q].freeze,
|
|
52
|
+
run: %w[d|detach detach-keys=q hostname=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=b 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 user=e
|
|
56
57
|
w|workdir=q].freeze,
|
|
57
|
-
rm: %w[f|force l|link v|volumes].freeze,
|
|
58
58
|
commit: %w[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,
|
|
61
|
-
stop: %w[s|signal=b t|
|
|
62
|
-
restart: %w[s|signal=b t|
|
|
61
|
+
stop: %w[s|signal=b t|timeout=i].freeze,
|
|
62
|
+
restart: %w[s|signal=b t|timeout=i].freeze,
|
|
63
63
|
kill: %w[s|signal=b].freeze,
|
|
64
64
|
stats: %w[a|all no-stream no-trunc format|q].freeze
|
|
65
65
|
}.freeze,
|
|
66
66
|
image: {
|
|
67
|
-
|
|
68
|
-
push: %w[a|all-tags disable-content-trust=b? platform=
|
|
69
|
-
rm: %w[f|force no-prune platform=
|
|
70
|
-
save: %w[o|output=p platform=
|
|
67
|
+
ls: %w[a|all digests no-trunc q|quiet tree f|filter=q format=q].freeze,
|
|
68
|
+
push: %w[a|all-tags disable-content-trust=b? platform=b q|quiet].freeze,
|
|
69
|
+
rm: %w[f|force no-prune platform=b].freeze,
|
|
70
|
+
save: %w[o|output=p platform=b].freeze
|
|
71
71
|
}.freeze,
|
|
72
72
|
network: {
|
|
73
73
|
connect: %w[alias=b driver-opt=q gw-priority=n ip=b ip6=q link=b link-local-ip=q].freeze,
|
|
@@ -81,6 +81,15 @@ module Squared
|
|
|
81
81
|
volume: %w[volume-subpath volume-nocopy volume-opt].freeze,
|
|
82
82
|
tmpfs: %w[tmpfs-size tmpfs-mode].freeze,
|
|
83
83
|
image: %w[image-path].freeze
|
|
84
|
+
}.freeze,
|
|
85
|
+
ls: {
|
|
86
|
+
compose: %w[Name Image Command Service RunningFor Status Ports CreatedAt ExitCode Health ID Labels
|
|
87
|
+
LocalVolumes Mounts Names Networks Project Publishers Size State].freeze,
|
|
88
|
+
container: %w[ID Image Command RunningFor Status Ports Names CreatedAt Labels LocalVolumes Mounts Networks
|
|
89
|
+
Platform Size State].freeze,
|
|
90
|
+
image: %w[Repository Tag ID Containers CreatedSince Size CreatedAt Digest SharedSize UniqueSize
|
|
91
|
+
VirtualSize].freeze,
|
|
92
|
+
network: %w[ID Name Driver Scope CreatedAt IPv4 IPv6 Internal Labels].freeze
|
|
84
93
|
}.freeze
|
|
85
94
|
}.freeze
|
|
86
95
|
private_constant :COMPOSEFILE, :BAKEFILE, :OPT_DOCKER, :VAL_DOCKER
|
|
@@ -99,12 +108,13 @@ module Squared
|
|
|
99
108
|
|
|
100
109
|
subtasks({
|
|
101
110
|
'build' => %i[tag context].freeze,
|
|
102
|
-
'compose' => %i[build run exec up down].freeze,
|
|
111
|
+
'compose' => %i[build create run exec up down service].freeze,
|
|
103
112
|
'bake' => %i[build check].freeze,
|
|
104
|
-
'image' => %i[
|
|
113
|
+
'image' => %i[ls rm push tag save].freeze,
|
|
105
114
|
'container' => %i[run create exec update commit inspect diff start stop restart pause unpause top stats kill
|
|
106
115
|
rm].freeze,
|
|
107
|
-
'network' => %i[connect disconnect].freeze
|
|
116
|
+
'network' => %i[connect disconnect].freeze,
|
|
117
|
+
'ls' => nil
|
|
108
118
|
})
|
|
109
119
|
|
|
110
120
|
attr_reader :context
|
|
@@ -137,114 +147,168 @@ module Squared
|
|
|
137
147
|
Docker.subtasks do |action, flags|
|
|
138
148
|
next if task_pass?(action)
|
|
139
149
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
if flags.nil?
|
|
151
|
+
case action
|
|
152
|
+
when 'ls'
|
|
153
|
+
format_desc(action, nil, VAL_DOCKER[:ls].keys, after: 'a/ll?,s/tandard?,range*', arg: nil)
|
|
154
|
+
task action, [:command] do |_, args|
|
|
155
|
+
command = param_guard(action, 'command', args: args, key: :command)
|
|
156
|
+
args = args.extras
|
|
157
|
+
a = args.delete('a') || args.delete('all')
|
|
158
|
+
ls = case command
|
|
159
|
+
when 'network'
|
|
160
|
+
a = nil
|
|
161
|
+
'ls'
|
|
162
|
+
when 'image', 'container'
|
|
163
|
+
'ls'
|
|
164
|
+
when 'compose'
|
|
165
|
+
'ps'
|
|
166
|
+
else
|
|
167
|
+
raise_error ArgumentError, 'unrecognized command', hint: command
|
|
168
|
+
end
|
|
169
|
+
data = VAL_DOCKER[:ls][command.to_sym]
|
|
170
|
+
if args.delete('s') || args.delete('standard')
|
|
171
|
+
cols = data.first(data.index('CreatedAt'))
|
|
172
|
+
else
|
|
173
|
+
cols = []
|
|
174
|
+
args.each do |val|
|
|
175
|
+
if val =~ /^(\d+)$/
|
|
176
|
+
cols << data[$1.to_i.pred]
|
|
177
|
+
elsif val =~ /^(\d+)(-|\.{2,3})(\d+)$/
|
|
178
|
+
j = $1.to_i.pred
|
|
179
|
+
k = $3.to_i - ($2 == '..' ? 2 : 1)
|
|
180
|
+
cols.concat(data[j..k]) if k > j
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
if cols.empty?
|
|
184
|
+
cols = choice_index('Select a column', data, multiple: true, force: true, attempts: 1)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
cmd = docker_output(command, ls, a && '-a')
|
|
188
|
+
cmd << quote_option('format', "table #{cols.map! { |val| "{{.#{val}}}" }.join("\t")}")
|
|
189
|
+
run(cmd, banner: false, from: :ls)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
else
|
|
193
|
+
namespace action do
|
|
194
|
+
flags.each do |flag|
|
|
195
|
+
case action
|
|
196
|
+
when 'build'
|
|
146
197
|
format_desc(action, flag, 'opts*', before: flag == :tag ? 'name' : 'dir')
|
|
147
198
|
task flag, [flag] do |_, args|
|
|
148
199
|
param = param_guard(action, flag, args: args, key: flag)
|
|
149
200
|
buildx(:build, args.extras, "#{flag}": param)
|
|
150
201
|
end
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
202
|
+
when 'bake'
|
|
203
|
+
break unless bake?
|
|
204
|
+
|
|
205
|
+
case flag
|
|
206
|
+
when :build
|
|
207
|
+
format_desc action, flag, 'opts*,target*,context?|:'
|
|
208
|
+
task flag do |_, args|
|
|
209
|
+
args = args.to_a
|
|
210
|
+
if args.first == ':'
|
|
211
|
+
choice_command :bake
|
|
212
|
+
else
|
|
213
|
+
buildx :bake, args
|
|
214
|
+
end
|
|
164
215
|
end
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
buildx :bake, ['allow=fs.read=*', 'call=check', target]
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
when 'compose'
|
|
174
|
-
break unless compose?
|
|
175
|
-
|
|
176
|
-
case flag
|
|
177
|
-
when :build, :up, :down
|
|
178
|
-
format_desc action, flag, 'opts*,service*|:'
|
|
179
|
-
task flag do |_, args|
|
|
180
|
-
compose! flag, args.to_a
|
|
181
|
-
end
|
|
182
|
-
when :exec, :run
|
|
183
|
-
format_desc action, flag, "service|:,command#{flag == :exec ? '' : '?'}|::,args*,opts*"
|
|
184
|
-
task flag, [:service] do |_, args|
|
|
185
|
-
service = param_guard(action, flag, args: args, key: :service)
|
|
186
|
-
compose!(flag, args.extras, service: service)
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
when 'container'
|
|
190
|
-
case flag
|
|
191
|
-
when :exec, :commit
|
|
192
|
-
format_desc(action, flag, flag == :exec ? 'id/name,opts*,args+|:' : 'id/name,tag?,opts*')
|
|
193
|
-
task flag, [:id] do |_, args|
|
|
194
|
-
if flag == :exec && !args.id
|
|
195
|
-
choice_command flag
|
|
196
|
-
else
|
|
197
|
-
id = param_guard(action, flag, args: args, key: :id)
|
|
198
|
-
container(flag, args.extras, id: id)
|
|
216
|
+
when :check
|
|
217
|
+
format_desc action, flag, 'target'
|
|
218
|
+
task flag, [:target] do |_, args|
|
|
219
|
+
target = param_guard(action, flag, args: args, key: :target)
|
|
220
|
+
buildx :bake, ['allow=fs.read=*', 'call=check', target]
|
|
199
221
|
end
|
|
200
222
|
end
|
|
201
|
-
when
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
223
|
+
when 'compose'
|
|
224
|
+
break unless compose?
|
|
225
|
+
|
|
226
|
+
case flag
|
|
227
|
+
when :exec, :run
|
|
228
|
+
format_desc action, flag, "service|:,command#{flag == :exec ? '' : '?'}|::,args*,opts*"
|
|
229
|
+
task flag, [:service] do |_, args|
|
|
230
|
+
service = param_guard(action, flag, args: args, key: :service)
|
|
231
|
+
compose!(flag, args.extras, service: service)
|
|
232
|
+
end
|
|
233
|
+
when :service
|
|
234
|
+
cmds = %w[down kill pause restart rm start stop top unpause watch].freeze
|
|
235
|
+
format_desc(action, flag, cmds, arg: nil, after: 'name+|:')
|
|
236
|
+
task flag, [:command] do |_, args|
|
|
237
|
+
command = param_guard(action, flag, args: args, key: :command)
|
|
238
|
+
raise_error ArgumentError, 'unrecognized command', hint: command unless cmds.include?(command)
|
|
239
|
+
service = args.extras
|
|
240
|
+
if service.first == ':'
|
|
241
|
+
choice_command flag, command
|
|
242
|
+
else
|
|
243
|
+
compose!(flag, [command], service: service.empty? || service)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
else
|
|
247
|
+
format_desc action, flag, 'opts*,service*|:'
|
|
248
|
+
task flag do |_, args|
|
|
249
|
+
compose!(flag, args.to_a, multiple: true)
|
|
208
250
|
end
|
|
209
251
|
end
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
252
|
+
when 'container'
|
|
253
|
+
case flag
|
|
254
|
+
when :exec, :commit
|
|
255
|
+
format_desc(action, flag, flag == :exec ? 'id/name,opts*,args+|:' : 'id/name,tag?,opts*')
|
|
256
|
+
task flag, [:id] do |_, args|
|
|
257
|
+
if flag == :exec && !args.id
|
|
258
|
+
choice_command flag
|
|
259
|
+
else
|
|
260
|
+
id = param_guard(action, flag, args: args, key: :id)
|
|
261
|
+
container(flag, args.extras, id: id)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
when :run, :create
|
|
265
|
+
format_desc action, flag, 'image,opts*,args*|:'
|
|
266
|
+
task flag, [:image] do |_, args|
|
|
267
|
+
if args.image
|
|
268
|
+
container(flag, args.extras, id: args.image)
|
|
269
|
+
else
|
|
270
|
+
choice_command flag
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
else
|
|
274
|
+
format_desc action, flag, "opts*,id/name#{flag == :update ? '+' : '*'}"
|
|
275
|
+
task flag do |_, args|
|
|
276
|
+
container flag, args.to_a
|
|
277
|
+
end
|
|
214
278
|
end
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
279
|
+
when 'image'
|
|
280
|
+
case flag
|
|
281
|
+
when :push
|
|
282
|
+
format_desc action, flag, 'tag,registry/username?,opts*'
|
|
283
|
+
task flag, [:tag] do |_, args|
|
|
284
|
+
id = param_guard(action, flag, args: args, key: :tag)
|
|
285
|
+
image(flag, args.extras, id: id)
|
|
286
|
+
end
|
|
287
|
+
else
|
|
288
|
+
format_desc(action, flag, case flag
|
|
289
|
+
when :rm, :save then 'id*,opts*'
|
|
290
|
+
when :tag then 'version?'
|
|
291
|
+
else 'opts*,args*'
|
|
292
|
+
end)
|
|
293
|
+
task flag do |_, args|
|
|
294
|
+
args = args.to_a
|
|
295
|
+
if !args.empty? || flag == :ls
|
|
296
|
+
image flag, args
|
|
297
|
+
else
|
|
298
|
+
choice_command flag
|
|
299
|
+
end
|
|
300
|
+
end
|
|
223
301
|
end
|
|
224
|
-
|
|
225
|
-
format_desc
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
end)
|
|
230
|
-
task flag do |_, args|
|
|
231
|
-
args = args.to_a
|
|
232
|
-
if args.empty? && flag != :list
|
|
233
|
-
choice_command flag
|
|
302
|
+
when 'network'
|
|
303
|
+
format_desc action, flag, 'target,opts*'
|
|
304
|
+
task flag, [:target] do |_, args|
|
|
305
|
+
if args.target
|
|
306
|
+
network(flag, args.extras, target: args.target)
|
|
234
307
|
else
|
|
235
|
-
|
|
308
|
+
choice_command flag
|
|
236
309
|
end
|
|
237
310
|
end
|
|
238
311
|
end
|
|
239
|
-
when 'network'
|
|
240
|
-
format_desc action, flag, 'target,opts*'
|
|
241
|
-
task flag, [:target] do |_, args|
|
|
242
|
-
if args.target
|
|
243
|
-
network(flag, args.extras, target: args.target)
|
|
244
|
-
else
|
|
245
|
-
choice_command flag
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
312
|
end
|
|
249
313
|
end
|
|
250
314
|
end
|
|
@@ -255,28 +319,29 @@ module Squared
|
|
|
255
319
|
def clean(*, sync: invoked_sync?('clean'), **)
|
|
256
320
|
if runnable?(@clean)
|
|
257
321
|
super
|
|
258
|
-
|
|
259
|
-
image :
|
|
322
|
+
else
|
|
323
|
+
image(:rm, sync: sync)
|
|
260
324
|
end
|
|
261
325
|
end
|
|
262
326
|
|
|
263
327
|
def compose(opts, flags = nil, script: false, args: nil, from: :run, **)
|
|
264
|
-
return opts
|
|
328
|
+
return opts unless script
|
|
265
329
|
|
|
330
|
+
ret = docker_session
|
|
266
331
|
if from == :run
|
|
267
332
|
if bake?(n = filetype)
|
|
268
|
-
ret
|
|
333
|
+
ret << 'buildx bake'
|
|
269
334
|
append_file n
|
|
270
335
|
from = :bake
|
|
271
336
|
elsif compose?(n)
|
|
272
|
-
ret
|
|
337
|
+
ret << 'compose build'
|
|
273
338
|
append_file n
|
|
274
339
|
from = :compose
|
|
275
340
|
else
|
|
276
|
-
ret
|
|
341
|
+
ret << 'build'
|
|
277
342
|
end
|
|
278
343
|
else
|
|
279
|
-
ret
|
|
344
|
+
ret << from
|
|
280
345
|
end
|
|
281
346
|
case opts
|
|
282
347
|
when String
|
|
@@ -286,10 +351,10 @@ module Squared
|
|
|
286
351
|
when Enumerable
|
|
287
352
|
ret.merge(opts.to_a)
|
|
288
353
|
end
|
|
289
|
-
[args, flags].each_with_index do |
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
354
|
+
[args, flags].each_with_index do |item, i|
|
|
355
|
+
next unless item && (data = append_any(item, target: []))
|
|
356
|
+
|
|
357
|
+
ret.merge(data.map! { |arg| i == 0 ? fill_option(arg) : quote_option('build-arg', arg) })
|
|
293
358
|
end
|
|
294
359
|
case from
|
|
295
360
|
when :run
|
|
@@ -312,8 +377,8 @@ module Squared
|
|
|
312
377
|
end
|
|
313
378
|
append_context
|
|
314
379
|
when :bake, :compose
|
|
315
|
-
option(from == :bake ? 'target' : 'service', ignore: false) do |
|
|
316
|
-
ret.merge(split_escape(
|
|
380
|
+
option(from == :bake ? 'target' : 'service', ignore: false) do |val|
|
|
381
|
+
ret.merge(split_escape(val).map! { |s| shell_quote(s) })
|
|
317
382
|
end
|
|
318
383
|
end
|
|
319
384
|
ret
|
|
@@ -321,57 +386,80 @@ module Squared
|
|
|
321
386
|
|
|
322
387
|
def buildx(flag, opts = [], tag: nil, context: nil)
|
|
323
388
|
cmd, opts = docker_session('buildx', opts: opts)
|
|
324
|
-
op =
|
|
325
|
-
|
|
326
|
-
|
|
389
|
+
op = OPT_DOCKER[:buildx].yield_self do |data|
|
|
390
|
+
OptionPartition.new(opts, data[:common], cmd, project: self)
|
|
391
|
+
.append(flag, quote: false)
|
|
392
|
+
.parse(data[flag == :bake ? :bake : :build] + data[:shared])
|
|
393
|
+
end
|
|
327
394
|
case flag
|
|
328
395
|
when :build, :context
|
|
329
396
|
append_tag(tag || option('tag', ignore: false) || self.tag)
|
|
330
397
|
append_context context
|
|
331
398
|
when :bake
|
|
332
|
-
op.
|
|
333
|
-
|
|
399
|
+
unless op.empty?
|
|
400
|
+
args = op.dup
|
|
401
|
+
op.reset
|
|
402
|
+
if Dir.exist?(args.last)
|
|
403
|
+
if projectpath?(val = args.pop)
|
|
404
|
+
context = val
|
|
405
|
+
else
|
|
406
|
+
op.push(val)
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
op.append(args, escape: true, strip: /^:/)
|
|
410
|
+
contextdir context if context
|
|
411
|
+
end
|
|
334
412
|
end
|
|
335
413
|
op.clear(pass: false)
|
|
336
414
|
run(from: :"buildx:#{flag}")
|
|
337
415
|
end
|
|
338
416
|
|
|
339
|
-
def compose!(flag, opts = [], service: nil)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
read_composefile('services', target: op.values_of('f', 'file')) { |data| keys.merge(data.keys) }
|
|
352
|
-
service = unless keys.empty?
|
|
353
|
-
choice_index('Add services', keys, multiple: multiple, force: !multiple,
|
|
354
|
-
attempts: multiple ? 1 : 5)
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
if multiple
|
|
358
|
-
op.concat(service) if service
|
|
359
|
-
op.append(delim: true, escape: true, strip: /^:/)
|
|
417
|
+
def compose!(flag, opts = [], service: nil, multiple: false)
|
|
418
|
+
from = :"compose:#{flag}"
|
|
419
|
+
if flag == :service
|
|
420
|
+
command = opts.first
|
|
421
|
+
if service == true
|
|
422
|
+
cmd, status = filter_ps command, from
|
|
423
|
+
lines = IO.popen(cmd.temp('--services')).map(&:strip).reject(&:empty?)
|
|
424
|
+
return list_empty(hint: status) if lines.empty?
|
|
425
|
+
|
|
426
|
+
service = choice_index('Choose a service', lines, multiple: true, force: true, attempts: 1)
|
|
427
|
+
end
|
|
428
|
+
docker_session('compose', command, '--', *service)
|
|
360
429
|
else
|
|
361
|
-
|
|
362
|
-
|
|
430
|
+
cmd, opts = docker_session('compose', opts: opts)
|
|
431
|
+
op = OptionPartition.new(opts, OPT_DOCKER[:compose][:common], cmd, project: self)
|
|
432
|
+
append_file filetype unless op.arg?('f', 'file')
|
|
433
|
+
op << flag
|
|
434
|
+
op.parse(OPT_DOCKER[:compose].fetch(flag, []))
|
|
435
|
+
if op.remove(':') || service == ':'
|
|
436
|
+
keys = Set.new
|
|
437
|
+
read_composefile('services', target: op.values_of('f', 'file')) { |data| keys.merge(data.keys) }
|
|
438
|
+
service = unless keys.empty?
|
|
439
|
+
choice_index('Add services', keys, multiple: multiple, force: !multiple,
|
|
440
|
+
attempts: multiple ? 1 : 3)
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
if multiple
|
|
444
|
+
op.concat(service) if service
|
|
445
|
+
op.append(delim: true, escape: true, strip: /^:/)
|
|
446
|
+
else
|
|
447
|
+
raise_error ArgumentError, 'no service was selected', hint: flag unless service
|
|
448
|
+
append_command(flag, service, op.extras, prompt: '::')
|
|
449
|
+
end
|
|
363
450
|
end
|
|
364
|
-
run(from:
|
|
451
|
+
run(from: from)
|
|
365
452
|
end
|
|
366
|
-
alias compose_ compose!
|
|
367
453
|
|
|
368
454
|
def container(flag, opts = [], id: nil)
|
|
369
455
|
cmd, opts = docker_session('container', flag, opts: opts)
|
|
370
456
|
rc = flag == :run || flag == :create
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
457
|
+
op = OPT_DOCKER[:container].yield_self do |data|
|
|
458
|
+
list = data.fetch(flag, [])
|
|
459
|
+
list += data[:create] if flag == :run
|
|
460
|
+
list += data[:update] if rc
|
|
461
|
+
OptionPartition.new(opts, list, cmd, project: self, args: rc || flag == :exec)
|
|
462
|
+
end
|
|
375
463
|
from = :"container:#{flag}"
|
|
376
464
|
case flag
|
|
377
465
|
when :run, :create, :exec
|
|
@@ -388,12 +476,12 @@ module Squared
|
|
|
388
476
|
when 'bind', 'volume', 'image', 'tmpfs'
|
|
389
477
|
type = v
|
|
390
478
|
else
|
|
391
|
-
raise_error
|
|
479
|
+
raise_error TypeError, "unknown: #{v}", hint: flag
|
|
392
480
|
end
|
|
393
481
|
elsif all.include?(k)
|
|
394
482
|
unless type
|
|
395
|
-
VAL_DOCKER[:run].each_pair do |key,
|
|
396
|
-
next unless
|
|
483
|
+
VAL_DOCKER[:run].each_pair do |key, val|
|
|
484
|
+
next unless val.include?(k)
|
|
397
485
|
|
|
398
486
|
type = key.to_s unless key == :common
|
|
399
487
|
break
|
|
@@ -412,18 +500,18 @@ module Squared
|
|
|
412
500
|
log_message(Logger::INFO, 'unrecognized option', subject: from, hint: k)
|
|
413
501
|
end
|
|
414
502
|
end
|
|
415
|
-
raise_error
|
|
503
|
+
raise_error TypeError, 'none specified', hint: flag unless type
|
|
416
504
|
cmd << "--mount type=#{type},#{args.join(',')}"
|
|
417
505
|
end
|
|
418
506
|
end
|
|
419
507
|
append_command(flag, id || tagmain, op.extras)
|
|
420
|
-
when :update
|
|
421
|
-
raise_error
|
|
508
|
+
when :update
|
|
509
|
+
raise_error ArgumentError, 'missing container', hint: flag if op.empty?
|
|
422
510
|
op.append(escape: true, strip: /^:/)
|
|
423
511
|
when :commit
|
|
424
512
|
latest = op.shift || tagmain
|
|
425
513
|
cmd << id << latest
|
|
426
|
-
raise_error
|
|
514
|
+
raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag unless op.empty?
|
|
427
515
|
return unless confirm_command(cmd.to_s, title: from, target: id, as: latest)
|
|
428
516
|
|
|
429
517
|
registry = option('registry') || @registry
|
|
@@ -442,38 +530,12 @@ module Squared
|
|
|
442
530
|
return image(:push, opts, id: latest, registry: registry)
|
|
443
531
|
else
|
|
444
532
|
if op.empty?
|
|
445
|
-
status =
|
|
446
|
-
no
|
|
447
|
-
|
|
448
|
-
when :inspect, :diff
|
|
449
|
-
no = false
|
|
450
|
-
when :start
|
|
451
|
-
status = %w[created exited]
|
|
452
|
-
no = false
|
|
453
|
-
when :stop, :pause
|
|
454
|
-
status = %w[running restarting]
|
|
455
|
-
when :restart
|
|
456
|
-
status = %w[running paused exited]
|
|
457
|
-
when :unpause
|
|
458
|
-
status << 'paused'
|
|
459
|
-
no = false
|
|
460
|
-
when :top, :stats
|
|
461
|
-
status << 'running'
|
|
462
|
-
cmd << '--no-stream' if flag == :stats
|
|
463
|
-
no = false
|
|
464
|
-
when :kill
|
|
465
|
-
status = %w[running restarting paused]
|
|
466
|
-
when :rm
|
|
467
|
-
status = %w[created exited dead]
|
|
468
|
-
end
|
|
469
|
-
ps = docker_output('ps -a', *status.map { |s| quote_option('filter', "status=#{s}") })
|
|
470
|
-
list_image(flag, ps, no: no, hint: "status: #{status.join(', ')}", from: from) do |img|
|
|
471
|
-
run(cmd.temp(img), from: from)
|
|
472
|
-
end
|
|
533
|
+
ps, status, no = filter_ps flag, from
|
|
534
|
+
cmd << '--no-stream' if flag == :stats
|
|
535
|
+
list_image(flag, ps, no: no, hint: status, from: from) { |img| run(cmd.temp(img), from: from) }
|
|
473
536
|
return
|
|
474
|
-
else
|
|
475
|
-
op.append(escape: true, strip: /^:/)
|
|
476
537
|
end
|
|
538
|
+
op.append(escape: true, strip: /^:/)
|
|
477
539
|
end
|
|
478
540
|
run(from: from)
|
|
479
541
|
end
|
|
@@ -481,16 +543,21 @@ module Squared
|
|
|
481
543
|
def image(flag, opts = [], sync: true, id: nil, registry: nil)
|
|
482
544
|
cmd, opts = docker_session('image', flag, opts: opts)
|
|
483
545
|
op = OptionPartition.new(opts, OPT_DOCKER[:image].fetch(flag, []), cmd, project: self)
|
|
484
|
-
exception =
|
|
546
|
+
exception = self.exception
|
|
485
547
|
banner = true
|
|
486
548
|
from = :"image:#{flag}"
|
|
487
549
|
case flag
|
|
488
|
-
when :
|
|
550
|
+
when :ls
|
|
489
551
|
if opts.size == op.size
|
|
490
552
|
index = 0
|
|
491
553
|
name = nil
|
|
492
|
-
opts.reverse_each
|
|
493
|
-
|
|
554
|
+
opts.reverse_each do |opt|
|
|
555
|
+
if (name = opt[/^name=["']?(.+?)["']?$/, 1])
|
|
556
|
+
opts.delete(opt)
|
|
557
|
+
break
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
list_image(:run, from: from) do |val|
|
|
494
561
|
container(:run, if name
|
|
495
562
|
opts.dup << "name=#{index == 0 ? name : "#{name}-#{index}"}"
|
|
496
563
|
else
|
|
@@ -499,19 +566,12 @@ module Squared
|
|
|
499
566
|
index += 1
|
|
500
567
|
end
|
|
501
568
|
return
|
|
502
|
-
else
|
|
503
|
-
op.clear
|
|
504
569
|
end
|
|
570
|
+
op.clear
|
|
505
571
|
when :rm
|
|
506
|
-
|
|
507
|
-
op << id
|
|
508
|
-
if option('y')
|
|
509
|
-
exception = false
|
|
510
|
-
banner = false
|
|
511
|
-
end
|
|
512
|
-
else
|
|
572
|
+
unless id
|
|
513
573
|
if op.empty?
|
|
514
|
-
list_image(:rm,
|
|
574
|
+
list_image(:rm, from: from) do |val|
|
|
515
575
|
image(:rm, opts, sync: sync, id: val)
|
|
516
576
|
end
|
|
517
577
|
else
|
|
@@ -519,8 +579,13 @@ module Squared
|
|
|
519
579
|
end
|
|
520
580
|
return
|
|
521
581
|
end
|
|
582
|
+
op << id
|
|
583
|
+
if option('y')
|
|
584
|
+
exception = false
|
|
585
|
+
banner = false
|
|
586
|
+
end
|
|
522
587
|
when :tag, :save
|
|
523
|
-
list_image(flag,
|
|
588
|
+
list_image(flag, from: from) do |val|
|
|
524
589
|
op << val
|
|
525
590
|
if flag == :tag
|
|
526
591
|
op << tagname("#{project}:#{op.first}")
|
|
@@ -530,8 +595,14 @@ module Squared
|
|
|
530
595
|
when :push
|
|
531
596
|
id ||= option('tag', ignore: false) || tagmain
|
|
532
597
|
registry ||= op.shift || option('registry') || @registry
|
|
533
|
-
|
|
534
|
-
|
|
598
|
+
unless id && op.empty?
|
|
599
|
+
if id
|
|
600
|
+
raise_error ArgumentError, "unrecognized args: #{op.join(', ')}", hint: flag
|
|
601
|
+
else
|
|
602
|
+
raise_error 'no id/tag', hint: flag
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
raise_error ArgumentError, 'username/registry not specified', hint: flag unless registry
|
|
535
606
|
registry.chomp!('/')
|
|
536
607
|
uri = shell_quote "#{registry}/#{id}"
|
|
537
608
|
op << uri
|
|
@@ -543,17 +614,18 @@ module Squared
|
|
|
543
614
|
exception = true
|
|
544
615
|
banner = false
|
|
545
616
|
end
|
|
546
|
-
|
|
547
|
-
|
|
617
|
+
run(cmd, sync: sync, exception: exception, banner: banner, from: from).tap do |ret|
|
|
618
|
+
success?(ret, flag == :tag || flag == :save)
|
|
619
|
+
end
|
|
548
620
|
end
|
|
549
621
|
|
|
550
622
|
def network(flag, opts = [], target: nil)
|
|
551
623
|
cmd, opts = docker_session('network', flag, opts: opts)
|
|
552
|
-
|
|
553
|
-
|
|
624
|
+
OptionPartition.new(opts, OPT_DOCKER[:network].fetch(flag, []), cmd, project: self)
|
|
625
|
+
.clear
|
|
554
626
|
from = :"network:#{flag}"
|
|
555
627
|
list_image(flag, docker_output('ps -a'), from: from) do |img|
|
|
556
|
-
|
|
628
|
+
success?(run(cmd.temp(target, img), from: from))
|
|
557
629
|
end
|
|
558
630
|
end
|
|
559
631
|
|
|
@@ -580,10 +652,10 @@ module Squared
|
|
|
580
652
|
def dockerfile(val = nil)
|
|
581
653
|
if val
|
|
582
654
|
@file = if val.is_a?(Array)
|
|
583
|
-
val = val.select { |file|
|
|
655
|
+
val = val.select { |file| exist?(file) }
|
|
584
656
|
val.size > 1 ? val : val.first
|
|
585
657
|
elsif val == true
|
|
586
|
-
DIR_DOCKER.find { |file|
|
|
658
|
+
DIR_DOCKER.find { |file| exist?(file) }
|
|
587
659
|
elsif val != 'Dockerfile'
|
|
588
660
|
val
|
|
589
661
|
end
|
|
@@ -612,8 +684,7 @@ module Squared
|
|
|
612
684
|
return session('docker', *cmd) unless opts
|
|
613
685
|
|
|
614
686
|
op = OptionPartition.new(opts, OPT_DOCKER[:common], project: self)
|
|
615
|
-
|
|
616
|
-
[ret, op.extras]
|
|
687
|
+
[session('docker', *op.to_a, *cmd), op.extras]
|
|
617
688
|
end
|
|
618
689
|
|
|
619
690
|
def docker_output(*cmd, **kwargs)
|
|
@@ -623,8 +694,8 @@ module Squared
|
|
|
623
694
|
def append_command(flag, val, list, target: @session, prompt: ':')
|
|
624
695
|
if list.delete(prompt)
|
|
625
696
|
list << readline('Enter command [args]', force: flag == :exec)
|
|
626
|
-
|
|
627
|
-
list << args
|
|
697
|
+
else
|
|
698
|
+
env('DOCKER_ARGS') { |args| list << args }
|
|
628
699
|
end
|
|
629
700
|
case flag
|
|
630
701
|
when :run
|
|
@@ -632,24 +703,10 @@ module Squared
|
|
|
632
703
|
target << basic_option('name', dnsname("#{name}_%s" % rand_s(6)))
|
|
633
704
|
end
|
|
634
705
|
when :exec
|
|
635
|
-
raise_error
|
|
636
|
-
end
|
|
637
|
-
target << val
|
|
638
|
-
case list.first
|
|
639
|
-
when '--'
|
|
640
|
-
target.merge(list.drop(1))
|
|
641
|
-
when /\A(--|&&)(\s|\z)/
|
|
642
|
-
target.merge(list)
|
|
643
|
-
else
|
|
644
|
-
target << list.shift
|
|
645
|
-
unless list.empty?
|
|
646
|
-
if list.first.start_with?('-')
|
|
647
|
-
target.merge(list)
|
|
648
|
-
else
|
|
649
|
-
target << list.join(' && ')
|
|
650
|
-
end
|
|
651
|
-
end
|
|
706
|
+
raise_error ArgumentError, 'nothing to execute', hint: flag if list.empty?
|
|
652
707
|
end
|
|
708
|
+
target << val << list.shift
|
|
709
|
+
target << list.join(' && ') unless list.empty?
|
|
653
710
|
end
|
|
654
711
|
|
|
655
712
|
def append_file(type, target: @session, index: 2)
|
|
@@ -698,17 +755,45 @@ module Squared
|
|
|
698
755
|
end
|
|
699
756
|
end
|
|
700
757
|
|
|
701
|
-
def
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
758
|
+
def filter_ps(flag, from = :'container:ps')
|
|
759
|
+
no = false
|
|
760
|
+
status = case flag.to_sym
|
|
761
|
+
when :start
|
|
762
|
+
%w[created exited]
|
|
763
|
+
when :stop, :pause
|
|
764
|
+
no = true
|
|
765
|
+
%w[running restarting]
|
|
766
|
+
when :restart
|
|
767
|
+
no = true
|
|
768
|
+
%w[running paused exited]
|
|
769
|
+
when :unpause
|
|
770
|
+
%w[paused]
|
|
771
|
+
when :top, :stats, :watch
|
|
772
|
+
%w[running]
|
|
773
|
+
when :kill
|
|
774
|
+
no = true
|
|
775
|
+
%w[running paused restarting]
|
|
776
|
+
when :rm
|
|
777
|
+
no = true
|
|
778
|
+
%w[created exited dead]
|
|
779
|
+
else
|
|
780
|
+
[]
|
|
781
|
+
end
|
|
782
|
+
cmd = docker_output("#{from.to_s.split(':').first} ps -a",
|
|
783
|
+
*status.map { |s| quote_option('filter', "status=#{s}") })
|
|
784
|
+
[cmd, status, no]
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
def list_image(flag, cmd = docker_output('image ls -a'), hint: nil, from: nil, no: true)
|
|
788
|
+
pwd_set do
|
|
789
|
+
index = 1
|
|
705
790
|
all = option('all', prefix: 'docker')
|
|
706
791
|
y = from == :'image:rm' && option('y', prefix: 'docker')
|
|
707
792
|
pat = /\b(?:#{dnsname(name)}|#{tagname(project)}|#{tagmain.split(':', 2).first})\b/
|
|
708
|
-
IO.popen(
|
|
793
|
+
IO.popen(cmd.temp('--format=json')).each do |line|
|
|
709
794
|
data = JSON.parse(line)
|
|
710
795
|
id = data['ID']
|
|
711
|
-
rt = [data['Repository'], data['Tag']].reject { |val| val == '<none>' }.join(':')
|
|
796
|
+
rt = [data['Repository'], data['Tag']].reject { |val| val.to_s.empty? || val == '<none>' }.join(':')
|
|
712
797
|
rt = nil if rt.empty?
|
|
713
798
|
aa = data['Names'] || (if rt && data['Repository']
|
|
714
799
|
dd = true
|
|
@@ -720,16 +805,16 @@ module Squared
|
|
|
720
805
|
next unless all || ee.match?(pat) || aa.match?(pat)
|
|
721
806
|
|
|
722
807
|
unless y
|
|
723
|
-
bb = index.
|
|
724
|
-
cc = bb.size
|
|
808
|
+
bb = index.to_s
|
|
809
|
+
cc = bb.size.succ
|
|
725
810
|
a = sub_style(ee, styles: theme[:inline])
|
|
726
|
-
b = "Execute #{sub_style(flag, styles: theme[:active])} on #{a
|
|
811
|
+
b = "Execute #{sub_style(flag, styles: theme[:active])} on #{a.subhint(ee == id ? nil : id)}"
|
|
727
812
|
e = time_format(time_since(data['CreatedAt']), pass: ['ms'])
|
|
728
813
|
f = sub_style(ARG[:BORDER][0], styles: theme[:inline])
|
|
729
|
-
g = ' ' *
|
|
814
|
+
g = ' ' * cc.succ
|
|
730
815
|
h = "#{sub_style(bb.rjust(cc), styles: theme[:current])} #{f} "
|
|
731
|
-
puts unless index ==
|
|
732
|
-
puts
|
|
816
|
+
puts unless index == 1
|
|
817
|
+
puts (h + sub_style(aa, styles: theme[:subject])).subhint("created #{e} ago")
|
|
733
818
|
cols = %w[Tag Status Ports]
|
|
734
819
|
cols << case flag
|
|
735
820
|
when :connect, :disconnect
|
|
@@ -744,89 +829,104 @@ module Squared
|
|
|
744
829
|
end
|
|
745
830
|
w = 9 + flag.to_s.size + 4 + ee.size
|
|
746
831
|
puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), styles: theme[:inline])
|
|
747
|
-
found = true
|
|
748
832
|
index += 1
|
|
749
|
-
next unless confirm("#{h + b}?", no ? 'N' : 'Y'
|
|
833
|
+
next unless confirm("#{h + b}?", no ? 'N' : 'Y')
|
|
750
834
|
|
|
751
835
|
puts if printfirst?
|
|
752
836
|
end
|
|
753
837
|
yield id
|
|
754
838
|
end
|
|
755
|
-
|
|
839
|
+
list_empty(hint: hint || from) if index == 1 && !y
|
|
756
840
|
end
|
|
757
841
|
rescue StandardError => e
|
|
758
842
|
on_error e, from
|
|
759
843
|
end
|
|
760
844
|
|
|
845
|
+
def list_empty(subject: name, hint: nil, **kwargs)
|
|
846
|
+
hint = "status: #{hint.join(', ')}" if hint.is_a?(Array)
|
|
847
|
+
puts log_message(Logger::INFO, 'none detected', subject: subject, hint: hint, **kwargs)
|
|
848
|
+
end
|
|
849
|
+
|
|
761
850
|
def confirm_command(*args, title: nil, target: nil, as: nil)
|
|
762
851
|
return false unless title && target
|
|
763
852
|
|
|
764
853
|
puts unless printfirst?
|
|
765
854
|
t = title.to_s.split(':')
|
|
766
855
|
emphasize(args, title: message(t.first.upcase, *t.drop(1)), border: borderstyle, sub: [
|
|
767
|
-
|
|
768
|
-
|
|
856
|
+
opt_style(theme[:header], /\A(\w+(?: => \w+)+)(.*)\z/),
|
|
857
|
+
opt_style(theme[:caution], /\A(.+)\z/)
|
|
769
858
|
])
|
|
770
859
|
printsucc
|
|
771
860
|
a = t.last.capitalize
|
|
772
861
|
b = sub_style(target, styles: theme[:subject])
|
|
773
862
|
c = as && sub_style(as, styles: theme[:inline])
|
|
774
|
-
confirm
|
|
775
|
-
end
|
|
776
|
-
|
|
777
|
-
def choice_command(flag)
|
|
778
|
-
msg, cmd
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
863
|
+
confirm "#{a} #{b}#{c ? " as #{c}" : ''}?", 'N'
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
def choice_command(flag, *action)
|
|
867
|
+
msg, cmd = case flag
|
|
868
|
+
when :exec
|
|
869
|
+
['Choose a container', 'ps -a']
|
|
870
|
+
when :bake
|
|
871
|
+
['Choose a target', 'buildx bake --list=type=targets']
|
|
872
|
+
when :connect, :disconnect
|
|
873
|
+
['Choose a network', 'network ls']
|
|
874
|
+
when :service
|
|
875
|
+
['Choose a service',
|
|
876
|
+
'compose ps -a ' \
|
|
877
|
+
"--format='table {{.Service}}\t{{.Name}}\t{{.Image}}\t{{.Command}}\t{{.Status}}\t{{.Ports}}'"]
|
|
878
|
+
else
|
|
879
|
+
['Choose an image',
|
|
880
|
+
'images -a ' \
|
|
881
|
+
"--format='table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}\t{{.Size}}'"]
|
|
882
|
+
end
|
|
788
883
|
lines = `#{docker_output(cmd)}`.lines
|
|
789
884
|
header = lines.shift
|
|
790
885
|
if lines.empty?
|
|
791
|
-
puts log_message(Logger::INFO, 'none found', subject: name,
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
886
|
+
puts log_message(Logger::INFO, 'none found', subject: name,
|
|
887
|
+
hint: "docker #{cmd.split(' ', 3)[0...2].join(' ')}")
|
|
888
|
+
return
|
|
889
|
+
end
|
|
890
|
+
puts " # #{header}"
|
|
891
|
+
multiple = false
|
|
892
|
+
parse = ->(val) { val.split(/\s+/)[0] }
|
|
893
|
+
ctx = flag.to_s
|
|
894
|
+
case flag
|
|
895
|
+
when :run, :exec
|
|
896
|
+
values = [['Options', flag == :run], ['Arguments', flag == :exec]]
|
|
897
|
+
when :rm, :bake
|
|
898
|
+
values = ['Options']
|
|
899
|
+
multiple = true
|
|
900
|
+
ctx = flag == :rm ? 'image rm' : "buildx bake -f #{shell_quote(dockerfile)}"
|
|
901
|
+
when :save
|
|
902
|
+
values = [['Output', true], 'Platform']
|
|
903
|
+
multiple = true
|
|
904
|
+
when :service
|
|
905
|
+
values = []
|
|
906
|
+
multiple = true
|
|
907
|
+
ctx = 'compose'
|
|
908
|
+
when :connect, :disconnect
|
|
909
|
+
values = ['Options', ['Container', true]]
|
|
910
|
+
ctx = "network #{flag}"
|
|
911
|
+
end
|
|
912
|
+
out, opts, args = choice_index(msg, lines, multiple: multiple, values: values)
|
|
913
|
+
cmd = docker_output(ctx, *action)
|
|
914
|
+
case flag
|
|
915
|
+
when :tag
|
|
916
|
+
args = tagjoin @registry, tag
|
|
917
|
+
when :save
|
|
918
|
+
opts = "#{opts}.tar" unless opts.end_with?('.tar')
|
|
919
|
+
cmd << quote_option('output', File.expand_path(opts))
|
|
920
|
+
if args
|
|
921
|
+
cmd << basic_option('platform', args)
|
|
922
|
+
args = nil
|
|
825
923
|
end
|
|
826
|
-
|
|
827
|
-
cmd <<
|
|
828
|
-
print_success if success?(run(cmd, { 'NO_COLOR' => 'true' }), ctx.start_with?(/network|tag|save/))
|
|
924
|
+
else
|
|
925
|
+
cmd << opts << '--'
|
|
829
926
|
end
|
|
927
|
+
cmd.merge(Array(out).map! { |val| parse.call(val) })
|
|
928
|
+
cmd << args
|
|
929
|
+
success?(run(cmd), ctx.start_with?(/(?:network|tag|save)/))
|
|
830
930
|
end
|
|
831
931
|
|
|
832
932
|
def filetype(val = dockerfile)
|
|
@@ -855,10 +955,9 @@ module Squared
|
|
|
855
955
|
|
|
856
956
|
def tagname(val)
|
|
857
957
|
val = val.split(':').map! { |s| charname(s.sub(/^\W+/, '')) }
|
|
858
|
-
val.join(':')
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
end
|
|
958
|
+
ret = val.join(':')
|
|
959
|
+
ret = val.first if val.size > 1 && ret.size > 128
|
|
960
|
+
ret[0..127]
|
|
862
961
|
end
|
|
863
962
|
|
|
864
963
|
def dnsname(val)
|