dip 3.8.3 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +61 -22
- data/lib/dip.rb +1 -2
- data/lib/dip/cli.rb +6 -0
- data/lib/dip/cli/dns.rb +0 -2
- data/lib/dip/cli/nginx.rb +0 -2
- data/lib/dip/commands/list.rb +20 -0
- data/lib/dip/commands/run.rb +24 -35
- data/lib/dip/config.rb +8 -0
- data/lib/dip/errors.rb +8 -0
- data/lib/dip/interaction_tree.rb +84 -0
- data/lib/dip/version.rb +1 -1
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 814f6f4b810a700663a169d6d34f535c010d7c9c9241a1343307f9657f50a9cd
|
4
|
+
data.tar.gz: 2d6b76d8e3459302accc2c264148083bb0b15d63fb365e9d4a12ed245820f843
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc0e2299a4eed706c48868779fe655c7207e9b8d6353ed5bbb5887f318d1099e8c1db556f05af000b2869dc8e483b58ed874382062ebc2ea23d27def0481f705
|
7
|
+
data.tar.gz: f0a832bb6d9394b84a9e10b248ea9f1122d6cbed0fd8c973c679f9fc34faad066477b044bd8090cb8000b63d47f1d7329a67fd83e55a5d84e11c49379ec88d3e
|
data/README.md
CHANGED
@@ -6,10 +6,7 @@
|
|
6
6
|
|
7
7
|
Docker Interaction Process
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
<a href="https://evilmartians.com/?utm_source=dip">
|
12
|
-
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
9
|
+
A command-line utility that gives the "native" interaction with applications configured with Docker Compose. It is for local development only. In practice, it creates the feeling that you work without containers.
|
13
10
|
|
14
11
|
## Presentations and examples
|
15
12
|
|
@@ -22,7 +19,7 @@ Command line utility that gives the "native" interaction with applications confi
|
|
22
19
|
|
23
20
|
## Integration with shell
|
24
21
|
|
25
|
-
Dip can be injected into current shell (ZSH or Bash).
|
22
|
+
Dip can be injected into the current shell (ZSH or Bash).
|
26
23
|
|
27
24
|
```sh
|
28
25
|
eval "$(dip console)"
|
@@ -38,9 +35,9 @@ down
|
|
38
35
|
provision
|
39
36
|
```
|
40
37
|
|
41
|
-
When we change the current directory, all shell aliases will be automatically removed. But when we
|
38
|
+
When we change the current directory, all shell aliases will be automatically removed. But when we enter back into a directory with a `dip.yml` file, then shell aliases will be renewed.
|
42
39
|
|
43
|
-
Also, in shell mode Dip is trying to determine passed
|
40
|
+
Also, in shell mode Dip is trying to determine manually passed environment variables. For example:
|
44
41
|
|
45
42
|
```sh
|
46
43
|
VERSION=20180515103400 rails db:migrate:down
|
@@ -51,20 +48,34 @@ After that, it will be automatically applied when you open your preferred termin
|
|
51
48
|
|
52
49
|
## Installation
|
53
50
|
|
54
|
-
You have
|
51
|
+
You have many ways.
|
52
|
+
|
53
|
+
### Homebrew
|
54
|
+
|
55
55
|
|
56
|
-
|
56
|
+
You can use [Homebrew](https://brew.sh) on macOS (or [Linux](https://docs.brew.sh/Homebrew-on-Linux)).
|
57
|
+
|
58
|
+
Today Homebrew tap for DIP is located at https://github.com/bibendi/homebrew-dip
|
59
|
+
|
60
|
+
```sh
|
61
|
+
brew tap bibendi/dip
|
62
|
+
brew install dip
|
63
|
+
```
|
64
|
+
|
65
|
+
### Ruby Gem
|
57
66
|
|
58
67
|
```sh
|
59
68
|
gem install dip
|
60
69
|
```
|
61
70
|
|
62
|
-
|
71
|
+
### Precompiled binary
|
72
|
+
|
73
|
+
If you don't have installed Ruby, then you could copy a precompiled binary to your system.
|
63
74
|
It can be found at [releases page](https://github.com/bibendi/dip/releases)
|
64
75
|
or type bellow into your terminal:
|
65
76
|
|
66
77
|
```sh
|
67
|
-
curl -L https://github.com/bibendi/dip/releases/download/
|
78
|
+
curl -L https://github.com/bibendi/dip/releases/download/4.0.0/dip-`uname -s`-`uname -m` > /usr/local/bin/dip
|
68
79
|
chmod +x /usr/local/bin/dip
|
69
80
|
```
|
70
81
|
|
@@ -86,13 +97,13 @@ The configuration file `dip.yml` should be placed in a project root directory.
|
|
86
97
|
Also, in some cases, you may want to change the default config path by providing an environment variable `DIP_FILE`.
|
87
98
|
If nearby places `dip.override.yml` file it would be merged into the main config.
|
88
99
|
|
89
|
-
Below is an example of real config.
|
100
|
+
Below is an example of a real config.
|
90
101
|
`dip.yml` reference will be written soon.
|
91
|
-
Also, you can check out examples in the top.
|
92
|
-
|
102
|
+
Also, you can check out examples in the top.
|
93
103
|
|
94
104
|
```yml
|
95
|
-
version
|
105
|
+
# Required minimum dip version
|
106
|
+
version: '4'
|
96
107
|
|
97
108
|
environment:
|
98
109
|
COMPOSE_EXT: development
|
@@ -106,32 +117,48 @@ compose:
|
|
106
117
|
|
107
118
|
interaction:
|
108
119
|
bash:
|
120
|
+
description: Open the Bash shell in app's container
|
109
121
|
service: app
|
110
|
-
|
122
|
+
compose:
|
123
|
+
run_options: [no-deps]
|
111
124
|
|
112
125
|
bundle:
|
126
|
+
description: Run Bundler commands
|
113
127
|
service: app
|
114
128
|
command: bundle
|
115
129
|
|
116
130
|
rake:
|
131
|
+
description: Run Rake commands
|
117
132
|
service: app
|
118
133
|
command: bundle exec rake
|
119
134
|
|
120
135
|
rspec:
|
136
|
+
description: Run Rspec commands
|
121
137
|
service: app
|
122
138
|
environment:
|
123
139
|
RAILS_ENV: test
|
124
140
|
command: bundle exec rspec
|
125
141
|
|
126
142
|
rails:
|
143
|
+
description: Run Rails commands
|
127
144
|
service: app
|
128
145
|
command: bundle exec rails
|
129
146
|
subcommands:
|
130
147
|
s:
|
148
|
+
description: Run Rails server at http://localhost:3000
|
131
149
|
service: web
|
132
|
-
|
150
|
+
compose:
|
151
|
+
run_options: [service-ports]
|
152
|
+
|
153
|
+
sidekiq:
|
154
|
+
description: Run sidekiq in background
|
155
|
+
service: worker
|
156
|
+
compose:
|
157
|
+
method: up
|
158
|
+
run_options: [detach]
|
133
159
|
|
134
160
|
psql:
|
161
|
+
description: Run Postgres psql console
|
135
162
|
service: app
|
136
163
|
command: psql -h pg -U postgres
|
137
164
|
|
@@ -143,27 +170,39 @@ provision:
|
|
143
170
|
|
144
171
|
### dip run
|
145
172
|
|
146
|
-
Run commands defined within `interaction` section of dip.yml
|
173
|
+
Run commands defined within the `interaction` section of dip.yml
|
147
174
|
|
148
175
|
```sh
|
149
176
|
dip run rails c
|
150
177
|
dip run rake db:migrate
|
151
178
|
```
|
152
179
|
|
153
|
-
`run` argument can be
|
180
|
+
`run` argument can be omitted
|
154
181
|
|
155
182
|
```sh
|
156
183
|
dip rake db:migrate
|
157
184
|
dip VERSION=12352452 rake db:rollback
|
158
185
|
```
|
159
186
|
|
187
|
+
### dip ls
|
188
|
+
|
189
|
+
List al available run commands.
|
190
|
+
|
191
|
+
```sh
|
192
|
+
dip ls
|
193
|
+
|
194
|
+
bash # Open the Bash shell in app's container
|
195
|
+
rails # Run Rails command
|
196
|
+
rails s # Run Rails server at http://localhost:3000
|
197
|
+
```
|
198
|
+
|
160
199
|
### dip provision
|
161
200
|
|
162
201
|
Run commands each by each from `provision` section of dip.yml
|
163
202
|
|
164
203
|
### dip compose
|
165
204
|
|
166
|
-
Run docker-compose commands that are configured according
|
205
|
+
Run docker-compose commands that are configured according to the application's dip.yml :
|
167
206
|
|
168
207
|
```sh
|
169
208
|
dip compose COMMAND [OPTIONS]
|
@@ -199,7 +238,7 @@ volumes:
|
|
199
238
|
|
200
239
|
### dip nginx
|
201
240
|
|
202
|
-
Runs Nginx server container based on [bibendi/nginx-proxy](https://github.com/bibendi/nginx-proxy) image. An application's docker-compose.yml should
|
241
|
+
Runs Nginx server container based on [bibendi/nginx-proxy](https://github.com/bibendi/nginx-proxy) image. An application's docker-compose.yml should contain environment variable `VIRTUAL_HOST` and `VIRTUAL_PATH` and connects to external network `frontend`.
|
203
242
|
|
204
243
|
foo-project/docker-compose.yml
|
205
244
|
|
@@ -255,7 +294,7 @@ curl www.bar-app.docker/api/v1/baz_service/qzz
|
|
255
294
|
|
256
295
|
### dip dns
|
257
296
|
|
258
|
-
Runs DNS server container based on https://github.com/aacebedo/dnsdock It used for container to container requests through
|
297
|
+
Runs a DNS server container based on https://github.com/aacebedo/dnsdock. It is used for container to container requests through Nginx. An application's docker-compose.yml should define `dns` configuration with environment variable `$DIP_DNS` and connect to external network `frontend`. `$DIP_DNS` will be automatically assigned by dip.
|
259
298
|
|
260
299
|
```sh
|
261
300
|
dip dns up
|
data/lib/dip.rb
CHANGED
data/lib/dip/cli.rb
CHANGED
@@ -39,6 +39,12 @@ module Dip
|
|
39
39
|
end
|
40
40
|
map %w(--version -v) => :version
|
41
41
|
|
42
|
+
desc 'ls', 'List available run commands'
|
43
|
+
def ls
|
44
|
+
require_relative 'commands/list'
|
45
|
+
Dip::Commands::List.new.execute
|
46
|
+
end
|
47
|
+
|
42
48
|
desc 'compose CMD [OPTIONS]', 'Run docker-compose commands'
|
43
49
|
def compose(*argv)
|
44
50
|
require_relative 'commands/compose'
|
data/lib/dip/cli/dns.rb
CHANGED
@@ -22,7 +22,6 @@ module Dip
|
|
22
22
|
desc: 'Docker image name'
|
23
23
|
method_option :domain, aliases: '-d', type: :string, default: "docker",
|
24
24
|
desc: 'Top level domain'
|
25
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
26
25
|
def up
|
27
26
|
if options[:help]
|
28
27
|
invoke :help, ['up']
|
@@ -37,7 +36,6 @@ module Dip
|
|
37
36
|
).execute
|
38
37
|
end
|
39
38
|
end
|
40
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
41
39
|
|
42
40
|
desc "down", "Stop dnsdock container"
|
43
41
|
method_option :help, aliases: '-h', type: :boolean,
|
data/lib/dip/cli/nginx.rb
CHANGED
@@ -22,7 +22,6 @@ module Dip
|
|
22
22
|
desc: 'Docker image name'
|
23
23
|
method_option :domain, aliases: '-d', type: :string, default: "docker",
|
24
24
|
desc: 'Top level domain'
|
25
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
26
25
|
def up
|
27
26
|
if options[:help]
|
28
27
|
invoke :help, ['up']
|
@@ -37,7 +36,6 @@ module Dip
|
|
37
36
|
).execute
|
38
37
|
end
|
39
38
|
end
|
40
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
41
39
|
|
42
40
|
desc "down", "Stop nginx container"
|
43
41
|
method_option :help, aliases: '-h', type: :boolean,
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../command'
|
4
|
+
require_relative '../interaction_tree'
|
5
|
+
|
6
|
+
module Dip
|
7
|
+
module Commands
|
8
|
+
class List < Dip::Command
|
9
|
+
def execute
|
10
|
+
tree = InteractionTree.new(Dip.config.interaction).list
|
11
|
+
|
12
|
+
longest_name = tree.keys.map(&:size).max
|
13
|
+
|
14
|
+
tree.each do |name, command|
|
15
|
+
puts "#{name.ljust(longest_name)} ##{command[:description] ? ' ' + command[:description] : ''}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/dip/commands/run.rb
CHANGED
@@ -2,58 +2,47 @@
|
|
2
2
|
|
3
3
|
require 'shellwords'
|
4
4
|
require_relative '../command'
|
5
|
+
require_relative '../interaction_tree'
|
5
6
|
require_relative 'compose'
|
6
7
|
|
7
8
|
module Dip
|
8
9
|
module Commands
|
9
10
|
class Run < Dip::Command
|
10
|
-
def initialize(cmd,
|
11
|
-
@
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def initialize(cmd, *argv)
|
12
|
+
@command, @argv = InteractionTree.
|
13
|
+
new(Dip.config.interaction).
|
14
|
+
find(cmd, *argv)&.
|
15
|
+
values_at(:command, :argv)
|
16
|
+
|
17
|
+
raise Dip::Error, "Command `#{[cmd, *argv].join(' ')}` not recognized!" unless command
|
18
|
+
|
19
|
+
Dip.env.merge(command[:environment])
|
15
20
|
end
|
16
21
|
|
17
|
-
# TODO: Refactor
|
18
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
19
22
|
def execute
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
command.merge!(subcommand)
|
26
|
-
elsif @subcmd
|
27
|
-
@argv.unshift(@subcmd.to_s)
|
28
|
-
end
|
23
|
+
Dip::Commands::Compose.new(
|
24
|
+
command[:compose][:method],
|
25
|
+
*compose_arguments
|
26
|
+
).execute
|
27
|
+
end
|
29
28
|
|
30
|
-
|
29
|
+
private
|
31
30
|
|
32
|
-
|
31
|
+
attr_reader :command, :argv
|
33
32
|
|
34
|
-
|
35
|
-
compose_argv
|
33
|
+
def compose_arguments
|
34
|
+
compose_argv = command[:compose][:run_options].dup
|
36
35
|
|
37
|
-
if
|
36
|
+
if command[:compose][:method] == "run"
|
38
37
|
compose_argv.concat(run_vars)
|
39
38
|
compose_argv << "--rm"
|
40
39
|
end
|
41
40
|
|
42
|
-
compose_argv << command.fetch(:service)
|
43
|
-
compose_argv
|
44
|
-
compose_argv.concat(
|
45
|
-
|
46
|
-
Dip::Commands::Compose.new(compose_method, *compose_argv).execute
|
47
|
-
end
|
48
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
49
|
-
|
50
|
-
def prepare_compose_run_options(value)
|
51
|
-
return [] unless value
|
41
|
+
compose_argv << command.fetch(:service)
|
42
|
+
compose_argv.concat(command[:command].to_s.shellsplit)
|
43
|
+
compose_argv.concat(argv)
|
52
44
|
|
53
|
-
|
54
|
-
o = o.start_with?("-") ? o : "--#{o}"
|
55
|
-
o.shellsplit
|
56
|
-
end.flatten
|
45
|
+
compose_argv
|
57
46
|
end
|
58
47
|
|
59
48
|
def run_vars
|
data/lib/dip/config.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "yaml"
|
4
4
|
require "erb"
|
5
5
|
|
6
|
+
require "dip/version"
|
6
7
|
require "dip/ext/hash"
|
7
8
|
|
8
9
|
using ActiveSupportHashHelpers
|
@@ -52,6 +53,13 @@ module Dip
|
|
52
53
|
raise ArgumentError, "Dip config not found at path '#{self.class.path}'" unless self.class.exist?
|
53
54
|
|
54
55
|
config = self.class.load_yaml
|
56
|
+
|
57
|
+
unless Gem::Version.new(Dip::VERSION) >= Gem::Version.new(config.fetch(:version))
|
58
|
+
raise VersionMismatchError, "Your dip version is `#{Dip::VERSION}`, " \
|
59
|
+
"but config requires minimum version `#{config[:version]}`. " \
|
60
|
+
"Please upgrade your dip!"
|
61
|
+
end
|
62
|
+
|
55
63
|
config.deep_merge!(self.class.load_yaml(self.class.override_path))
|
56
64
|
|
57
65
|
@config = config
|
data/lib/dip/errors.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shellwords"
|
4
|
+
require "dip/ext/hash"
|
5
|
+
|
6
|
+
using ActiveSupportHashHelpers
|
7
|
+
|
8
|
+
module Dip
|
9
|
+
class InteractionTree
|
10
|
+
def initialize(entries)
|
11
|
+
@entries = entries
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(name, *argv)
|
15
|
+
entry = entries[name.to_sym]
|
16
|
+
return unless entry
|
17
|
+
|
18
|
+
commands = expand(name, entry)
|
19
|
+
|
20
|
+
keys = [name, *argv]
|
21
|
+
rest = []
|
22
|
+
|
23
|
+
keys.size.times do
|
24
|
+
if (command = commands[keys.join(" ")])
|
25
|
+
return {command: command, argv: rest.reverse!}
|
26
|
+
else
|
27
|
+
rest << keys.pop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def list
|
33
|
+
entries.each_with_object({}) do |(name, entry), memo|
|
34
|
+
expand(name.to_s, entry, tree: memo)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :entries
|
41
|
+
|
42
|
+
def expand(name, entry, tree: {})
|
43
|
+
cmd = build_command(entry)
|
44
|
+
|
45
|
+
tree[name] = cmd
|
46
|
+
|
47
|
+
entry[:subcommands]&.each do |sub_name, sub_entry|
|
48
|
+
sub_command_defaults!(sub_entry)
|
49
|
+
|
50
|
+
expand("#{name} #{sub_name}", entry.deep_merge(sub_entry), tree: tree)
|
51
|
+
end
|
52
|
+
|
53
|
+
tree
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_command(entry)
|
57
|
+
{
|
58
|
+
description: entry[:description],
|
59
|
+
service: entry.fetch(:service),
|
60
|
+
command: entry[:command],
|
61
|
+
environment: entry[:environment] || {},
|
62
|
+
compose: {
|
63
|
+
method: entry.dig(:compose, :method) || entry[:compose_method] || "run",
|
64
|
+
run_options: compose_run_options(entry.dig(:compose, :run_options) || entry[:compose_run_options])
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def sub_command_defaults!(entry)
|
70
|
+
entry[:command] ||= nil
|
71
|
+
entry[:subcommands] ||= nil
|
72
|
+
entry[:description] ||= nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def compose_run_options(value)
|
76
|
+
return [] unless value
|
77
|
+
|
78
|
+
value.map do |o|
|
79
|
+
o = o.start_with?("-") ? o : "--#{o}"
|
80
|
+
o.shellsplit
|
81
|
+
end.flatten
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/dip/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- bibendi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '13.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '13.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -144,13 +144,16 @@ files:
|
|
144
144
|
- lib/dip/commands/compose.rb
|
145
145
|
- lib/dip/commands/console.rb
|
146
146
|
- lib/dip/commands/dns.rb
|
147
|
+
- lib/dip/commands/list.rb
|
147
148
|
- lib/dip/commands/nginx.rb
|
148
149
|
- lib/dip/commands/provision.rb
|
149
150
|
- lib/dip/commands/run.rb
|
150
151
|
- lib/dip/commands/ssh.rb
|
151
152
|
- lib/dip/config.rb
|
152
153
|
- lib/dip/environment.rb
|
154
|
+
- lib/dip/errors.rb
|
153
155
|
- lib/dip/ext/hash.rb
|
156
|
+
- lib/dip/interaction_tree.rb
|
154
157
|
- lib/dip/run_vars.rb
|
155
158
|
- lib/dip/version.rb
|
156
159
|
homepage: https://github.com/bibendi/dip
|
@@ -166,14 +169,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
166
169
|
requirements:
|
167
170
|
- - ">="
|
168
171
|
- !ruby/object:Gem::Version
|
169
|
-
version: '
|
172
|
+
version: '2.3'
|
170
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
174
|
requirements:
|
172
175
|
- - ">="
|
173
176
|
- !ruby/object:Gem::Version
|
174
177
|
version: '0'
|
175
178
|
requirements: []
|
176
|
-
|
179
|
+
rubyforge_project:
|
180
|
+
rubygems_version: 2.7.7
|
177
181
|
signing_key:
|
178
182
|
specification_version: 4
|
179
183
|
summary: Ruby gem CLI tool for better interacting docker-compose files.
|