forger 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.gitmodules +0 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +147 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +136 -0
- data/Guardfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +249 -0
- data/Rakefile +6 -0
- data/docs/example/.env +2 -0
- data/docs/example/.env.development +2 -0
- data/docs/example/.env.production +3 -0
- data/docs/example/app/scripts/hello.sh +3 -0
- data/docs/example/app/user-data/bootstrap.sh +35 -0
- data/docs/example/config/development.yml +8 -0
- data/docs/example/profiles/default.yml +11 -0
- data/docs/example/profiles/spot.yml +20 -0
- data/exe/forger +14 -0
- data/forger.gemspec +38 -0
- data/lib/forger.rb +29 -0
- data/lib/forger/ami.rb +10 -0
- data/lib/forger/aws_service.rb +7 -0
- data/lib/forger/base.rb +42 -0
- data/lib/forger/clean.rb +13 -0
- data/lib/forger/cleaner.rb +5 -0
- data/lib/forger/cleaner/ami.rb +45 -0
- data/lib/forger/cli.rb +67 -0
- data/lib/forger/command.rb +67 -0
- data/lib/forger/completer.rb +161 -0
- data/lib/forger/completer/script.rb +6 -0
- data/lib/forger/completer/script.sh +10 -0
- data/lib/forger/config.rb +20 -0
- data/lib/forger/core.rb +51 -0
- data/lib/forger/create.rb +155 -0
- data/lib/forger/create/error_messages.rb +58 -0
- data/lib/forger/create/params.rb +106 -0
- data/lib/forger/dotenv.rb +30 -0
- data/lib/forger/help.rb +9 -0
- data/lib/forger/help/ami.md +13 -0
- data/lib/forger/help/clean/ami.md +22 -0
- data/lib/forger/help/compile.md +5 -0
- data/lib/forger/help/completion.md +22 -0
- data/lib/forger/help/completion_script.md +3 -0
- data/lib/forger/help/create.md +7 -0
- data/lib/forger/help/upload.md +10 -0
- data/lib/forger/help/wait/ami.md +12 -0
- data/lib/forger/hook.rb +33 -0
- data/lib/forger/profile.rb +64 -0
- data/lib/forger/script.rb +46 -0
- data/lib/forger/script/compile.rb +40 -0
- data/lib/forger/script/compress.rb +62 -0
- data/lib/forger/script/templates/ami_creation.sh +12 -0
- data/lib/forger/script/templates/auto_terminate.sh +11 -0
- data/lib/forger/script/templates/auto_terminate_after_timeout.sh +5 -0
- data/lib/forger/script/templates/cloudwatch.sh +3 -0
- data/lib/forger/script/templates/extract_aws_ec2_scripts.sh +48 -0
- data/lib/forger/script/upload.rb +99 -0
- data/lib/forger/scripts/auto_terminate.sh +14 -0
- data/lib/forger/scripts/auto_terminate/after_timeout.sh +18 -0
- data/lib/forger/scripts/auto_terminate/functions.sh +130 -0
- data/lib/forger/scripts/auto_terminate/functions/amazonlinux2.sh +10 -0
- data/lib/forger/scripts/auto_terminate/functions/ubuntu.sh +11 -0
- data/lib/forger/scripts/auto_terminate/setup.sh +31 -0
- data/lib/forger/scripts/cloudwatch.sh +24 -0
- data/lib/forger/scripts/cloudwatch/configure.sh +84 -0
- data/lib/forger/scripts/cloudwatch/install.sh +3 -0
- data/lib/forger/scripts/cloudwatch/install/amazonlinux2.sh +4 -0
- data/lib/forger/scripts/cloudwatch/install/ubuntu.sh +23 -0
- data/lib/forger/scripts/cloudwatch/service.sh +3 -0
- data/lib/forger/scripts/cloudwatch/service/amazonlinux2.sh +11 -0
- data/lib/forger/scripts/cloudwatch/service/ubuntu.sh +8 -0
- data/lib/forger/scripts/shared/functions.sh +78 -0
- data/lib/forger/setting.rb +52 -0
- data/lib/forger/template.rb +13 -0
- data/lib/forger/template/context.rb +32 -0
- data/lib/forger/template/helper.rb +17 -0
- data/lib/forger/template/helper/ami_helper.rb +33 -0
- data/lib/forger/template/helper/core_helper.rb +127 -0
- data/lib/forger/template/helper/partial_helper.rb +71 -0
- data/lib/forger/template/helper/script_helper.rb +53 -0
- data/lib/forger/template/helper/ssh_key_helper.rb +21 -0
- data/lib/forger/version.rb +3 -0
- data/lib/forger/wait.rb +12 -0
- data/lib/forger/waiter.rb +5 -0
- data/lib/forger/waiter/ami.rb +61 -0
- data/spec/fixtures/demo_project/config/settings.yml +22 -0
- data/spec/fixtures/demo_project/config/test.yml +9 -0
- data/spec/fixtures/demo_project/profiles/default.yml +33 -0
- data/spec/lib/cli_spec.rb +41 -0
- data/spec/lib/params_spec.rb +71 -0
- data/spec/spec_helper.rb +33 -0
- metadata +354 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module Forger::Template::Helper::PartialHelper
|
2
|
+
def partial_exist?(path)
|
3
|
+
path = partial_path_for(path)
|
4
|
+
path = auto_add_format(path)
|
5
|
+
path && File.exist?(path)
|
6
|
+
end
|
7
|
+
|
8
|
+
# The partial's path is a relative path given without the extension and
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
# Given: file in app/partials/mypartial.sh
|
12
|
+
# The path should be: mypartial
|
13
|
+
#
|
14
|
+
# If the user specifies the extension then use that instead of auto-adding
|
15
|
+
# the detected format.
|
16
|
+
def partial(path,vars={}, options={})
|
17
|
+
path = partial_path_for(path)
|
18
|
+
path = auto_add_format(path)
|
19
|
+
|
20
|
+
result = RenderMePretty.result(path, context: self)
|
21
|
+
result = indent(result, options[:indent]) if options[:indent]
|
22
|
+
if options[:indent]
|
23
|
+
# Add empty line at beginning because empty lines gets stripped during
|
24
|
+
# processing anyway. This allows the user to call partial without having
|
25
|
+
# to put the partial call at very beginning of the line.
|
26
|
+
# This only should happen if user is using indent option.
|
27
|
+
["\n", result].join("\n")
|
28
|
+
else
|
29
|
+
result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# add indentation
|
34
|
+
def indent(text, indentation_amount)
|
35
|
+
text.split("\n").map do |line|
|
36
|
+
" " * indentation_amount + line
|
37
|
+
end.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def partial_path_for(path)
|
42
|
+
"#{Forger.root}/app/partials/#{path}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def auto_add_format(path)
|
46
|
+
# Return immediately if user provided explicit extension
|
47
|
+
extension = File.extname(path) # current extension
|
48
|
+
return path if !extension.empty?
|
49
|
+
|
50
|
+
# Else let's auto detect
|
51
|
+
paths = Dir.glob("#{path}.*")
|
52
|
+
|
53
|
+
if paths.size == 1 # non-ambiguous match
|
54
|
+
return paths.first
|
55
|
+
end
|
56
|
+
|
57
|
+
if paths.size > 1 # ambiguous match
|
58
|
+
puts "ERROR: Multiple possible partials found:".colorize(:red)
|
59
|
+
paths.each do |path|
|
60
|
+
puts " #{path}"
|
61
|
+
end
|
62
|
+
puts "Please specify an extension in the name to remove the ambiguity.".colorize(:green)
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
# Account for case when user wants to include a file with no extension at all
|
67
|
+
return path if File.exist?(path) && !File.directory?(path)
|
68
|
+
|
69
|
+
path # original path if this point is reached
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Forger::Template::Helper::ScriptHelper
|
2
|
+
# Bash code that is meant to included in user-data
|
3
|
+
def extract_scripts(options={})
|
4
|
+
check_s3_folder_settings!
|
5
|
+
|
6
|
+
settings_options = settings["extract_scripts"] || {}
|
7
|
+
options = settings_options.merge(options)
|
8
|
+
# defaults also here in case they are removed from settings
|
9
|
+
to = options[:to] || "/opt"
|
10
|
+
user = options[:as] || "ec2-user"
|
11
|
+
|
12
|
+
if Dir.glob("#{Forger.root}/app/scripts*").empty?
|
13
|
+
puts "WARN: you are using the extract_scripts helper method but you do not have any app/scripts.".colorize(:yellow)
|
14
|
+
calling_line = caller[0].split(':')[0..1].join(':')
|
15
|
+
puts "Called from: #{calling_line}"
|
16
|
+
return ""
|
17
|
+
end
|
18
|
+
|
19
|
+
<<-BASH_CODE
|
20
|
+
# Generated from the forger extract_scripts helper.
|
21
|
+
# Downloads scripts from s3, extract them, and setup.
|
22
|
+
mkdir -p #{to}
|
23
|
+
aws s3 cp #{scripts_s3_path} #{to}/
|
24
|
+
(
|
25
|
+
cd #{to}
|
26
|
+
tar zxf #{to}/#{scripts_name}
|
27
|
+
chmod -R a+x #{to}/scripts
|
28
|
+
chown -R #{user}:#{user} #{to}/scripts
|
29
|
+
)
|
30
|
+
BASH_CODE
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def check_s3_folder_settings!
|
35
|
+
return if settings["s3_folder"]
|
36
|
+
|
37
|
+
puts "Helper method called that requires the s3_folder to be set at:"
|
38
|
+
lines = caller.reject { |l| l =~ %r{lib/forger} } # hide internal forger trace
|
39
|
+
puts " #{lines[0]}"
|
40
|
+
|
41
|
+
puts "Please configure your config/settings.yml with an s3_folder.".colorize(:red)
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def scripts_name
|
46
|
+
File.basename(scripts_s3_path)
|
47
|
+
end
|
48
|
+
|
49
|
+
def scripts_s3_path
|
50
|
+
upload = Forger::Script::Upload.new
|
51
|
+
upload.s3_dest
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Forger::Template::Helper::SshKeyHelper
|
2
|
+
def add_ssh_key(user="ec2-user")
|
3
|
+
key_path = "#{ENV['HOME']}/.ssh/id_rsa.pub"
|
4
|
+
if File.exist?(key_path)
|
5
|
+
public_key = IO.read(key_path).strip
|
6
|
+
end
|
7
|
+
if public_key
|
8
|
+
<<-SCRIPT
|
9
|
+
# Automatically add user's public key from #{key_path}
|
10
|
+
cp /home/#{user}/.ssh/authorized_keys{,.bak}
|
11
|
+
echo #{public_key} >> /home/#{user}/.ssh/authorized_keys
|
12
|
+
chown #{user}:#{user} /home/#{user}/.ssh/authorized_keys
|
13
|
+
SCRIPT
|
14
|
+
else
|
15
|
+
<<-SCRIPT
|
16
|
+
# WARN: unable to find a ~/.ssh/id_rsa.pub locally on your machine. user: #{ENV['USER']}
|
17
|
+
# Unable to automatically add the public key
|
18
|
+
SCRIPT
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/forger/wait.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Forger
|
2
|
+
autoload :Waiter, 'forger/waiter'
|
3
|
+
|
4
|
+
class Wait < Command
|
5
|
+
desc "ami", "Wait until AMI available."
|
6
|
+
long_desc Help.text("wait:ami")
|
7
|
+
option :timeout, type: :numeric, default: 1800, desc: "Timeout in seconds."
|
8
|
+
def ami(name)
|
9
|
+
Waiter::Ami.new(options.merge(name: name)).wait
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Forger::Waiter
|
2
|
+
class Ami < Forger::Base
|
3
|
+
include Forger::AwsService
|
4
|
+
|
5
|
+
def wait
|
6
|
+
delay = 30
|
7
|
+
timeout = @options[:timeout]
|
8
|
+
max_attempts = timeout / delay
|
9
|
+
current_time = 0
|
10
|
+
|
11
|
+
puts "Waiting for #{@options[:name]} to be available. Delay: #{delay}s. Timeout: #{timeout}s"
|
12
|
+
puts "Current time: #{Time.now}"
|
13
|
+
return if ENV['TEST']
|
14
|
+
|
15
|
+
# Using while loop because of issues with ruby's Timeout module
|
16
|
+
# http://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/
|
17
|
+
detected = detect_ami
|
18
|
+
until detected || current_time > timeout
|
19
|
+
print '.'
|
20
|
+
sleep delay
|
21
|
+
current_time += 30
|
22
|
+
detected = detect_ami
|
23
|
+
end
|
24
|
+
puts
|
25
|
+
|
26
|
+
if current_time > timeout
|
27
|
+
puts "ERROR: Timeout. Unable to detect and available ami: #{@options[:name]}"
|
28
|
+
exit 1
|
29
|
+
else
|
30
|
+
puts "Found available AMI: #{@options[:name]}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
# Using custom detect_ami instead of ec2.wait_until(:image_availalbe, ...)
|
36
|
+
# because we start checking for the ami even before we've called
|
37
|
+
# create_ami. We start checking right after we launch the instance
|
38
|
+
# which will create the ami at the end.
|
39
|
+
def detect_ami(owners=["self"])
|
40
|
+
images = ec2.describe_images(
|
41
|
+
owners: owners,
|
42
|
+
filters: filters
|
43
|
+
).images
|
44
|
+
detected = images.first
|
45
|
+
!!detected
|
46
|
+
end
|
47
|
+
|
48
|
+
def filters
|
49
|
+
name_is_ami_id = @options[:name] =~ /^ami-/
|
50
|
+
|
51
|
+
filters = [{name: "state", values: ["available"]}]
|
52
|
+
filters << if name_is_ami_id
|
53
|
+
{name: "image-id", values: [@options[:name]]}
|
54
|
+
else
|
55
|
+
{name: "name", values: [@options[:name]]}
|
56
|
+
end
|
57
|
+
|
58
|
+
filters
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
# Settings control internal forger behavior.
|
3
|
+
# Settings are different from the config files. The config files are meant to
|
4
|
+
# expose config variables that you can use in your ERB code.
|
5
|
+
# There are separate files to separate user defined variables and internal
|
6
|
+
# setting configs.
|
7
|
+
development:
|
8
|
+
# By setting s3_folder, forger will automatically tarball and upload your scripts
|
9
|
+
# to set. You then can then use the extract_scripts helper method to download
|
10
|
+
# the scripts onto the server.
|
11
|
+
# s3_folder: mybucket/path/to/folder # simple string
|
12
|
+
# compile_clean: true # uncomment to clean
|
13
|
+
# extract_scripts:
|
14
|
+
# to: "/opt"
|
15
|
+
# as: "ec2-user"
|
16
|
+
# aws_profiles:
|
17
|
+
# - profile1
|
18
|
+
|
19
|
+
production:
|
20
|
+
# s3_folder: mybucket/folder
|
21
|
+
# aws_profiles:
|
22
|
+
# - profile2
|
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
# image_id: ami-97785bed # Amazon Linux AMI
|
3
|
+
image_id: ami-4fffc834 # Amazon Lambda AMI
|
4
|
+
# https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html
|
5
|
+
instance_type: t2.medium
|
6
|
+
key_name: default
|
7
|
+
max_count: 1
|
8
|
+
min_count: 1
|
9
|
+
user_data:
|
10
|
+
iam_instance_profile:
|
11
|
+
name: DevLinux
|
12
|
+
# public network settings
|
13
|
+
security_group_ids: <%= config["security_group_ids"].inspect %>
|
14
|
+
subnet_id: <%= config["subnets"].shuffle.first %>
|
15
|
+
block_device_mappings:
|
16
|
+
- device_name: /dev/xvda
|
17
|
+
ebs:
|
18
|
+
volume_size: 20
|
19
|
+
instance_market_options:
|
20
|
+
market_type: spot
|
21
|
+
# https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_LaunchTemplateSpotMarketOptionsRequest.html
|
22
|
+
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/EC2/Types/SpotMarketOptions.html
|
23
|
+
spot_options:
|
24
|
+
max_price: "0.018" # $0.020/hr = $14.40/mo
|
25
|
+
# $0.018/hr = $12.96/mo
|
26
|
+
# valid combinations:
|
27
|
+
# spot_instance_type: persistent
|
28
|
+
# instance_interruption_behavior: hibernate
|
29
|
+
# or
|
30
|
+
# spot_instance_type: one-time
|
31
|
+
# More info: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-interruptions.html
|
32
|
+
spot_instance_type: persistent
|
33
|
+
instance_interruption_behavior: hibernate
|
@@ -0,0 +1,41 @@
|
|
1
|
+
describe Forger::CLI do
|
2
|
+
before(:all) do
|
3
|
+
@args = "--noop"
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "forger" do
|
7
|
+
it "create" do
|
8
|
+
out = execute("exe/forger create server #{@args}")
|
9
|
+
expect(out).to include("Creating EC2 instance")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "ami" do
|
13
|
+
out = execute("exe/forger ami myimage #{@args}")
|
14
|
+
expect(out).to include("Creating EC2 instance")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "wait ami" do
|
18
|
+
out = execute("exe/forger wait ami myimage")
|
19
|
+
expect(out).to include("Waiting for")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "clean ami" do
|
23
|
+
out = execute("exe/forger clean ami imagebasename")
|
24
|
+
expect(out).to include("Cleaning out old AMIs")
|
25
|
+
end
|
26
|
+
|
27
|
+
commands = {
|
28
|
+
"am" => "ami",
|
29
|
+
"compile" => "--profile",
|
30
|
+
"create -" => "--profile",
|
31
|
+
"create" => "name",
|
32
|
+
"create name --" => "--profile",
|
33
|
+
}
|
34
|
+
commands.each do |command, expected_word|
|
35
|
+
it "completion #{command}" do
|
36
|
+
out = execute("exe/forger completion #{command}")
|
37
|
+
expect(out).to include(expected_word) # only checking for one word for simplicity
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
describe Forger::Create::Params do
|
2
|
+
let(:param) { Forger::Create::Params.new(name: "myserver") }
|
3
|
+
|
4
|
+
context "completely empty" do
|
5
|
+
it '#upsert_name_tag!' do
|
6
|
+
params = {}
|
7
|
+
result = param.upsert_name_tag!(params)
|
8
|
+
# puts "params: #{params.inspect}" # uncomment to see and debug
|
9
|
+
expect(result).to eq(
|
10
|
+
{"tag_specifications"=>
|
11
|
+
[{"resource_type"=>"instance",
|
12
|
+
"tags"=>[{"key"=>"Name", "value"=>"myserver"}]}]}
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "empty tag_specifications" do
|
18
|
+
it '#upsert_name_tag!' do
|
19
|
+
params = {"tag_specifications" => []}
|
20
|
+
result = param.upsert_name_tag!(params)
|
21
|
+
# puts "params: #{params.inspect}" # uncomment to see and debug
|
22
|
+
expect(result).to eq(
|
23
|
+
{"tag_specifications"=>
|
24
|
+
[{"resource_type"=>"instance",
|
25
|
+
"tags"=>[{"key"=>"Name", "value"=>"myserver"}]}]}
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "contains 1 instance with name" do
|
31
|
+
it '#upsert_name_tag!' do
|
32
|
+
params = { "tag_specifications" =>
|
33
|
+
[{
|
34
|
+
"resource_type"=>"instance",
|
35
|
+
"tags"=> [{"key"=>"Name", "value"=>"override-myserver"} ]
|
36
|
+
}]
|
37
|
+
}
|
38
|
+
result = param.upsert_name_tag!(params)
|
39
|
+
# puts "params: #{params.inspect}" # uncomment to see and debug
|
40
|
+
expect(result).to eq(
|
41
|
+
{"tag_specifications"=>
|
42
|
+
[{"resource_type"=>"instance",
|
43
|
+
"tags"=>[{"key"=>"Name", "value"=>"override-myserver"}]}]}
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "contains 1 instance with non-name tag" do
|
49
|
+
it '#upsert_name_tag!' do
|
50
|
+
params = { "tag_specifications" =>
|
51
|
+
[{
|
52
|
+
"resource_type"=>"instance",
|
53
|
+
"tags"=> [{"key"=>"Os", "value"=>"amazonlinux"} ]
|
54
|
+
}]
|
55
|
+
}
|
56
|
+
result = param.upsert_name_tag!(params)
|
57
|
+
# puts "params: #{params.inspect}" # uncomment to see and debug
|
58
|
+
expect(result).to eq(
|
59
|
+
{ "tag_specifications" =>
|
60
|
+
[{
|
61
|
+
"resource_type"=>"instance",
|
62
|
+
"tags"=> [
|
63
|
+
{"key"=>"Os", "value"=>"amazonlinux"},
|
64
|
+
{"key"=>"Name", "value"=>"myserver"},
|
65
|
+
]
|
66
|
+
}]
|
67
|
+
}
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
ENV["TEST"] = "1"
|
2
|
+
ENV["AWS_EC2_ENV"] = "test"
|
3
|
+
ENV["AWS_EC2_ROOT"] = "spec/fixtures/demo_project"
|
4
|
+
# Ensures aws api never called. Fixture home folder does not contain ~/.aws/credentails
|
5
|
+
ENV['HOME'] = "spec/fixtures/home"
|
6
|
+
|
7
|
+
# CodeClimate test coverage: https://docs.codeclimate.com/docs/configuring-test-coverage
|
8
|
+
# require 'simplecov'
|
9
|
+
# SimpleCov.start
|
10
|
+
|
11
|
+
require "pp"
|
12
|
+
require "byebug"
|
13
|
+
root = File.expand_path("../", File.dirname(__FILE__))
|
14
|
+
require "#{root}/lib/forger"
|
15
|
+
|
16
|
+
module Helper
|
17
|
+
def execute(cmd)
|
18
|
+
puts "Running: #{cmd}" if show_command?
|
19
|
+
out = `#{cmd}`
|
20
|
+
puts out if show_command?
|
21
|
+
out
|
22
|
+
end
|
23
|
+
|
24
|
+
# Added SHOW_COMMAND because DEBUG is also used by other libraries like
|
25
|
+
# bundler and it shows its internal debugging logging also.
|
26
|
+
def show_command?
|
27
|
+
ENV['DEBUG'] || ENV['SHOW_COMMAND']
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec.configure do |c|
|
32
|
+
c.include Helper
|
33
|
+
end
|