Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to run commands in previous mode without shell interpolations #128

Merged
merged 1 commit into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ interaction:
default_args: db_dev
command: psql -h pg -U postgres

setup_key:
description: Copy key
service: app
command: cp `pwd`/config/key.pem /root/keys/
shell: false # you can disable shell interpolations on the host machine and send the command as is

clean_cache:
description: Delete cache files on the host machine
command: rm -rf $(pwd)/tmp/cache/*
Expand Down
35 changes: 22 additions & 13 deletions lib/dip/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,47 @@ module Dip
class Command
extend Forwardable

def_delegators self, :shell, :subshell
def_delegators self, :exec_program, :exec_subprocess

class ExecRunner
class ProgramRunner
def self.call(cmdline, env: {}, **options)
::Process.exec(env, cmdline, options)
if cmdline.is_a?(Array)
::Kernel.exec(env, cmdline[0], *cmdline[1..-1], **options)
else
::Kernel.exec(env, cmdline, **options)
end
end
end

class SubshellRunner
class SubprocessRunner
def self.call(cmdline, env: {}, panic: true, **options)
return if ::Kernel.system(env, cmdline, options)
return if ::Kernel.system(env, cmdline, **options)
raise Dip::Error, "Command '#{cmdline}' executed with error." if panic
end
end

class << self
def shell(cmd, argv = [], subshell: false, **options)
def exec_program(*args, **kwargs)
run(ProgramRunner, *args, **kwargs)
end

def exec_subprocess(*args, **kwargs)
run(SubprocessRunner, *args, **kwargs)
end

private

def run(runner, cmd, argv = [], shell: true, **options)
cmd = Dip.env.interpolate(cmd)
argv = [argv] if argv.is_a?(String)
argv = argv.map { |arg| Dip.env.interpolate(arg) }
cmdline = [cmd, *argv].compact.join(" ").strip
cmdline = [cmd, *argv].compact
cmdline = cmdline.join(" ").strip if shell

puts [Dip.env.vars, cmdline].inspect if Dip.debug?

runner = subshell ? SubshellRunner : ExecRunner
runner.call(cmdline, env: Dip.env.vars, **options)
end

def subshell(*args, **kwargs)
kwargs[:subshell] = true
shell(*args, **kwargs)
end
end
end
end
7 changes: 4 additions & 3 deletions lib/dip/commands/compose.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ module Commands
class Compose < Dip::Command
DOCKER_EMBEDDED_DNS = "127.0.0.11"

attr_reader :argv, :config
attr_reader :argv, :config, :shell

def initialize(*argv)
def initialize(*argv, shell: true)
@argv = argv
@shell = shell
@config = ::Dip.config.compose || {}
end

Expand All @@ -22,7 +23,7 @@ def execute

compose_argv = Array(find_files) + Array(cli_options) + argv

shell("docker-compose", compose_argv)
exec_program("docker-compose", compose_argv, shell: shell)
end

private
Expand Down
10 changes: 5 additions & 5 deletions lib/dip/commands/dns.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def initialize(name:, socket:, net:, publish:, image:, domain:)
end

def execute
subshell("docker", "network create #{@net}", panic: false, err: File::NULL)
subshell("docker", "run #{container_args} #{@image} --domain=#{@domain}")
exec_subprocess("docker", "network create #{@net}", panic: false, err: File::NULL)
exec_subprocess("docker", "run #{container_args} #{@image} --domain=#{@domain}")
end

private
Expand All @@ -40,8 +40,8 @@ def initialize(name:)
end

def execute
subshell("docker", "stop #{@name}", panic: false, out: File::NULL, err: File::NULL)
subshell("docker", "rm -v #{@name}", panic: false, out: File::NULL, err: File::NULL)
exec_subprocess("docker", "stop #{@name}", panic: false, out: File::NULL, err: File::NULL)
exec_subprocess("docker", "rm -v #{@name}", panic: false, out: File::NULL, err: File::NULL)
end
end

Expand All @@ -52,7 +52,7 @@ def initialize(name:, net:)
end

def execute(**options)
subshell(
exec_subprocess(
"docker",
"inspect --format '{{ .NetworkSettings.Networks.#{@net}.IPAddress }}' #{@name}",
**options
Expand Down
8 changes: 4 additions & 4 deletions lib/dip/commands/nginx.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ def initialize(name:, socket:, net:, publish:, image:, domain:, certs:)
end

def execute
subshell("docker", "network create #{@net}", panic: false, err: File::NULL)
subshell("docker", "run #{container_args} #{@image}")
exec_subprocess("docker", "network create #{@net}", panic: false, err: File::NULL)
exec_subprocess("docker", "run #{container_args} #{@image}")
end

private
Expand All @@ -43,8 +43,8 @@ def initialize(name:)
end

def execute
subshell("docker", "stop #{@name}", panic: false, out: File::NULL, err: File::NULL)
subshell("docker", "rm -v #{@name}", panic: false, out: File::NULL, err: File::NULL)
exec_subprocess("docker", "stop #{@name}", panic: false, out: File::NULL, err: File::NULL)
exec_subprocess("docker", "rm -v #{@name}", panic: false, out: File::NULL, err: File::NULL)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/dip/commands/provision.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Commands
class Provision < Dip::Command
def execute
Dip.config.provision.each do |command|
subshell(command)
exec_subprocess(command)
end
end
end
Expand Down
17 changes: 13 additions & 4 deletions lib/dip/commands/run.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ def initialize(cmd, *argv, publish: nil)

def execute
if command[:service].nil?
shell(command[:command], get_args)
exec_program(command[:command], get_args, shell: command[:shell])
else
Dip::Commands::Compose.new(
command[:compose][:method],
*compose_arguments
*compose_arguments,
shell: command[:shell]
).execute
end
end
Expand All @@ -48,7 +49,11 @@ def compose_arguments
compose_argv << command.fetch(:service)

unless (cmd = command[:command]).empty?
compose_argv << cmd
if command[:shell]
compose_argv << cmd
else
compose_argv.concat(cmd.shellsplit)
end
end

compose_argv.concat(get_args)
Expand All @@ -75,7 +80,11 @@ def get_args
if argv.any?
argv
elsif !(default_args = command[:default_args]).empty?
Array(default_args)
if command[:shell]
default_args.shellsplit
else
Array(default_args)
end
else
[]
end
Expand Down
14 changes: 7 additions & 7 deletions lib/dip/commands/ssh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ def initialize(key:, volume:, interactive:, user: nil)
end

def execute
subshell("docker", "volume create --name ssh_data", out: File::NULL, err: File::NULL)
exec_subprocess("docker", "volume create --name ssh_data", out: File::NULL, err: File::NULL)

subshell(
exec_subprocess(
"docker",
"run #{user_args}--detach --volume ssh_data:/ssh --name=ssh-agent whilp/ssh-agent"
)

key = Dip.env.interpolate(@key)
subshell("docker", "run #{container_args} whilp/ssh-agent ssh-add #{key}")
exec_subprocess("docker", "run #{container_args} whilp/ssh-agent ssh-add #{key}")
end

private
Expand All @@ -44,15 +44,15 @@ def container_args

class Down < Dip::Command
def execute
subshell("docker", "stop ssh-agent", panic: false, out: File::NULL, err: File::NULL)
subshell("docker", "rm -v ssh-agent", panic: false, out: File::NULL, err: File::NULL)
subshell("docker", "volume rm ssh_data", panic: false, out: File::NULL, err: File::NULL)
exec_subprocess("docker", "stop ssh-agent", panic: false, out: File::NULL, err: File::NULL)
exec_subprocess("docker", "rm -v ssh-agent", panic: false, out: File::NULL, err: File::NULL)
exec_subprocess("docker", "volume rm ssh_data", panic: false, out: File::NULL, err: File::NULL)
end
end

class Status < Dip::Command
def execute
subshell("docker", "inspect --format '{{.State.Status}}' ssh-agent")
exec_subprocess("docker", "inspect --format '{{.State.Status}}' ssh-agent")
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/dip/interaction_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def build_command(entry)
description: entry[:description],
service: entry[:service],
command: entry[:command].to_s.strip,
shell: entry.fetch(:shell, true),
default_args: entry[:default_args].to_s.strip,
environment: entry[:environment] || {},
compose: {
Expand Down
32 changes: 16 additions & 16 deletions spec/lib/dip/commands/dns_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,78 +18,78 @@

context "when without arguments" do
before { cli.start "up".shellsplit }
it { expected_subshell("docker", ["network", "create", "frontend"]) }
it { expected_subshell("docker", cmd) }
it { expected_subprocess("docker", ["network", "create", "frontend"]) }
it { expected_subprocess("docker", cmd) }
end

context "when option `name` is present" do
let(:name) { "--name foo" }
before { cli.start "up --name foo".shellsplit }
it { expected_subshell("docker", cmd) }
it { expected_subprocess("docker", cmd) }
end

context "when option `socket` is present" do
let(:volume) { "--volume foo:/var/run/docker.sock:ro" }
before { cli.start "up --socket foo".shellsplit }
it { expected_subshell("docker", cmd) }
it { expected_subprocess("docker", cmd) }
end

context "when option `net` is present" do
let(:net) { "--net foo" }
before { cli.start "up --net foo".shellsplit }
it { expected_subshell("docker", ["network", "create", "foo"]) }
it { expected_subshell("docker", cmd) }
it { expected_subprocess("docker", ["network", "create", "foo"]) }
it { expected_subprocess("docker", cmd) }
end

context "when option `publish` is present" do
let(:port) { "--publish foo" }
before { cli.start "up --publish foo".shellsplit }
it { expected_subshell("docker", cmd) }
it { expected_subprocess("docker", cmd) }
end

context "when option `image` is present" do
let(:image) { "foo" }
before { cli.start "up --image foo".shellsplit }
it { expected_subshell("docker", cmd) }
it { expected_subprocess("docker", cmd) }
end

context "when option `domain` is present" do
let(:domain) { "--domain=foo" }
before { cli.start "up --domain foo".shellsplit }
it { expected_subshell("docker", cmd) }
it { expected_subprocess("docker", cmd) }
end
end

describe Dip::Commands::DNS::Down do
context "when without arguments" do
before { cli.start "down".shellsplit }
it { expected_subshell("docker", "stop dnsdock") }
it { expected_subshell("docker", "rm -v dnsdock") }
it { expected_subprocess("docker", "stop dnsdock") }
it { expected_subprocess("docker", "rm -v dnsdock") }
end

context "when option `name` is present" do
before { cli.start "down --name foo".shellsplit }
it { expected_subshell("docker", "stop foo") }
it { expected_subshell("docker", "rm -v foo") }
it { expected_subprocess("docker", "stop foo") }
it { expected_subprocess("docker", "rm -v foo") }
end
end

describe Dip::Commands::DNS::IP do
context "when without arguments" do
before { cli.start "ip".shellsplit }
it do
expected_subshell("docker", "inspect --format '{{ .NetworkSettings.Networks.frontend.IPAddress }}' dnsdock")
expected_subprocess("docker", "inspect --format '{{ .NetworkSettings.Networks.frontend.IPAddress }}' dnsdock")
end
end

context "when option `name` is present" do
before { cli.start "ip --name foo".shellsplit }
it { expected_subshell("docker", "inspect --format '{{ .NetworkSettings.Networks.frontend.IPAddress }}' foo") }
it { expected_subprocess("docker", "inspect --format '{{ .NetworkSettings.Networks.frontend.IPAddress }}' foo") }
end

context "when option `net` is present" do
before { cli.start "ip --net foo".shellsplit }
it { expected_subshell("docker", "inspect --format '{{ .NetworkSettings.Networks.foo.IPAddress }}' dnsdock") }
it { expected_subprocess("docker", "inspect --format '{{ .NetworkSettings.Networks.foo.IPAddress }}' dnsdock") }
end
end
end
Loading