dip 6.0.0 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
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