dip 4.2.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../command'
4
- require_relative '../interaction_tree'
3
+ require_relative "../command"
4
+ require_relative "../interaction_tree"
5
5
 
6
6
  module Dip
7
7
  module Commands
@@ -12,7 +12,7 @@ module Dip
12
12
  longest_name = tree.keys.map(&:size).max
13
13
 
14
14
  tree.each do |name, command|
15
- puts "#{name.ljust(longest_name)} ##{command[:description] ? ' ' + command[:description] : ''}"
15
+ puts "#{name.ljust(longest_name)} ##{command[:description] ? " #{command[:description]}" : ""}"
16
16
  end
17
17
  end
18
18
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shellwords"
4
- require_relative '../command'
4
+ require_relative "../command"
5
5
 
6
6
  module Dip
7
7
  module Commands
@@ -25,15 +25,15 @@ module Dip
25
25
  private
26
26
 
27
27
  def container_args
28
- result = %w(--detach)
28
+ result = %w[--detach]
29
29
  result << "--volume #{@socket}:/tmp/docker.sock:ro"
30
30
  result << "--volume #{@certs}:/etc/nginx/certs" unless @certs.to_s.empty?
31
31
  result << "--restart always"
32
- result << Array(@publish).map { |p| "--publish #{p}" }.join(' ')
32
+ result << Array(@publish).map { |p| "--publish #{p}" }.join(" ")
33
33
  result << "--net #{@net}"
34
34
  result << "--name #{@name}"
35
35
  result << "--label com.dnsdock.alias=#{@domain}"
36
- result.join(' ')
36
+ result.join(" ")
37
37
  end
38
38
  end
39
39
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../command'
3
+ require_relative "../command"
4
4
 
5
5
  module Dip
6
6
  module Commands
@@ -1,50 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'shellwords'
4
- require_relative '../command'
5
- require_relative '../interaction_tree'
6
- require_relative 'compose'
3
+ require "shellwords"
4
+ require_relative "../../../lib/dip/run_vars"
5
+ require_relative "../command"
6
+ require_relative "../interaction_tree"
7
+ require_relative "compose"
7
8
 
8
9
  module Dip
9
10
  module Commands
10
11
  class Run < Dip::Command
11
- def initialize(cmd, *argv)
12
- @command, @argv = InteractionTree.
13
- new(Dip.config.interaction).
14
- find(cmd, *argv)&.
15
- values_at(:command, :argv)
12
+ def initialize(cmd, *argv, publish: nil)
13
+ @publish = publish
16
14
 
17
- raise Dip::Error, "Command `#{[cmd, *argv].join(' ')}` not recognized!" unless command
15
+ @command, @argv = InteractionTree
16
+ .new(Dip.config.interaction)
17
+ .find(cmd, *argv)&.values_at(:command, :argv)
18
+
19
+ raise Dip::Error, "Command `#{[cmd, *argv].join(" ")}` not recognized!" unless command
18
20
 
19
21
  Dip.env.merge(command[:environment])
20
22
  end
21
23
 
22
24
  def execute
23
- Dip::Commands::Compose.new(
24
- command[:compose][:method],
25
- *compose_arguments
26
- ).execute
25
+ if command[:service].nil?
26
+ shell(command[:command], get_args)
27
+ else
28
+ Dip::Commands::Compose.new(
29
+ command[:compose][:method],
30
+ *compose_arguments
31
+ ).execute
32
+ end
27
33
  end
28
34
 
29
35
  private
30
36
 
31
- attr_reader :command, :argv
37
+ attr_reader :command, :argv, :publish
32
38
 
33
39
  def compose_arguments
34
40
  compose_argv = command[:compose][:run_options].dup
35
41
 
36
42
  if command[:compose][:method] == "run"
37
43
  compose_argv.concat(run_vars)
44
+ compose_argv.concat(published_ports)
38
45
  compose_argv << "--rm"
39
46
  end
40
47
 
41
48
  compose_argv << command.fetch(:service)
42
49
 
43
- unless (cmd = command[:command].to_s).empty?
44
- compose_argv.concat(cmd.shellsplit)
50
+ unless (cmd = command[:command]).empty?
51
+ compose_argv << cmd
45
52
  end
46
53
 
47
- compose_argv.concat(argv.any? ? argv : command[:default_args])
54
+ compose_argv.concat(get_args)
48
55
 
49
56
  compose_argv
50
57
  end
@@ -53,7 +60,25 @@ module Dip
53
60
  run_vars = Dip::RunVars.env
54
61
  return [] unless run_vars
55
62
 
56
- run_vars.map { |k, v| ["-e", "#{k}=#{v}"] }.flatten
63
+ run_vars.map { |k, v| ["-e", "#{k}=#{Shellwords.escape(v)}"] }.flatten
64
+ end
65
+
66
+ def published_ports
67
+ if publish.respond_to?(:each)
68
+ publish.map { |p| "--publish=#{p}" }
69
+ else
70
+ []
71
+ end
72
+ end
73
+
74
+ def get_args
75
+ if argv.any?
76
+ argv
77
+ elsif !(default_args = command[:default_args]).empty?
78
+ Array(default_args)
79
+ else
80
+ []
81
+ end
57
82
  end
58
83
  end
59
84
  end
@@ -1,22 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shellwords"
4
- require_relative '../command'
4
+ require_relative "../command"
5
5
 
6
6
  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,13 +28,17 @@ 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
- result = %w(--rm)
36
+ result = %w[--rm]
29
37
  volume = Dip.env.interpolate(@volume)
30
38
  result << "--volume ssh_data:/ssh"
31
39
  result << "--volume #{volume}:#{volume}"
32
40
  result << "--interactive --tty" if @interactive
33
- result.join(' ')
41
+ result.join(" ")
34
42
  end
35
43
  end
36
44
 
data/lib/dip/config.rb CHANGED
@@ -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
- VAR_REGEX = /\$[\{]?(?<var_name>[a-zA-Z_][a-zA-Z0-9_]*)[\}]?/.freeze
6
- SPECIAL_VARS = {"DIP_OS" => :find_dip_os}.freeze
7
+ VAR_REGEX = /\$\{?(?<var_name>[a-zA-Z_][a-zA-Z0-9_]*)\}?/.freeze
8
+ SPECIAL_VARS = %i[os work_dir_rel_path].freeze
7
9
 
8
10
  attr_reader :vars
9
11
 
@@ -24,28 +26,42 @@ module Dip
24
26
  vars.fetch(name) { ENV[name] }
25
27
  end
26
28
 
29
+ def fetch(name, &block)
30
+ vars.fetch(name) { ENV.fetch(name, &block) }
31
+ end
32
+
27
33
  def []=(key, value)
28
34
  @vars[key] = value
29
35
  end
30
36
 
31
37
  def interpolate(value)
32
- value.gsub(VAR_REGEX) do
38
+ value.gsub(VAR_REGEX) do |match|
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
- self[var_name]
44
+ fetch(var_name) { match }
39
45
  end
40
46
  end
41
47
  end
42
48
 
43
- alias replace interpolate
49
+ alias_method :replace, :interpolate
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
data/lib/dip/ext/hash.rb CHANGED
@@ -24,7 +24,7 @@ module ActiveSupportHashHelpers
24
24
  merge!(other_hash) do |key, this_val, other_val|
25
25
  if this_val.is_a?(Hash) && other_val.is_a?(Hash)
26
26
  this_val.deep_merge(other_val, &block)
27
- elsif block_given?
27
+ elsif block
28
28
  block.call(key, this_val, other_val)
29
29
  else
30
30
  other_val
@@ -58,9 +58,9 @@ module Dip
58
58
  def build_command(entry)
59
59
  {
60
60
  description: entry[:description],
61
- service: entry.fetch(:service),
62
- command: entry[:command],
63
- default_args: prepare_default_args(entry[:default_args]),
61
+ service: entry[:service],
62
+ command: entry[:command].to_s.strip,
63
+ default_args: entry[:default_args].to_s.strip,
64
64
  environment: entry[:environment] || {},
65
65
  compose: {
66
66
  method: entry.dig(:compose, :method) || entry[:compose_method] || "run",
@@ -76,19 +76,6 @@ module Dip
76
76
  entry[:description] ||= nil
77
77
  end
78
78
 
79
- def prepare_default_args(args)
80
- return [] if args.nil?
81
-
82
- case args
83
- when Array
84
- args
85
- when String
86
- args.shellsplit
87
- else
88
- raise ArgumentError, "Unknown type for default_args: #{args.inspect}"
89
- end
90
- end
91
-
92
79
  def compose_run_options(value)
93
80
  return [] unless value
94
81