recap 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +4 -0
- data/README.md +12 -3
- data/Rakefile +8 -0
- data/Vagrantfile +61 -0
- data/doc/index.html +45 -26
- data/doc/lib/recap/bootstrap.html +42 -0
- data/doc/lib/recap/bundler.html +36 -19
- data/doc/lib/recap/capistrano_extensions.html +28 -23
- data/doc/lib/recap/cli.html +3 -0
- data/doc/lib/recap/compatibility.html +6 -3
- data/doc/lib/recap/deploy.html +41 -86
- data/doc/lib/recap/env.html +6 -1
- data/doc/lib/recap/foreman.html +3 -0
- data/doc/lib/recap/namespace.html +42 -0
- data/doc/lib/recap/preflight.html +12 -7
- data/doc/lib/recap/rails.html +3 -0
- data/doc/lib/recap/version.html +3 -0
- data/doc/lib/recap.html +42 -0
- data/features/bundling-gems.feature +18 -0
- data/features/deploying-projects.feature +21 -0
- data/features/managing-processes.feature +17 -0
- data/features/setting-environment-variables.feature +21 -0
- data/features/steps/capistrano_steps.rb +98 -0
- data/features/support/project.rb +211 -0
- data/features/support/server.rb +53 -0
- data/features/templates/gem/binary.erb +43 -0
- data/features/templates/gem/gemspec.erb +11 -0
- data/features/templates/project/Capfile +21 -0
- data/features/templates/project/Capfile.erb +21 -0
- data/features/templates/project/Gemfile.erb +7 -0
- data/features/templates/project/Procfile.erb +1 -0
- data/index.rb +26 -17
- data/lib/recap/bootstrap.rb +47 -0
- data/lib/recap/bundler.rb +31 -21
- data/lib/recap/capistrano_extensions.rb +11 -9
- data/lib/recap/cli.rb +1 -1
- data/lib/recap/compatibility.rb +3 -3
- data/lib/recap/deploy.rb +45 -57
- data/lib/recap/env.rb +30 -26
- data/lib/recap/environment.rb +54 -0
- data/lib/recap/foreman.rb +28 -9
- data/lib/recap/namespace.rb +37 -0
- data/lib/recap/preflight.rb +10 -8
- data/lib/recap/rails.rb +6 -4
- data/lib/recap/ruby.rb +3 -0
- data/lib/recap/static.rb +1 -0
- data/lib/recap/version.rb +1 -1
- data/lib/recap.rb +12 -0
- data/recap.gemspec +8 -4
- data/spec/models/environment_spec.rb +143 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/tasks/bootstrap_spec.rb +34 -0
- data/spec/tasks/bundler_spec.rb +126 -0
- data/spec/tasks/deploy_spec.rb +209 -0
- data/spec/tasks/env_spec.rb +38 -0
- data/spec/tasks/foreman_spec.rb +154 -0
- data/test-vm/manifests/base.pp +17 -0
- data/test-vm/share/.gitkeep +0 -0
- metadata +138 -19
- /data/bin/{tomafro-deploy → recap} +0 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
class Recap::Environment
|
2
|
+
def initialize(variables = {})
|
3
|
+
@variables = variables
|
4
|
+
end
|
5
|
+
|
6
|
+
def get(name)
|
7
|
+
@variables[name]
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(name, value)
|
11
|
+
if value.nil? || value.empty?
|
12
|
+
@variables.delete(name)
|
13
|
+
else
|
14
|
+
@variables[name] = value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_string(string)
|
19
|
+
if string =~ /\A([A-Za-z0-9_]+)=(.*)\z/
|
20
|
+
set $1, $2
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def empty?
|
25
|
+
@variables.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def merge(hash)
|
29
|
+
hash.each {|k, v| set(k, v)}
|
30
|
+
end
|
31
|
+
|
32
|
+
def each(&block)
|
33
|
+
@variables.sort.each(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def include?(key)
|
37
|
+
@variables.include?(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
@variables.keys.sort.map do |key|
|
42
|
+
key + "=" + @variables[key] + "\n" if @variables[key]
|
43
|
+
end.compact.join
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
def from_string(string)
|
48
|
+
string.split("\n").inject(new) do |env, line|
|
49
|
+
env.set_string(line)
|
50
|
+
env
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/recap/foreman.rb
CHANGED
@@ -1,39 +1,58 @@
|
|
1
|
-
|
1
|
+
module Recap::Foreman
|
2
|
+
extend Recap::Namespace
|
3
|
+
|
2
4
|
namespace :foreman do
|
3
|
-
|
5
|
+
# Processes are delcared in a `Procfile`, by default in the root of the application directory
|
6
|
+
set(:procfile) { "#{deploy_to}/Procfile" }
|
7
|
+
|
8
|
+
# Foreman startup scripts are exported in `upstart` format by default
|
4
9
|
set(:foreman_export_format, "upstart")
|
10
|
+
|
11
|
+
# Scripts are exported (as the the application user) to a temporary location first
|
12
|
+
set(:foreman_tmp_location) { "#{deploy_to}/tmp/foreman" }
|
13
|
+
|
14
|
+
# After exports, the scripts are moved to their final location, usually `/etc/init`
|
5
15
|
set(:foreman_export_location, "/etc/init")
|
6
16
|
|
17
|
+
# The standard foreman export
|
18
|
+
set(:foreman_export_command) { "./bin/foreman export #{foreman_export_format} #{foreman_tmp_location} --procfile #{procfile} --app #{application} --user #{application_user} --log #{deploy_to}/log" }
|
19
|
+
|
7
20
|
namespace :export do
|
21
|
+
# After each deployment, the startup scripts are exported if the `Procfile` has changed
|
8
22
|
task :if_changed do
|
9
23
|
if deployed_file_changed?(procfile)
|
10
24
|
top.foreman.export.default
|
11
25
|
end
|
12
26
|
end
|
13
27
|
|
14
|
-
|
28
|
+
# To export the scripts, they are first generated in a temporary location, then copied to their final
|
29
|
+
# destination. This is done because the foreman export command needs to be run as the application user,
|
30
|
+
# while sudo is required to write to `/etc/init`.
|
31
|
+
task :default do
|
15
32
|
if deployed_file_exists?(procfile)
|
16
|
-
|
17
|
-
as_app "./bin/foreman export #{foreman_export_format} #{tmp} --procfile #{procfile} --app #{application} --user #{application_user} --log #{deploy_to}/log"
|
33
|
+
as_app foreman_export_command
|
18
34
|
sudo "rm -f #{foreman_export_location}/#{application}*"
|
19
|
-
sudo "cp #{
|
35
|
+
sudo "cp #{foreman_tmp_location}/* #{foreman_export_location}"
|
20
36
|
end
|
21
37
|
end
|
22
38
|
end
|
23
39
|
|
24
|
-
|
40
|
+
# Starts all processes that form the application
|
41
|
+
task :start do
|
25
42
|
if deployed_file_exists?(procfile)
|
26
43
|
sudo "start #{application}"
|
27
44
|
end
|
28
45
|
end
|
29
46
|
|
30
|
-
|
47
|
+
# Restarts all processes that form the application
|
48
|
+
task :restart do
|
31
49
|
if deployed_file_exists?(procfile)
|
32
50
|
sudo "restart #{application} || sudo start #{application}"
|
33
51
|
end
|
34
52
|
end
|
35
53
|
|
36
|
-
|
54
|
+
# Stops all processes that form the application
|
55
|
+
task :stop do
|
37
56
|
if deployed_file_exists?(procfile)
|
38
57
|
sudo "stop #{application}"
|
39
58
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'capistrano'
|
2
|
+
require 'recap/capistrano_extensions'
|
3
|
+
|
4
|
+
module Recap::Namespace
|
5
|
+
def self.default_config
|
6
|
+
@default_config
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.default_config=(config)
|
10
|
+
@default_config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
if Capistrano::Configuration.instance
|
14
|
+
self.default_config = Capistrano::Configuration.instance(:must_exist)
|
15
|
+
end
|
16
|
+
|
17
|
+
def capistrano_definitions
|
18
|
+
@capistrano_definitions ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
def namespace(name, &block)
|
22
|
+
capistrano_definitions << Proc.new do
|
23
|
+
namespace name do
|
24
|
+
instance_eval(&block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
load_into(Recap::Namespace.default_config) if Recap::Namespace.default_config
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_into(configuration)
|
32
|
+
configuration.extend(self)
|
33
|
+
capistrano_definitions.each do |definition|
|
34
|
+
configuration.load(&definition)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/recap/preflight.rb
CHANGED
@@ -17,17 +17,19 @@
|
|
17
17
|
# This preflight recipe checks each of these things in turn, and attempts to give helpful advice
|
18
18
|
# should a check fail.
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
before 'deploy:setup', 'preflight:check'
|
23
|
-
before 'deploy', 'preflight:check'
|
24
|
-
|
25
|
-
set(:remote_username) { capture('whoami').strip }
|
20
|
+
module Recap::Preflight
|
21
|
+
extend Recap::Namespace
|
26
22
|
|
27
23
|
namespace :preflight do
|
24
|
+
# The preflight check is pretty quick, so run it before every `deploy:setup` and `deploy`
|
25
|
+
before 'deploy:setup', 'preflight:check'
|
26
|
+
before 'deploy', 'preflight:check'
|
27
|
+
|
28
|
+
set(:remote_username) { capture('whoami').strip }
|
29
|
+
|
28
30
|
task :check do
|
29
31
|
# First check the `application_user` exists
|
30
|
-
if
|
32
|
+
if exit_code("id #{application_user}").strip != "0"
|
31
33
|
abort %{
|
32
34
|
The application user '#{application_user}' doesn't exist. You can create this user by logging into the server and running:
|
33
35
|
|
@@ -36,7 +38,7 @@ The application user '#{application_user}' doesn't exist. You can create this u
|
|
36
38
|
end
|
37
39
|
|
38
40
|
# Then the `application_group`
|
39
|
-
if
|
41
|
+
if exit_code("id -g #{application_group}") != "0"
|
40
42
|
abort %{
|
41
43
|
The application group '#{application_group}' doesn't exist. You can create this group by logging into the server and running:
|
42
44
|
|
data/lib/recap/rails.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'recap/deploy'
|
2
2
|
|
3
|
-
|
3
|
+
module Recap::Rails
|
4
|
+
extend Recap::Namespace
|
5
|
+
|
4
6
|
namespace :rails do
|
5
7
|
namespace :db do
|
6
8
|
task :load_schema do
|
@@ -15,8 +17,8 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end
|
18
|
-
end
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
after "deploy:clone_code", "rails:db:load_schema"
|
22
|
+
after "deploy:update_code", "rails:db:migrate"
|
23
|
+
end
|
22
24
|
end
|
data/lib/recap/ruby.rb
ADDED
data/lib/recap/static.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'recap/deploy'
|
data/lib/recap/version.rb
CHANGED
data/lib/recap.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Recap
|
2
|
+
autoload :Namespace, 'recap/namespace'
|
3
|
+
|
4
|
+
autoload :Bootstrap, 'recap/bootstrap'
|
5
|
+
autoload :Bundler, 'recap/bundler'
|
6
|
+
autoload :Compatibility, 'recap/compatibility'
|
7
|
+
autoload :Deploy, 'recap/deploy'
|
8
|
+
autoload :Env, 'recap/env'
|
9
|
+
autoload :Environment, 'recap/environment'
|
10
|
+
autoload :Foreman, 'recap/foreman'
|
11
|
+
autoload :Rails, 'recap/rails'
|
12
|
+
end
|
data/recap.gemspec
CHANGED
@@ -7,19 +7,23 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Recap::VERSION
|
8
8
|
s.authors = ["Tom Ward"]
|
9
9
|
s.email = ["tom@popdog.net"]
|
10
|
-
s.homepage = "
|
10
|
+
s.homepage = "http://code.gofreerange.com/recap"
|
11
11
|
s.summary = %q{GIT based deployment recipes for Capistrano}
|
12
12
|
s.description = %q{GIT based deployment recipes for Capistrano}
|
13
13
|
|
14
|
-
s.rubyforge_project = "recap"
|
15
|
-
|
16
14
|
s.files = `git ls-files`.split("\n")
|
17
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
17
|
s.require_paths = ["lib"]
|
20
18
|
|
21
|
-
s.add_dependency('capistrano', '~>2.
|
19
|
+
s.add_dependency('capistrano', '~>2.9.0')
|
22
20
|
s.add_dependency('thor')
|
23
21
|
s.add_development_dependency('rake', '~>0.9.2')
|
24
22
|
s.add_development_dependency('rocco', '~>0.8.1')
|
23
|
+
s.add_development_dependency('rspec', '~>2.7.0')
|
24
|
+
s.add_development_dependency('mocha', '~>0.10.0')
|
25
|
+
s.add_development_dependency('vagrant', '~>0.9.7')
|
26
|
+
s.add_development_dependency('sahara', '~>0.0.10')
|
27
|
+
s.add_development_dependency('cucumber', '~>1.1.4')
|
28
|
+
s.add_development_dependency('faker', '~>1.0.1')
|
25
29
|
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Recap::Environment do
|
4
|
+
describe '#empty?' do
|
5
|
+
it 'returns true if no variables set' do
|
6
|
+
Recap::Environment.new.empty?.should be_true
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'returns false if no variables set' do
|
10
|
+
Recap::Environment.new('FIRST' => 'One').empty?.should be_false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#include?(key)' do
|
15
|
+
it 'returns true if variables set' do
|
16
|
+
Recap::Environment.new('FIRST' => 'One').include?('FIRST').should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns false if variable has not been set' do
|
20
|
+
Recap::Environment.new('DIFFERENT' => 'One').include?('FIRST').should be_false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#get(name)' do
|
25
|
+
subject do
|
26
|
+
Recap::Environment.new('FIRST' => 'One')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns value if variable set' do
|
30
|
+
subject.get('FIRST').should eql('One')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns nil if variable not set' do
|
34
|
+
subject.get('MISSING').should be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#set(name, value)' do
|
39
|
+
subject do
|
40
|
+
Recap::Environment.new('FIRST' => 'One')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'sets variable value' do
|
44
|
+
subject.set('SECOND', 'Two')
|
45
|
+
subject.get('SECOND').should eql('Two')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'unsets variable if value is nil' do
|
49
|
+
subject.set('FIRST', nil)
|
50
|
+
subject.get('FIRST').should be_nil
|
51
|
+
subject.include?('FIRST').should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'unsets variable if value is empty' do
|
55
|
+
subject.set('FIRST', '')
|
56
|
+
subject.get('FIRST').should be_nil
|
57
|
+
subject.include?('FIRST').should be_false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#each' do
|
62
|
+
subject do
|
63
|
+
Recap::Environment.new('FIRST' => 'One', 'SECOND' => 'Two', 'THIRD' => 'Three', 'FOURTH' => 'Four')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'yields each variable and value in turn (ordered alphabetically)' do
|
67
|
+
result = []
|
68
|
+
subject.each do |k, v|
|
69
|
+
result << [k, v]
|
70
|
+
end
|
71
|
+
result.should eql([['FIRST', 'One'], ['FOURTH', 'Four'], ['SECOND', 'Two'], ['THIRD', 'Three']])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#merge(variables)' do
|
76
|
+
subject do
|
77
|
+
Recap::Environment.new('FIRST' => 'One')
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'sets each variable value' do
|
81
|
+
subject.merge('SECOND' => 'Two', 'THIRD' => 'Three')
|
82
|
+
subject.get('SECOND').should eql('Two')
|
83
|
+
subject.get('THIRD').should eql('Three')
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'preserves existing values if not provided' do
|
87
|
+
subject.merge('ANYTHING' => 'Goes')
|
88
|
+
subject.get('FIRST').should eql('One')
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'overides existing values if provided' do
|
92
|
+
subject.merge('FIRST' => 'Un')
|
93
|
+
subject.get('FIRST').should eql('Un')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#to_s' do
|
98
|
+
subject do
|
99
|
+
Recap::Environment.new('FIRST' => 'One', 'SECOND' => 'Two', 'THIRD' => nil, 'FOURTH' => 'Four').to_s
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'declares each variable on its own line' do
|
103
|
+
subject.match(/^FIRST=One\n/).should_not be_nil
|
104
|
+
subject.match(/^SECOND=Two\n/).should_not be_nil
|
105
|
+
subject.match(/^FOURTH=Four\n/).should_not be_nil
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'ignores nil variable values' do
|
109
|
+
subject.match(/THIRD/).should be_nil
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'orders variables alphabetically' do
|
113
|
+
indexes = ['FIRST', 'FOURTH', 'SECOND'].map {|k| subject.index(k)}
|
114
|
+
indexes.sort.should eql(indexes)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '.from_string(declarations)' do
|
119
|
+
it 'builds instance using string representation' do
|
120
|
+
instance = Recap::Environment.from_string("FIRST=One\nSECOND=Two\n")
|
121
|
+
instance.get('FIRST').should eql('One')
|
122
|
+
instance.get('SECOND').should eql('Two')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'handles variables with numbers and underscores in their names' do
|
126
|
+
instance = Recap::Environment.from_string("THIS_1=One\nThose_2=Two\n")
|
127
|
+
instance.get('THIS_1').should eql('One')
|
128
|
+
instance.get('Those_2').should eql('Two')
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'gracefully ignores missing newline at end of string' do
|
132
|
+
instance = Recap::Environment.from_string("FIRST=One\nSECOND=Two")
|
133
|
+
instance.get('FIRST').should eql('One')
|
134
|
+
instance.get('SECOND').should eql('Two')
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'acts as the inverse of #to_s' do
|
138
|
+
string = "FIRST=One\nSECOND=Two\nTHIRD=three\n"
|
139
|
+
excercised = Recap::Environment.from_string(Recap::Environment.from_string(string).to_s).to_s
|
140
|
+
excercised.should eql(string)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'recap/bootstrap'
|
3
|
+
|
4
|
+
describe Recap::Bootstrap do
|
5
|
+
let :config do
|
6
|
+
Capistrano::Configuration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
let :namespace do
|
10
|
+
config.bootstrap
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
Recap::Bootstrap.load_into(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'Tasks' do
|
18
|
+
describe 'bootstrap' do
|
19
|
+
it 'runs bootsrap:application and bootstrap:user tasks' do
|
20
|
+
namespace.expects(:application).in_sequence
|
21
|
+
namespace.expects(:user).in_sequence
|
22
|
+
config.find_and_execute_task('bootstrap')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'bootstrap:user' do
|
27
|
+
pending 'Tests not written'
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'bootstrap:application' do
|
31
|
+
pending 'Tests not written'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Recap::Bundler do
|
4
|
+
let :config do
|
5
|
+
Capistrano::Configuration.new
|
6
|
+
end
|
7
|
+
|
8
|
+
let :namespace do
|
9
|
+
config.bundle
|
10
|
+
end
|
11
|
+
|
12
|
+
let :deploy_to do
|
13
|
+
'path/to/deploy/to'
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
config.set :deploy_to, deploy_to
|
18
|
+
Recap::Bundler.load_into(config)
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'Settings' do
|
22
|
+
describe '#bundle_gemfile' do
|
23
|
+
it 'defaults to deploy_to + /Gemfile' do
|
24
|
+
config.bundle_gemfile.should eql(deploy_to + '/Gemfile')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#bundle_gemfile_lock' do
|
29
|
+
it 'defaults to bundle_gemfile + .lock' do
|
30
|
+
config.set :bundle_gemfile, 'custom/Gemfile'
|
31
|
+
config.bundle_gemfile_lock.should eql('custom/Gemfile.lock')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#bundle_path' do
|
36
|
+
it 'defaults to deploy_to + /vendor/gems' do
|
37
|
+
config.bundle_path.should eql(deploy_to + '/vendor/gems')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#bundle_without' do
|
42
|
+
it 'defaults to development, test and assets groups' do
|
43
|
+
config.bundle_without.should eql("development test assets")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#bundle_install_command' do
|
48
|
+
it 'takes --gemfile from the bundle_gemfile setting' do
|
49
|
+
config.set :bundle_gemfile, 'path/to/bundle/Gemfile'
|
50
|
+
config.bundle_install_command.include?(" --gemfile path/to/bundle/Gemfile ").should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'takes --path from the bundle_path setting' do
|
54
|
+
config.set :bundle_path, 'path/to/install/gems'
|
55
|
+
config.bundle_install_command.include?(" --path path/to/install/gems ").should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'takes --without from the bundle_without setting' do
|
59
|
+
config.set :bundle_without, 'groups to skip'
|
60
|
+
config.bundle_install_command.include?(" --without groups to skip").should be_true
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'includes --deployment flag to ensure Gemfile.lock exists' do
|
64
|
+
config.bundle_install_command.include?(" --deployment ").should be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'includes --binstubs flag to generate binary stubs used by other tasks' do
|
68
|
+
config.bundle_install_command.include?(" --binstubs ").should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'includes --quiet flag to reduce uneccessary noise' do
|
72
|
+
config.bundle_install_command.include?(" --quiet ").should be_true
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'Tasks' do
|
78
|
+
describe 'bundle:install' do
|
79
|
+
it 'run bundle_install_command as the app if the Gemfile and Gemfile.lock exist' do
|
80
|
+
namespace.stubs(:deployed_file_exists?).with(config.bundle_gemfile).returns(true)
|
81
|
+
namespace.stubs(:deployed_file_exists?).with(config.bundle_gemfile_lock).returns(true)
|
82
|
+
namespace.expects(:as_app).with(config.bundle_install_command)
|
83
|
+
|
84
|
+
config.find_and_execute_task('bundle:install')
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'skips bundle_install if the Gemfile missing' do
|
88
|
+
namespace.stubs(:deployed_file_exists?).with(config.bundle_gemfile).returns(false)
|
89
|
+
namespace.expects(:as_app).never
|
90
|
+
|
91
|
+
config.find_and_execute_task('bundle:install')
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'aborts with warning if Gemfile exists but Gemfile.lock doesn\'t' do
|
95
|
+
namespace.stubs(:deployed_file_exists?).with(config.bundle_gemfile).returns(true)
|
96
|
+
namespace.stubs(:deployed_file_exists?).with(config.bundle_gemfile_lock).returns(false)
|
97
|
+
lambda do
|
98
|
+
namespace.find_and_execute_task('bundle:install')
|
99
|
+
end.should raise_error(SystemExit, 'Gemfile found without Gemfile.lock. The Gemfile.lock should be committed to the project repository')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe 'bundle:install:if_changed' do
|
104
|
+
it 'calls bundle:install:default if the Gemfile.lock has changed' do
|
105
|
+
namespace.stubs(:deployed_file_changed?).with(config.bundle_gemfile).returns(false)
|
106
|
+
namespace.stubs(:deployed_file_changed?).with(config.bundle_gemfile_lock).returns(true)
|
107
|
+
namespace.install.expects(:default)
|
108
|
+
config.find_and_execute_task('bundle:install:if_changed')
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'calls bundle:install:default if the Gemfile has changed' do
|
112
|
+
namespace.stubs(:deployed_file_changed?).with(config.bundle_gemfile).returns(true)
|
113
|
+
namespace.stubs(:deployed_file_changed?).with(config.bundle_gemfile_lock).returns(false)
|
114
|
+
namespace.install.expects(:default)
|
115
|
+
config.find_and_execute_task('bundle:install:if_changed')
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'skips bundle_install if neither Gemfile nor Gemfile.lock have changed' do
|
119
|
+
namespace.stubs(:deployed_file_changed?).with(config.bundle_gemfile).returns(false)
|
120
|
+
namespace.stubs(:deployed_file_changed?).with(config.bundle_gemfile_lock).returns(false)
|
121
|
+
namespace.install.expects(:default).never
|
122
|
+
config.find_and_execute_task('bundle:install:if_changed')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|