dip 4.2.0 → 7.0.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/README.md +85 -42
- data/exe/dip +8 -8
- data/lib/dip.rb +2 -2
- data/lib/dip/cli.rb +47 -33
- data/lib/dip/cli/base.rb +11 -0
- data/lib/dip/cli/console.rb +9 -8
- data/lib/dip/cli/dns.rb +33 -32
- data/lib/dip/cli/nginx.rb +27 -26
- data/lib/dip/cli/ssh.rb +27 -23
- data/lib/dip/command.rb +8 -7
- data/lib/dip/commands/compose.rb +21 -16
- data/lib/dip/commands/console.rb +6 -6
- data/lib/dip/commands/dns.rb +7 -7
- data/lib/dip/commands/list.rb +3 -3
- data/lib/dip/commands/nginx.rb +4 -4
- data/lib/dip/commands/provision.rb +1 -1
- data/lib/dip/commands/run.rb +44 -19
- data/lib/dip/commands/ssh.rb +13 -5
- data/lib/dip/config.rb +73 -15
- data/lib/dip/environment.rb +24 -8
- data/lib/dip/ext/hash.rb +1 -1
- data/lib/dip/interaction_tree.rb +3 -16
- data/lib/dip/version.rb +1 -1
- metadata +46 -12
data/lib/dip/commands/list.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
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] ?
|
15
|
+
puts "#{name.ljust(longest_name)} ##{command[:description] ? " #{command[:description]}" : ""}"
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
data/lib/dip/commands/nginx.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "shellwords"
|
4
|
-
require_relative
|
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
|
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
|
|
data/lib/dip/commands/run.rb
CHANGED
@@ -1,50 +1,57 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
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
|
-
@
|
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
|
-
|
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
|
-
|
24
|
-
command[:
|
25
|
-
|
26
|
-
|
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]
|
44
|
-
compose_argv
|
50
|
+
unless (cmd = command[:command]).empty?
|
51
|
+
compose_argv << cmd
|
45
52
|
end
|
46
53
|
|
47
|
-
compose_argv.concat(
|
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
|
data/lib/dip/commands/ssh.rb
CHANGED
@@ -1,22 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "shellwords"
|
4
|
-
require_relative
|
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(
|
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
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
21
|
-
|
38
|
+
def exist?
|
39
|
+
file_path&.exist?
|
22
40
|
end
|
23
41
|
|
24
|
-
|
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
|
-
)
|
68
|
+
)&.deep_symbolize_keys! || {}
|
35
69
|
end
|
36
70
|
end
|
37
71
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
data/lib/dip/environment.rb
CHANGED
@@ -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 =
|
6
|
-
SPECIAL_VARS =
|
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
|
36
|
-
|
41
|
+
if special_vars.key?(var_name)
|
42
|
+
fetch(var_name) { send(special_vars[var_name]) }
|
37
43
|
else
|
38
|
-
|
44
|
+
fetch(var_name) { match }
|
39
45
|
end
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
43
|
-
|
49
|
+
alias_method :replace, :interpolate
|
44
50
|
|
45
51
|
private
|
46
52
|
|
47
|
-
def
|
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
|
27
|
+
elsif block
|
28
28
|
block.call(key, this_val, other_val)
|
29
29
|
else
|
30
30
|
other_val
|
data/lib/dip/interaction_tree.rb
CHANGED
@@ -58,9 +58,9 @@ module Dip
|
|
58
58
|
def build_command(entry)
|
59
59
|
{
|
60
60
|
description: entry[:description],
|
61
|
-
service: entry
|
62
|
-
command: entry[:command],
|
63
|
-
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
|
|