dip 4.1.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ceb8c2f3312fdbc11a07bb73df3bb6c1a7a5bffbbab5b8adfd9a40585acb463
4
- data.tar.gz: 2f34aae2a69df0f8356245558f996f37ae459c72bd00136b39bd6bce63fc3977
3
+ metadata.gz: 33e5ab547c594c58ea29e95555ac2d055480456f8427c1673db014a4d75388df
4
+ data.tar.gz: 2304c89be09d4ad6a348f3fe66492bcee50292b430df54d12902346cbb005aab
5
5
  SHA512:
6
- metadata.gz: acd4d9ac91aadf4ea842350a3bcd2cf18c6ef05c55c6a659f2f32a822b03fa0effbde00e163ad8de9dc98e24dd5d0329320eb79f03b6b109cc6b318d07b1b928
7
- data.tar.gz: 3f0933d9bd9c482b733bb5672fd23b0def1fbfe6484166f759a017b6fc6beb5e20554b6de54a46009da8856894fbfbd0376cde2e3103426290205bcfe540965e
6
+ metadata.gz: 58a00ed2d495ec460579afde5a6e1a7df48983d7979512985e2606b606ab8731aa3b281c1d0a3387534f147a3cd957a9d5884f69a7997fd72c402155e2c7c7c3
7
+ data.tar.gz: 34aa9e6d7739a39633431d8756446bc49be63ab9329b2afeaf6e6133745f1b84614d926ad5a13e50330bb7f6425604764d6cf51eae23cb0bf6f8643bc8d3e80c
data/README.md CHANGED
@@ -8,11 +8,16 @@ Docker Interaction Process
8
8
 
9
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.
10
10
 
11
+ <p float="left">
12
+ <a href="https://evilmartians.com/?utm_source=dip"><img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" height="80" /></a>
13
+ <img src="https://ya-webdesign.com/images250_/vertical-divider-png-1.png" width="50" height="100" />
14
+ <a href="https://www.jetbrains.com/?from=DIP"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/JetBrains_Logo_2016.svg/1200px-JetBrains_Logo_2016.svg.png" alt="Sponsored by JetBrains" height="100" /></a></p>
15
+
11
16
  ## Presentations and examples
12
17
 
13
18
  - [Local development with Docker containers](https://slides.com/bibendi/dip)
14
- - [Dockerized Ruby on Rails application](https://github.com/bibendi/dip-example-rails)
15
- - [Dockerized Node.js application](https://github.com/bibendi/yt-graphql-react-event-booking-api)
19
+ - Dockerized Ruby on Rails applications: [one](https://github.com/lewagon/rails-k8s-demo), [two](https://github.com/bibendi/dip-example-rails), [three](https://github.com/evilmartians/evil_chat)
20
+ - Dockerized Node.js application: [one](https://github.com/bibendi/twinkle.js), [two](https://github.com/bibendi/yt-graphql-react-event-booking-api)
16
21
  - [Dockerized Ruby gem](https://github.com/bibendi/schked)
17
22
 
18
23
  [![asciicast](https://asciinema.org/a/210236.svg)](https://asciinema.org/a/210236)
@@ -39,11 +44,11 @@ When we change the current directory, all shell aliases will be automatically re
39
44
 
40
45
  Also, in shell mode Dip is trying to determine manually passed environment variables. For example:
41
46
 
42
- ```sh
47
+ ```shhttps://ya-webdesign.com/images250_/vertical-divider-png-1.pnghttps://ya-webdesign.com/images250_/vertical-divider-png-1.png
43
48
  VERSION=20180515103400 rails db:migrate:down
44
49
  ```
45
50
 
46
- You could add this `eval` at the end of your `~/.zshrc`, or `~/.bashrc`, or `~/.bash_profile`.
51
+ You could add this `eval` at the end of your `~/.zshrc`, or `~/.bashrc`, or `~/.bash_profile`.
47
52
  After that, it will be automatically applied when you open your preferred terminal.
48
53
 
49
54
  ## Installation
@@ -70,14 +75,14 @@ gem install dip
70
75
 
71
76
  ### Precompiled binary
72
77
 
73
- If you don't have installed Ruby, then you could copy a precompiled binary to your system.
78
+ If you don't have installed Ruby, then you could copy a precompiled binary to your system.
74
79
  It can be found at [releases page](https://github.com/bibendi/dip/releases)
75
80
  or type bellow into your terminal:
76
81
 
77
82
  ```sh
78
- curl -L https://github.com/bibendi/dip/releases/download/4.1.0/dip-`uname -s`-`uname -m` > /usr/local/bin/dip
83
+ curl -L https://github.com/bibendi/dip/releases/download/v6.1.0/dip-`uname -s`-`uname -m` > /usr/local/bin/dip
79
84
  chmod +x /usr/local/bin/dip
80
- ```
85
+ ```
81
86
 
82
87
  ## Docker installation
83
88
 
@@ -93,17 +98,17 @@ dip SUBCOMMAND --help
93
98
 
94
99
  ### dip.yml
95
100
 
96
- The configuration file `dip.yml` should be placed in a project root directory.
101
+ The configuration is loaded from `dip.yml` file. It may be located in a working directory, or it will be found in the nearest parent directory up to the file system root. If nearby places `dip.override.yml` file, it will be merged into the main config.
102
+
97
103
  Also, in some cases, you may want to change the default config path by providing an environment variable `DIP_FILE`.
98
- If nearby places `dip.override.yml` file it would be merged into the main config.
99
104
 
100
- Below is an example of a real config.
101
- `dip.yml` reference will be written soon.
102
- Also, you can check out examples in the top.
105
+ Below is an example of a real config.
106
+ Config file reference will be written soon.
107
+ Also, you can check out examples at the top.
103
108
 
104
109
  ```yml
105
110
  # Required minimum dip version
106
- version: '4'
111
+ version: '4.1'
107
112
 
108
113
  environment:
109
114
  COMPOSE_EXT: development
@@ -119,6 +124,7 @@ interaction:
119
124
  bash:
120
125
  description: Open the Bash shell in app's container
121
126
  service: app
127
+ command: bash
122
128
  compose:
123
129
  run_options: [no-deps]
124
130
 
@@ -148,7 +154,7 @@ interaction:
148
154
  description: Run Rails server at http://localhost:3000
149
155
  service: web
150
156
  compose:
151
- run_options: [service-ports]
157
+ run_options: [service-ports, use-aliases]
152
158
 
153
159
  sidekiq:
154
160
  description: Run sidekiq in background
@@ -169,6 +175,44 @@ provision:
169
175
  - dip bash -c ./bin/setup
170
176
  ```
171
177
 
178
+ ### Predefined environment variables
179
+
180
+ #### $DIP_OS
181
+
182
+ Current OS architecture (e.g. `linux`, `darwin`, `freebsd`, and so on). Sometime it may be useful to have one common `docker-compose.yml` and OS-dependent Compose configs.
183
+
184
+ #### $DIP_WORK_DIR_REL_PATH
185
+
186
+ Relative path from the current directory to the nearest directory where a Dip's config is found. It is useful when you need to mount a specific local directory to a container along with ability to change its working dir. For example:
187
+
188
+ ```
189
+ - project_root
190
+ |- dip.yml (1)
191
+ |- docker-compose.yml (2)
192
+ |- sub-project-dir
193
+ |- your current directory is here <<<
194
+ ```
195
+
196
+ ```yml
197
+ # dip.yml (1)
198
+ environment:
199
+ WORK_DIR: /app/${DIP_WORK_DIR_REL_PATH}
200
+ ```
201
+
202
+ ```yml
203
+ # docker-compose.yml (2)
204
+ services:
205
+ app:
206
+ working_dir: ${WORK_DIR:-/app}
207
+ ```
208
+
209
+ ```sh
210
+ cd sub-project-dir
211
+ dip run bash -c pwd
212
+ ```
213
+
214
+ returned is `/app/sub-project-dir`.
215
+
172
216
  ### dip run
173
217
 
174
218
  Run commands defined within the `interaction` section of dip.yml
@@ -178,16 +222,27 @@ dip run rails c
178
222
  dip run rake db:migrate
179
223
  ```
180
224
 
181
- `run` argument can be omitted
225
+ Also, `run` argument can be omitted
182
226
 
183
227
  ```sh
184
228
  dip rake db:migrate
229
+ ```
230
+
231
+ You can pass in a custom environment variable into a container:
232
+
233
+ ```sh
185
234
  dip VERSION=12352452 rake db:rollback
186
235
  ```
187
236
 
237
+ Use options `-p, --publish=[]` if you need to additionally publish a container's port(s) to the host unless this behaviour is not configured at dip.yml:
238
+
239
+ ```sh
240
+ dip run -p 3000:3000 bundle exec rackup config.ru
241
+ ```
242
+
188
243
  ### dip ls
189
244
 
190
- List al available run commands.
245
+ List all available run commands.
191
246
 
192
247
  ```sh
193
248
  dip ls
@@ -237,6 +292,21 @@ volumes:
237
292
  name: ssh_data
238
293
  ```
239
294
 
295
+ if you want to use non-root user you can specify UID like so:
296
+
297
+ ```
298
+ dip ssh up -u 1000
299
+ ```
300
+
301
+ This especially helpful if you have something like this in your docker-compose.yml:
302
+
303
+ ```
304
+ services:
305
+ web:
306
+ user: "1000:1000"
307
+
308
+ ```
309
+
240
310
  ### dip nginx
241
311
 
242
312
  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`.
@@ -293,6 +363,20 @@ curl www.bar-app.docker/api/v1/quz
293
363
  curl www.bar-app.docker/api/v1/baz_service/qzz
294
364
  ```
295
365
 
366
+ #### Pass SSL certificates
367
+
368
+ ```sh
369
+ dip nginx up -c $HOME/ssl_certificates
370
+ ```
371
+
372
+ #### Publish more than one port to localhost
373
+
374
+ Just pass a list, separated by a space:
375
+
376
+ ```sh
377
+ dip nginx up -p 80:80 443:443
378
+ ```
379
+
296
380
  ### dip dns
297
381
 
298
382
  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.
data/exe/dip CHANGED
@@ -4,16 +4,16 @@
4
4
  lib_path = File.expand_path('../lib', __dir__)
5
5
  $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
6
 
7
- require 'dip'
8
- require 'dip/cli'
9
- require 'dip/run_vars'
10
-
11
7
  begin
12
- require 'pry-byebug' if Dip.debug?
8
+ require 'pry-byebug' if ENV["DIP_ENV"] == "debug"
13
9
  rescue LoadError
14
- puts "pry-byebug not found!"
10
+ # do nothing
15
11
  end
16
12
 
13
+ require 'dip'
14
+ require 'dip/cli'
15
+ require 'dip/run_vars'
16
+
17
17
  Signal.trap('INT') do
18
18
  warn("\n#{caller.join("\n")}: interrupted")
19
19
  exit(1)
data/lib/dip.rb CHANGED
@@ -11,7 +11,7 @@ module Dip
11
11
  end
12
12
 
13
13
  def env
14
- @env ||= Dip::Environment.new(Dip::Config.exist? ? config.environment : {})
14
+ @env ||= Dip::Environment.new(config.exist? ? config.environment : {})
15
15
  end
16
16
 
17
17
  def bin_path
@@ -5,6 +5,8 @@ require 'dip/run_vars'
5
5
 
6
6
  module Dip
7
7
  class CLI < Thor
8
+ TOP_LEVEL_COMMANDS = %w[help version ls compose up stop down run provision ssh dns nginx console].freeze
9
+
8
10
  class << self
9
11
  # Hackery. Take the run method away from Thor so that we can redefine it.
10
12
  def is_thor_reserved_word?(word, type)
@@ -13,24 +15,24 @@ module Dip
13
15
  super
14
16
  end
15
17
 
16
- def start(argv)
17
- super Dip::RunVars.call(argv, ENV)
18
+ def exit_on_failure?
19
+ true
18
20
  end
19
- end
20
21
 
21
- stop_on_unknown_option! :up
22
+ def start(argv)
23
+ argv = Dip::RunVars.call(argv, ENV)
22
24
 
23
- def method_missing(cmd, *args)
24
- if Dip.config.interaction.key?(cmd.to_sym)
25
- self.class.start(["run", cmd.to_s, *args])
26
- else
27
- super
25
+ cmd = argv.first
26
+
27
+ if cmd && !TOP_LEVEL_COMMANDS.include?(cmd) && Dip.config.exist? && Dip.config.interaction.key?(cmd.to_sym)
28
+ argv.unshift("run")
29
+ end
30
+
31
+ super Dip::RunVars.call(argv, ENV)
28
32
  end
29
33
  end
30
34
 
31
- def respond_to_missing?(cmd)
32
- Dip.config.interaction.key?(cmd.to_sym)
33
- end
35
+ stop_on_unknown_option! :run
34
36
 
35
37
  desc 'version', 'dip version'
36
38
  def version
@@ -66,10 +68,17 @@ module Dip
66
68
  compose("down", *argv)
67
69
  end
68
70
 
69
- desc 'CMD or dip run CMD [OPTIONS]', 'Run configured command in a docker-compose service'
71
+ desc 'run [OPTIONS] CMD [ARGS]', 'Run configured command in a docker-compose service. `run` prefix may be omitted'
72
+ method_option :publish, aliases: '-p', type: :string, repeatable: true,
73
+ desc: "Publish a container's port(s) to the host"
74
+ method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
70
75
  def run(*argv)
71
- require_relative 'commands/run'
72
- Dip::Commands::Run.new(*argv).execute
76
+ if argv.empty? || options[:help]
77
+ invoke :help, ['run']
78
+ else
79
+ require_relative 'commands/run'
80
+ Dip::Commands::Run.new(*argv, publish: options[:publish]).execute
81
+ end
73
82
  end
74
83
 
75
84
  desc "provision", "Execute commands within provision section"
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dip
4
+ class CLI
5
+ class Base < Thor
6
+ def self.exit_on_failure?
7
+ true
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require_relative "./base"
4
5
  require_relative "../commands/console"
5
6
 
6
7
  module Dip
7
8
  class CLI
8
- class Console < Thor
9
+ class Console < Base
9
10
  desc "start", "Integrate Dip into current shell"
10
11
  method_option :help, aliases: '-h', type: :boolean,
11
12
  desc: 'Display usage information'
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require_relative "./base"
4
5
  require_relative "../commands/dns"
5
6
 
6
7
  module Dip
7
8
  class CLI
8
9
  # See more https://github.com/aacebedo/dnsdock
9
- class DNS < Thor
10
+ class DNS < Base
10
11
  desc "up", "Run dnsdock container"
11
12
  method_option :help, aliases: '-h', type: :boolean,
12
13
  desc: 'Display usage information'
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require_relative "./base"
4
5
  require_relative "../commands/nginx"
5
6
 
6
7
  module Dip
7
8
  class CLI
8
9
  # See more https://github.com/bibendi/nginx-proxy
9
- class Nginx < Thor
10
+ class Nginx < Base
10
11
  desc "up", "Run nginx container"
11
12
  method_option :help, aliases: '-h', type: :boolean,
12
13
  desc: 'Display usage information'
@@ -16,12 +17,13 @@ module Dip
16
17
  desc: 'Path to docker socket'
17
18
  method_option :net, aliases: '-t', type: :string, default: "frontend",
18
19
  desc: 'Container network name'
19
- method_option :publish, aliases: '-p', type: :string, default: "80:80",
20
- desc: 'Container port'
20
+ method_option :publish, aliases: '-p', type: :array, default: ["80:80"],
21
+ desc: 'Container port(s). For more than one port, separate them by a space'
21
22
  method_option :image, aliases: '-i', type: :string, default: "bibendi/nginx-proxy:latest",
22
23
  desc: 'Docker image name'
23
24
  method_option :domain, aliases: '-d', type: :string, default: "docker",
24
25
  desc: 'Top level domain'
26
+ method_option :certs, aliases: '-c', type: :string, desc: 'Path to ssl certificates'
25
27
  def up
26
28
  if options[:help]
27
29
  invoke :help, ['up']
@@ -32,7 +34,8 @@ module Dip
32
34
  net: options.fetch(:net),
33
35
  publish: options.fetch(:publish),
34
36
  image: options.fetch(:image),
35
- domain: options.fetch(:domain)
37
+ domain: options.fetch(:domain),
38
+ certs: options[:certs]
36
39
  ).execute
37
40
  end
38
41
  end
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require_relative "./base"
4
5
  require_relative "../commands/ssh"
5
6
 
6
7
  module Dip
7
8
  class CLI
8
- class SSH < Thor
9
+ class SSH < Base
9
10
  desc "up", "Run ssh-agent container"
10
11
  method_option :help, aliases: '-h', type: :boolean,
11
12
  desc: 'Display usage information'
@@ -15,6 +16,8 @@ module Dip
15
16
  desc: 'Mounted docker volume'
16
17
  method_option :interactive, aliases: '-t', type: :boolean, default: true,
17
18
  desc: 'Run in interactive mode'
19
+ method_option :user, aliases: '-u', type: :string,
20
+ desc: 'UID for ssh-agent container'
18
21
  # Backward compatibility
19
22
  method_option :nonteractive, aliases: '-T', type: :boolean,
20
23
  desc: 'Run in noninteractive mode'
@@ -25,7 +28,8 @@ module Dip
25
28
  Dip::Commands::SSH::Up.new(
26
29
  key: options.fetch(:key),
27
30
  volume: options.fetch(:volume),
28
- interactive: options.nonteractive? ? false : options.interactive?
31
+ interactive: options.nonteractive? ? false : options.interactive?,
32
+ user: options.user
29
33
  ).execute
30
34
  end
31
35
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  require_relative '../command'
4
6
  require_relative 'dns'
5
7
 
@@ -18,7 +20,7 @@ module Dip
18
20
  def execute
19
21
  Dip.env["DIP_DNS"] ||= find_dns
20
22
 
21
- compose_argv = Array(find_files) + Array(find_project_name) + argv
23
+ compose_argv = Array(find_files) + Array(cli_options) + argv
22
24
 
23
25
  shell("docker-compose", compose_argv)
24
26
  end
@@ -29,23 +31,26 @@ module Dip
29
31
  return unless (files = config[:files])
30
32
 
31
33
  if files.is_a?(Array)
32
- files.each_with_object([]) do |file_name, memo|
33
- file_name = ::Dip.env.interpolate(file_name)
34
- next unless File.exist?(file_name)
34
+ files.each_with_object([]) do |file_path, memo|
35
+ file_path = ::Dip.env.interpolate(file_path)
36
+ file_path = Pathname.new(file_path)
37
+ file_path = Dip.config.file_path.parent.join(file_path).expand_path if file_path.relative?
38
+ next unless file_path.exist?
35
39
 
36
40
  memo << "--file"
37
- memo << file_name
41
+ memo << file_path.to_s
38
42
  end
39
43
  end
40
44
  end
41
45
 
42
- def find_project_name
43
- return unless (project_name = config[:project_name])
46
+ def cli_options
47
+ %i[project_name project_directory].flat_map do |name|
48
+ next unless (value = config[name])
49
+ next unless value.is_a?(String)
44
50
 
45
- if project_name.is_a?(String)
46
- project_name = ::Dip.env.interpolate(project_name)
47
- ["--project-name", project_name]
48
- end
51
+ value = ::Dip.env.interpolate(value)
52
+ ["--#{name.to_s.gsub('_', '-')}", value]
53
+ end.compact
49
54
  end
50
55
 
51
56
  def find_dns
@@ -86,7 +86,7 @@ module Dip
86
86
  end
87
87
 
88
88
  def execute
89
- if Dip::Config.exist?
89
+ if Dip.config.exist?
90
90
  add_aliases(*Dip.config.interaction.keys) if Dip.config.interaction
91
91
  add_aliases("compose", "up", "stop", "down", "provision")
92
92
  end
@@ -7,13 +7,14 @@ module Dip
7
7
  module Commands
8
8
  module Nginx
9
9
  class Up < Dip::Command
10
- def initialize(name:, socket:, net:, publish:, image:, domain:)
10
+ def initialize(name:, socket:, net:, publish:, image:, domain:, certs:)
11
11
  @name = name
12
12
  @socket = socket
13
13
  @net = net
14
14
  @publish = publish
15
15
  @image = image
16
16
  @domain = domain
17
+ @certs = certs
17
18
  end
18
19
 
19
20
  def execute
@@ -26,8 +27,9 @@ module Dip
26
27
  def container_args
27
28
  result = %w(--detach)
28
29
  result << "--volume #{@socket}:/tmp/docker.sock:ro"
30
+ result << "--volume #{@certs}:/etc/nginx/certs" unless @certs.to_s.empty?
29
31
  result << "--restart always"
30
- result << "--publish #{@publish}"
32
+ result << Array(@publish).map { |p| "--publish #{p}" }.join(' ')
31
33
  result << "--net #{@net}"
32
34
  result << "--name #{@name}"
33
35
  result << "--label com.dnsdock.alias=#{@domain}"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'shellwords'
4
+ require_relative '../../../lib/dip/run_vars'
4
5
  require_relative '../command'
5
6
  require_relative '../interaction_tree'
6
7
  require_relative 'compose'
@@ -8,7 +9,9 @@ require_relative 'compose'
8
9
  module Dip
9
10
  module Commands
10
11
  class Run < Dip::Command
11
- def initialize(cmd, *argv)
12
+ def initialize(cmd, *argv, publish: nil)
13
+ @publish = publish
14
+
12
15
  @command, @argv = InteractionTree.
13
16
  new(Dip.config.interaction).
14
17
  find(cmd, *argv)&.
@@ -28,13 +31,14 @@ module Dip
28
31
 
29
32
  private
30
33
 
31
- attr_reader :command, :argv
34
+ attr_reader :command, :argv, :publish
32
35
 
33
36
  def compose_arguments
34
37
  compose_argv = command[:compose][:run_options].dup
35
38
 
36
39
  if command[:compose][:method] == "run"
37
40
  compose_argv.concat(run_vars)
41
+ compose_argv.concat(published_ports)
38
42
  compose_argv << "--rm"
39
43
  end
40
44
 
@@ -55,6 +59,14 @@ module Dip
55
59
 
56
60
  run_vars.map { |k, v| ["-e", "#{k}=#{v}"] }.flatten
57
61
  end
62
+
63
+ def published_ports
64
+ if publish.respond_to?(:each)
65
+ publish.map { |p| "--publish=#{p}" }
66
+ else
67
+ []
68
+ end
69
+ end
58
70
  end
59
71
  end
60
72
  end
@@ -7,16 +7,20 @@ module Dip
7
7
  module Commands
8
8
  module SSH
9
9
  class Up < Dip::Command
10
- def initialize(key:, volume:, interactive:)
10
+ def initialize(key:, volume:, interactive:, user: nil)
11
11
  @key = key
12
12
  @volume = volume
13
13
  @interactive = interactive
14
+ @user = user
14
15
  end
15
16
 
16
17
  def execute
17
18
  subshell("docker", "volume create --name ssh_data".shellsplit, out: File::NULL, err: File::NULL)
18
19
 
19
- subshell("docker", "run --detach --volume ssh_data:/ssh --name=ssh-agent whilp/ssh-agent".shellsplit)
20
+ subshell(
21
+ "docker",
22
+ "run #{user_args} --detach --volume ssh_data:/ssh --name=ssh-agent whilp/ssh-agent".shellsplit
23
+ )
20
24
 
21
25
  key = Dip.env.interpolate(@key)
22
26
  subshell("docker", "run #{container_args} whilp/ssh-agent ssh-add #{key}".shellsplit)
@@ -24,6 +28,10 @@ module Dip
24
28
 
25
29
  private
26
30
 
31
+ def user_args
32
+ "-u #{@user}" if @user
33
+ end
34
+
27
35
  def container_args
28
36
  result = %w(--rm)
29
37
  volume = Dip.env.interpolate(@volume)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "yaml"
4
4
  require "erb"
5
+ require "pathname"
5
6
 
6
7
  require "dip/version"
7
8
  require "dip/ext/hash"
@@ -12,47 +13,98 @@ module Dip
12
13
  class Config
13
14
  DEFAULT_PATH = "dip.yml"
14
15
 
15
- class << self
16
- def exist?
17
- File.exist?(path)
16
+ CONFIG_DEFAULTS = {
17
+ environment: {},
18
+ compose: {},
19
+ interation: {},
20
+ provision: []
21
+ }.freeze
22
+
23
+ ConfigKeyMissingError = Class.new(ArgumentError)
24
+
25
+ class ConfigFinder
26
+ attr_reader :file_path
27
+
28
+ def initialize(work_dir, override: false)
29
+ @override = override
30
+
31
+ @file_path = if ENV["DIP_FILE"]
32
+ Pathname.new(prepared_name(ENV["DIP_FILE"]))
33
+ else
34
+ find(Pathname.new(work_dir))
35
+ end
18
36
  end
19
37
 
20
- def path
21
- ENV["DIP_FILE"] || File.join(Dir.pwd, DEFAULT_PATH)
38
+ def exist?
39
+ file_path&.exist?
22
40
  end
23
41
 
24
- def override_path
42
+ private
43
+
44
+ attr_reader :override
45
+
46
+ def prepared_name(path)
47
+ return path unless override
48
+
25
49
  path.gsub(/\.yml$/, ".override.yml")
26
50
  end
27
51
 
52
+ def find(path)
53
+ file = path.join(prepared_name(DEFAULT_PATH))
54
+ return file if file.exist?
55
+ return if path.root?
56
+
57
+ find(path.parent)
58
+ end
59
+ end
60
+
61
+ class << self
28
62
  def load_yaml(file_path = path)
29
63
  return {} unless File.exist?(file_path)
30
64
 
31
65
  YAML.safe_load(
32
66
  ERB.new(File.read(file_path)).result,
33
67
  [], [], true
34
- ).deep_symbolize_keys!
68
+ )&.deep_symbolize_keys! || {}
35
69
  end
36
70
  end
37
71
 
38
- %i[environment compose interaction provision].each do |key|
39
- define_method(key) do
40
- config[key]
41
- end
72
+ def initialize(work_dir = Dir.pwd)
73
+ @work_dir = work_dir
74
+ end
75
+
76
+ def file_path
77
+ finder.file_path
78
+ end
79
+
80
+ def exist?
81
+ finder.exist?
42
82
  end
43
83
 
44
84
  def to_h
45
85
  config
46
86
  end
47
87
 
88
+ %i[environment compose interaction provision].each do |key|
89
+ define_method(key) do
90
+ config[key] || (raise config_missing_error(key))
91
+ end
92
+ end
93
+
48
94
  private
49
95
 
96
+ attr_reader :work_dir
97
+
98
+ def finder
99
+ @finder ||= ConfigFinder.new(work_dir)
100
+ end
101
+
50
102
  def config
51
103
  return @config if @config
52
104
 
53
- raise ArgumentError, "Dip config not found at path '#{self.class.path}'" unless self.class.exist?
105
+ raise Dip::Error, "Could not find dip.yml config" unless finder.exist?
54
106
 
55
- config = self.class.load_yaml
107
+ config = self.class.load_yaml(finder.file_path)
56
108
 
57
109
  unless Gem::Version.new(Dip::VERSION) >= Gem::Version.new(config.fetch(:version))
58
110
  raise VersionMismatchError, "Your dip version is `#{Dip::VERSION}`, " \
@@ -60,9 +112,15 @@ module Dip
60
112
  "Please upgrade your dip!"
61
113
  end
62
114
 
63
- config.deep_merge!(self.class.load_yaml(self.class.override_path))
115
+ override_finder = ConfigFinder.new(work_dir, override: true)
116
+ config.deep_merge!(self.class.load_yaml(override_finder.file_path)) if override_finder.exist?
117
+
118
+ @config = CONFIG_DEFAULTS.merge(config)
119
+ end
64
120
 
65
- @config = config
121
+ def config_missing_error(config_key)
122
+ msg = 'config for %<key>s is not defined in %<path>s' % {key: config_key, path: finder.file_path}
123
+ ConfigKeyMissingError.new(msg)
66
124
  end
67
125
  end
68
126
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pathname"
4
+
3
5
  module Dip
4
6
  class Environment
5
7
  VAR_REGEX = /\$[\{]?(?<var_name>[a-zA-Z_][a-zA-Z0-9_]*)[\}]?/.freeze
6
- SPECIAL_VARS = {"DIP_OS" => :find_dip_os}.freeze
8
+ SPECIAL_VARS = %i[os work_dir_rel_path].freeze
7
9
 
8
10
  attr_reader :vars
9
11
 
@@ -24,6 +26,10 @@ module Dip
24
26
  vars.fetch(name) { ENV[name] }
25
27
  end
26
28
 
29
+ def fetch(name)
30
+ vars.fetch(name) { ENV.fetch(name) { yield } }
31
+ end
32
+
27
33
  def []=(key, value)
28
34
  @vars[key] = value
29
35
  end
@@ -32,8 +38,8 @@ module Dip
32
38
  value.gsub(VAR_REGEX) do
33
39
  var_name = Regexp.last_match[:var_name]
34
40
 
35
- if SPECIAL_VARS.key?(var_name)
36
- self[var_name] || send(SPECIAL_VARS[var_name])
41
+ if special_vars.key?(var_name)
42
+ fetch(var_name) { send(special_vars[var_name]) }
37
43
  else
38
44
  self[var_name]
39
45
  end
@@ -44,8 +50,18 @@ module Dip
44
50
 
45
51
  private
46
52
 
47
- def find_dip_os
53
+ def special_vars
54
+ @special_vars ||= SPECIAL_VARS.each_with_object({}) do |key, memo|
55
+ memo["DIP_#{key.to_s.upcase}"] = "find_#{key}"
56
+ end
57
+ end
58
+
59
+ def find_os
48
60
  @dip_os ||= Gem::Platform.local.os
49
61
  end
62
+
63
+ def find_work_dir_rel_path
64
+ @find_work_dir_rel_path ||= Pathname.getwd.relative_path_from(Dip.config.file_path.parent).to_s
65
+ end
50
66
  end
51
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dip
4
- VERSION = "4.1.0"
4
+ VERSION = "6.1.0"
5
5
  end
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dip
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 6.1.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-10-23 00:00:00.000000000 Z
11
+ date: 2020-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0.20'
20
+ - - "<"
18
21
  - !ruby/object:Gem::Version
19
- version: 0.20.0
22
+ version: '1.1'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0.20'
30
+ - - "<"
25
31
  - !ruby/object:Gem::Version
26
- version: 0.20.0
32
+ version: '1.1'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: bundler
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +92,14 @@ dependencies:
86
92
  requirements:
87
93
  - - "~>"
88
94
  - !ruby/object:Gem::Version
89
- version: '0.59'
95
+ version: '0.81'
90
96
  type: :development
91
97
  prerelease: false
92
98
  version_requirements: !ruby/object:Gem::Requirement
93
99
  requirements:
94
100
  - - "~>"
95
101
  - !ruby/object:Gem::Version
96
- version: '0.59'
102
+ version: '0.81'
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: simplecov
99
105
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +142,7 @@ files:
136
142
  - exe/dip
137
143
  - lib/dip.rb
138
144
  - lib/dip/cli.rb
145
+ - lib/dip/cli/base.rb
139
146
  - lib/dip/cli/console.rb
140
147
  - lib/dip/cli/dns.rb
141
148
  - lib/dip/cli/nginx.rb
@@ -169,15 +176,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
169
176
  requirements:
170
177
  - - ">="
171
178
  - !ruby/object:Gem::Version
172
- version: '2.3'
179
+ version: '2.4'
173
180
  required_rubygems_version: !ruby/object:Gem::Requirement
174
181
  requirements:
175
182
  - - ">="
176
183
  - !ruby/object:Gem::Version
177
184
  version: '0'
178
185
  requirements: []
179
- rubyforge_project:
180
- rubygems_version: 2.7.7
186
+ rubygems_version: 3.0.8
181
187
  signing_key:
182
188
  specification_version: 4
183
189
  summary: Ruby gem CLI tool for better interacting docker-compose files.