recap 0.1.0 → 0.2.0
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/.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
|