dip 6.0.0 → 7.1.1

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.
data/lib/dip/cli/ssh.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
3
+ require "thor"
4
4
  require_relative "./base"
5
5
  require_relative "../commands/ssh"
6
6
 
@@ -8,25 +8,28 @@ module Dip
8
8
  class CLI
9
9
  class SSH < Base
10
10
  desc "up", "Run ssh-agent container"
11
- method_option :help, aliases: '-h', type: :boolean,
12
- desc: 'Display usage information'
13
- method_option :key, aliases: '-k', type: :string, default: "$HOME/.ssh/id_rsa",
14
- desc: 'Path to ssh key'
15
- method_option :volume, aliases: '-v', type: :string, default: "$HOME",
16
- desc: 'Mounted docker volume'
17
- method_option :interactive, aliases: '-t', type: :boolean, default: true,
18
- desc: 'Run in interactive mode'
11
+ method_option :help, aliases: "-h", type: :boolean,
12
+ desc: "Display usage information"
13
+ method_option :key, aliases: "-k", type: :string, default: "$HOME/.ssh/id_rsa",
14
+ desc: "Path to ssh key"
15
+ method_option :volume, aliases: "-v", type: :string, default: "$HOME",
16
+ desc: "Mounted docker volume"
17
+ method_option :interactive, aliases: "-t", type: :boolean, default: true,
18
+ desc: "Run in interactive mode"
19
+ method_option :user, aliases: "-u", type: :string,
20
+ desc: "UID for ssh-agent container"
19
21
  # Backward compatibility
20
- method_option :nonteractive, aliases: '-T', type: :boolean,
21
- desc: 'Run in noninteractive mode'
22
+ method_option :nonteractive, aliases: "-T", type: :boolean,
23
+ desc: "Run in noninteractive mode"
22
24
  def up
23
25
  if options[:help]
24
- invoke :help, ['up']
26
+ invoke :help, ["up"]
25
27
  else
26
28
  Dip::Commands::SSH::Up.new(
27
29
  key: options.fetch(:key),
28
30
  volume: options.fetch(:volume),
29
- interactive: options.nonteractive? ? false : options.interactive?
31
+ interactive: options.nonteractive? ? false : options.interactive?,
32
+ user: options.user
30
33
  ).execute
31
34
  end
32
35
  end
@@ -34,22 +37,22 @@ module Dip
34
37
  map add: :up
35
38
 
36
39
  desc "down", "Stop ssh-agent container"
37
- method_option :help, aliases: '-h', type: :boolean,
38
- desc: 'Display usage information'
40
+ method_option :help, aliases: "-h", type: :boolean,
41
+ desc: "Display usage information"
39
42
  def down
40
43
  if options[:help]
41
- invoke :help, ['down']
44
+ invoke :help, ["down"]
42
45
  else
43
46
  Dip::Commands::SSH::Down.new.execute
44
47
  end
45
48
  end
46
49
 
47
50
  desc "restart", "Stop and start ssh-agent container"
48
- method_option :help, aliases: '-h', type: :boolean,
49
- desc: 'Display usage information'
51
+ method_option :help, aliases: "-h", type: :boolean,
52
+ desc: "Display usage information"
50
53
  def restart(*args)
51
54
  if options[:help]
52
- invoke :help, ['restart']
55
+ invoke :help, ["restart"]
53
56
  else
54
57
  Dip::CLI::SSH.start(["down"] + args)
55
58
  sleep 1
@@ -58,11 +61,11 @@ module Dip
58
61
  end
59
62
 
60
63
  desc "status", "Show status of ssh-agent container"
61
- method_option :help, aliases: '-h', type: :boolean,
62
- desc: 'Display usage information'
64
+ method_option :help, aliases: "-h", type: :boolean,
65
+ desc: "Display usage information"
63
66
  def status
64
67
  if options[:help]
65
- invoke :help, ['status']
68
+ invoke :help, ["status"]
66
69
  else
67
70
  Dip::Commands::SSH::Status.new.execute
68
71
  end
data/lib/dip/command.rb CHANGED
@@ -6,35 +6,46 @@ module Dip
6
6
  class Command
7
7
  extend Forwardable
8
8
 
9
- def_delegators self, :shell, :subshell
10
-
11
- class ExecRunner
12
- def self.call(cmd, argv, env: {}, **options)
13
- ::Process.exec(env, cmd, *argv, options)
9
+ def_delegators self, :exec_program, :exec_subprocess
10
+
11
+ class ProgramRunner
12
+ def self.call(cmdline, env: {}, **options)
13
+ if cmdline.is_a?(Array)
14
+ ::Kernel.exec(env, cmdline[0], *cmdline[1..-1], **options)
15
+ else
16
+ ::Kernel.exec(env, cmdline, **options)
17
+ end
14
18
  end
15
19
  end
16
20
 
17
- class SubshellRunner
18
- def self.call(cmd, argv, env: {}, panic: true, **options)
19
- return if ::Kernel.system(env, cmd, *argv, options)
20
- raise Dip::Error, "Command '#{([cmd] + argv).join(' ')}' executed with error." if panic
21
+ class SubprocessRunner
22
+ def self.call(cmdline, env: {}, panic: true, **options)
23
+ return if ::Kernel.system(env, cmdline, **options)
24
+ raise Dip::Error, "Command '#{cmdline}' executed with error." if panic
21
25
  end
22
26
  end
23
27
 
24
28
  class << self
25
- def shell(cmd, argv = [], subshell: false, **options)
29
+ def exec_program(*args, **kwargs)
30
+ run(ProgramRunner, *args, **kwargs)
31
+ end
32
+
33
+ def exec_subprocess(*args, **kwargs)
34
+ run(SubprocessRunner, *args, **kwargs)
35
+ end
36
+
37
+ private
38
+
39
+ def run(runner, cmd, argv = [], shell: true, **options)
26
40
  cmd = Dip.env.interpolate(cmd)
41
+ argv = [argv] if argv.is_a?(String)
27
42
  argv = argv.map { |arg| Dip.env.interpolate(arg) }
43
+ cmdline = [cmd, *argv].compact
44
+ cmdline = cmdline.join(" ").strip if shell
28
45
 
29
- puts [Dip.env.vars, cmd, argv].inspect if Dip.debug?
30
-
31
- runner = subshell ? SubshellRunner : ExecRunner
32
- runner.call(cmd, argv, env: Dip.env.vars, **options)
33
- end
46
+ puts [Dip.env.vars, cmdline].inspect if Dip.debug?
34
47
 
35
- def subshell(*args, **kwargs)
36
- kwargs[:subshell] = true
37
- shell(*args, **kwargs)
48
+ runner.call(cmdline, env: Dip.env.vars, **options)
38
49
  end
39
50
  end
40
51
  end
@@ -1,19 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
3
+ require "pathname"
4
4
 
5
- require_relative '../command'
6
- require_relative 'dns'
5
+ require_relative "../command"
6
+ require_relative "dns"
7
7
 
8
8
  module Dip
9
9
  module Commands
10
10
  class Compose < Dip::Command
11
11
  DOCKER_EMBEDDED_DNS = "127.0.0.11"
12
12
 
13
- attr_reader :argv, :config
13
+ attr_reader :argv, :config, :shell
14
14
 
15
- def initialize(*argv)
15
+ def initialize(*argv, shell: true)
16
16
  @argv = argv
17
+ @shell = shell
17
18
  @config = ::Dip.config.compose || {}
18
19
  end
19
20
 
@@ -22,7 +23,7 @@ module Dip
22
23
 
23
24
  compose_argv = Array(find_files) + Array(cli_options) + argv
24
25
 
25
- shell("docker-compose", compose_argv)
26
+ exec_program("docker-compose", compose_argv, shell: shell)
26
27
  end
27
28
 
28
29
  private
@@ -49,7 +50,7 @@ module Dip
49
50
  next unless value.is_a?(String)
50
51
 
51
52
  value = ::Dip.env.interpolate(value)
52
- ["--#{name.to_s.gsub('_', '-')}", value]
53
+ ["--#{name.to_s.tr("_", "-")}", value]
53
54
  end.compact
54
55
  end
55
56
 
@@ -58,9 +59,9 @@ module Dip
58
59
  net = Dip.env["FRONTEND_NETWORK"] || "frontend"
59
60
 
60
61
  IO.pipe do |r, w|
61
- Dip::Commands::DNS::IP.
62
- new(name: name, net: net).
63
- execute(out: w, err: File::NULL, panic: false)
62
+ Dip::Commands::DNS::IP
63
+ .new(name: name, net: net)
64
+ .execute(out: w, err: File::NULL, panic: false)
64
65
 
65
66
  w.close_write
66
67
  ip = r.readlines[0].to_s.strip
@@ -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
@@ -13,9 +13,9 @@ module Dip
13
13
  private
14
14
 
15
15
  def script
16
- <<-SH.gsub(/^[ ]{12}/, '')
16
+ <<-SH.gsub(/^ {12}/, "")
17
17
  export DIP_SHELL=1
18
- export DIP_EARLY_ENVS=#{ENV.keys.join(',')}
18
+ export DIP_EARLY_ENVS=#{ENV.keys.join(",")}
19
19
  export DIP_PROMPT_TEXT="ⅆ"
20
20
 
21
21
  function dip_clear() {
@@ -88,7 +88,7 @@ module Dip
88
88
  def execute
89
89
  if Dip.config.exist?
90
90
  add_aliases(*Dip.config.interaction.keys) if Dip.config.interaction
91
- add_aliases("compose", "up", "stop", "down", "provision")
91
+ add_aliases("compose", "up", "stop", "down", "provision", "build")
92
92
  end
93
93
 
94
94
  clear_aliases
@@ -107,7 +107,7 @@ module Dip
107
107
 
108
108
  def clear_aliases
109
109
  out << "function dip_clear() { \n" \
110
- "#{aliases.any? ? aliases.map { |a| " unset -f #{a}" }.join("\n") : 'true'} " \
110
+ "#{aliases.any? ? aliases.map { |a| " unset -f #{a}" }.join("\n") : "true"} " \
111
111
  "\n}"
112
112
  end
113
113
  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
@@ -17,20 +17,20 @@ module Dip
17
17
  end
18
18
 
19
19
  def execute
20
- subshell("docker", "network create #{@net}".shellsplit, panic: false, err: File::NULL)
21
- subshell("docker", "run #{container_args} #{@image} --domain=#{@domain}".shellsplit)
20
+ exec_subprocess("docker", "network create #{@net}", panic: false, err: File::NULL)
21
+ exec_subprocess("docker", "run #{container_args} #{@image} --domain=#{@domain}")
22
22
  end
23
23
 
24
24
  private
25
25
 
26
26
  def container_args
27
- result = %w(--detach)
27
+ result = %w[--detach]
28
28
  result << "--volume #{@socket}:/var/run/docker.sock:ro"
29
29
  result << "--restart always"
30
30
  result << "--publish #{@publish}"
31
31
  result << "--net #{@net}"
32
32
  result << "--name #{@name}"
33
- result.join(' ')
33
+ result.join(" ")
34
34
  end
35
35
  end
36
36
 
@@ -40,8 +40,8 @@ module Dip
40
40
  end
41
41
 
42
42
  def execute
43
- subshell("docker", "stop #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
44
- subshell("docker", "rm -v #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
43
+ exec_subprocess("docker", "stop #{@name}", panic: false, out: File::NULL, err: File::NULL)
44
+ exec_subprocess("docker", "rm -v #{@name}", panic: false, out: File::NULL, err: File::NULL)
45
45
  end
46
46
  end
47
47
 
@@ -52,11 +52,11 @@ module Dip
52
52
  end
53
53
 
54
54
  def execute(**options)
55
- subshell("docker",
56
- "inspect " \
57
- "--format '{{ .NetworkSettings.Networks.#{@net}.IPAddress }}' " \
58
- "#{@name}".shellsplit,
59
- **options)
55
+ exec_subprocess(
56
+ "docker",
57
+ "inspect --format '{{ .NetworkSettings.Networks.#{@net}.IPAddress }}' #{@name}",
58
+ **options
59
+ )
60
60
  end
61
61
  end
62
62
  end
@@ -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
@@ -18,22 +18,22 @@ module Dip
18
18
  end
19
19
 
20
20
  def execute
21
- subshell("docker", "network create #{@net}".shellsplit, panic: false, err: File::NULL)
22
- subshell("docker", "run #{container_args} #{@image}".shellsplit)
21
+ exec_subprocess("docker", "network create #{@net}", panic: false, err: File::NULL)
22
+ exec_subprocess("docker", "run #{container_args} #{@image}")
23
23
  end
24
24
 
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
 
@@ -43,8 +43,8 @@ module Dip
43
43
  end
44
44
 
45
45
  def execute
46
- subshell("docker", "stop #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
47
- subshell("docker", "rm -v #{@name}".shellsplit, panic: false, out: File::NULL, err: File::NULL)
46
+ exec_subprocess("docker", "stop #{@name}", panic: false, out: File::NULL, err: File::NULL)
47
+ exec_subprocess("docker", "rm -v #{@name}", panic: false, out: File::NULL, err: File::NULL)
48
48
  end
49
49
  end
50
50
  end
@@ -1,13 +1,13 @@
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
7
7
  class Provision < Dip::Command
8
8
  def execute
9
9
  Dip.config.provision.each do |command|
10
- subshell(command)
10
+ exec_subprocess(command)
11
11
  end
12
12
  end
13
13
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'shellwords'
4
- require_relative '../../../lib/dip/run_vars'
5
- require_relative '../command'
6
- require_relative '../interaction_tree'
7
- 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"
8
8
 
9
9
  module Dip
10
10
  module Commands
@@ -12,21 +12,25 @@ module Dip
12
12
  def initialize(cmd, *argv, publish: nil)
13
13
  @publish = publish
14
14
 
15
- @command, @argv = InteractionTree.
16
- new(Dip.config.interaction).
17
- find(cmd, *argv)&.
18
- values_at(:command, :argv)
15
+ @command, @argv = InteractionTree
16
+ .new(Dip.config.interaction)
17
+ .find(cmd, *argv)&.values_at(:command, :argv)
19
18
 
20
- raise Dip::Error, "Command `#{[cmd, *argv].join(' ')}` not recognized!" unless command
19
+ raise Dip::Error, "Command `#{[cmd, *argv].join(" ")}` not recognized!" unless command
21
20
 
22
21
  Dip.env.merge(command[:environment])
23
22
  end
24
23
 
25
24
  def execute
26
- Dip::Commands::Compose.new(
27
- command[:compose][:method],
28
- *compose_arguments
29
- ).execute
25
+ if command[:service].nil?
26
+ exec_program(command[:command], get_args, shell: command[:shell])
27
+ else
28
+ Dip::Commands::Compose.new(
29
+ command[:compose][:method],
30
+ *compose_arguments,
31
+ shell: command[:shell]
32
+ ).execute
33
+ end
30
34
  end
31
35
 
32
36
  private
@@ -44,11 +48,15 @@ module Dip
44
48
 
45
49
  compose_argv << command.fetch(:service)
46
50
 
47
- unless (cmd = command[:command].to_s).empty?
48
- compose_argv.concat(cmd.shellsplit)
51
+ unless (cmd = command[:command]).empty?
52
+ if command[:shell]
53
+ compose_argv << cmd
54
+ else
55
+ compose_argv.concat(cmd.shellsplit)
56
+ end
49
57
  end
50
58
 
51
- compose_argv.concat(argv.any? ? argv : command[:default_args])
59
+ compose_argv.concat(get_args)
52
60
 
53
61
  compose_argv
54
62
  end
@@ -57,7 +65,7 @@ module Dip
57
65
  run_vars = Dip::RunVars.env
58
66
  return [] unless run_vars
59
67
 
60
- run_vars.map { |k, v| ["-e", "#{k}=#{v}"] }.flatten
68
+ run_vars.map { |k, v| ["-e", "#{k}=#{Shellwords.escape(v)}"] }.flatten
61
69
  end
62
70
 
63
71
  def published_ports
@@ -67,6 +75,20 @@ module Dip
67
75
  []
68
76
  end
69
77
  end
78
+
79
+ def get_args
80
+ if argv.any?
81
+ argv
82
+ elsif !(default_args = command[:default_args]).empty?
83
+ if command[:shell]
84
+ default_args.shellsplit
85
+ else
86
+ Array(default_args)
87
+ end
88
+ else
89
+ []
90
+ end
91
+ end
70
92
  end
71
93
  end
72
94
  end