captivate 0.0.1
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/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.markdown +79 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/bin/captivate +4 -0
- data/captivate-0.0.0.gem +0 -0
- data/captivate.gemspec +263 -0
- data/lib/captivate.rb +199 -0
- data/spec/acceptance_spec.rb +55 -0
- data/spec/captivate_spec.rb +269 -0
- data/spec/fixtures/rails_apps/multi_stage/Capfile +4 -0
- data/spec/fixtures/rails_apps/multi_stage/README +243 -0
- data/spec/fixtures/rails_apps/multi_stage/Rakefile +10 -0
- data/spec/fixtures/rails_apps/multi_stage/app/controllers/application_controller.rb +10 -0
- data/spec/fixtures/rails_apps/multi_stage/app/helpers/application_helper.rb +3 -0
- data/spec/fixtures/rails_apps/multi_stage/config/boot.rb +110 -0
- data/spec/fixtures/rails_apps/multi_stage/config/captivate.yml +4 -0
- data/spec/fixtures/rails_apps/multi_stage/config/database.yml +22 -0
- data/spec/fixtures/rails_apps/multi_stage/config/deploy.rb +27 -0
- data/spec/fixtures/rails_apps/multi_stage/config/deploy/staging.rb +0 -0
- data/spec/fixtures/rails_apps/multi_stage/config/environment.rb +41 -0
- data/spec/fixtures/rails_apps/multi_stage/config/environments/development.rb +17 -0
- data/spec/fixtures/rails_apps/multi_stage/config/environments/production.rb +28 -0
- data/spec/fixtures/rails_apps/multi_stage/config/environments/test.rb +28 -0
- data/spec/fixtures/rails_apps/multi_stage/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/fixtures/rails_apps/multi_stage/config/initializers/inflections.rb +10 -0
- data/spec/fixtures/rails_apps/multi_stage/config/initializers/mime_types.rb +5 -0
- data/spec/fixtures/rails_apps/multi_stage/config/initializers/new_rails_defaults.rb +21 -0
- data/spec/fixtures/rails_apps/multi_stage/config/initializers/session_store.rb +15 -0
- data/spec/fixtures/rails_apps/multi_stage/config/locales/en.yml +5 -0
- data/spec/fixtures/rails_apps/multi_stage/config/routes.rb +43 -0
- data/spec/fixtures/rails_apps/multi_stage/db/seeds.rb +7 -0
- data/spec/fixtures/rails_apps/multi_stage/doc/README_FOR_APP +2 -0
- data/spec/fixtures/rails_apps/multi_stage/log/development.log +0 -0
- data/spec/fixtures/rails_apps/multi_stage/log/production.log +0 -0
- data/spec/fixtures/rails_apps/multi_stage/log/server.log +0 -0
- data/spec/fixtures/rails_apps/multi_stage/log/test.log +0 -0
- data/spec/fixtures/rails_apps/multi_stage/public/404.html +30 -0
- data/spec/fixtures/rails_apps/multi_stage/public/422.html +30 -0
- data/spec/fixtures/rails_apps/multi_stage/public/500.html +30 -0
- data/spec/fixtures/rails_apps/multi_stage/public/favicon.ico +0 -0
- data/spec/fixtures/rails_apps/multi_stage/public/images/rails.png +0 -0
- data/spec/fixtures/rails_apps/multi_stage/public/index.html +275 -0
- data/spec/fixtures/rails_apps/multi_stage/public/javascripts/application.js +2 -0
- data/spec/fixtures/rails_apps/multi_stage/public/javascripts/controls.js +963 -0
- data/spec/fixtures/rails_apps/multi_stage/public/javascripts/dragdrop.js +973 -0
- data/spec/fixtures/rails_apps/multi_stage/public/javascripts/effects.js +1128 -0
- data/spec/fixtures/rails_apps/multi_stage/public/javascripts/prototype.js +4320 -0
- data/spec/fixtures/rails_apps/multi_stage/public/robots.txt +5 -0
- data/spec/fixtures/rails_apps/multi_stage/script/about +4 -0
- data/spec/fixtures/rails_apps/multi_stage/script/console +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/dbconsole +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/destroy +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/generate +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/performance/benchmarker +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/performance/profiler +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/plugin +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/runner +3 -0
- data/spec/fixtures/rails_apps/multi_stage/script/server +3 -0
- data/spec/fixtures/rails_apps/multi_stage/test/performance/browsing_test.rb +9 -0
- data/spec/fixtures/rails_apps/multi_stage/test/test_helper.rb +38 -0
- data/spec/fixtures/rails_apps/no_cap/README +243 -0
- data/spec/fixtures/rails_apps/no_cap/Rakefile +10 -0
- data/spec/fixtures/rails_apps/no_cap/app/controllers/application_controller.rb +10 -0
- data/spec/fixtures/rails_apps/no_cap/app/helpers/application_helper.rb +3 -0
- data/spec/fixtures/rails_apps/no_cap/config/boot.rb +110 -0
- data/spec/fixtures/rails_apps/no_cap/config/database.yml +22 -0
- data/spec/fixtures/rails_apps/no_cap/config/environment.rb +41 -0
- data/spec/fixtures/rails_apps/no_cap/config/environments/development.rb +17 -0
- data/spec/fixtures/rails_apps/no_cap/config/environments/production.rb +28 -0
- data/spec/fixtures/rails_apps/no_cap/config/environments/test.rb +28 -0
- data/spec/fixtures/rails_apps/no_cap/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/fixtures/rails_apps/no_cap/config/initializers/inflections.rb +10 -0
- data/spec/fixtures/rails_apps/no_cap/config/initializers/mime_types.rb +5 -0
- data/spec/fixtures/rails_apps/no_cap/config/initializers/new_rails_defaults.rb +21 -0
- data/spec/fixtures/rails_apps/no_cap/config/initializers/session_store.rb +15 -0
- data/spec/fixtures/rails_apps/no_cap/config/locales/en.yml +5 -0
- data/spec/fixtures/rails_apps/no_cap/config/routes.rb +43 -0
- data/spec/fixtures/rails_apps/no_cap/db/seeds.rb +7 -0
- data/spec/fixtures/rails_apps/no_cap/doc/README_FOR_APP +2 -0
- data/spec/fixtures/rails_apps/no_cap/log/development.log +0 -0
- data/spec/fixtures/rails_apps/no_cap/log/production.log +0 -0
- data/spec/fixtures/rails_apps/no_cap/log/server.log +0 -0
- data/spec/fixtures/rails_apps/no_cap/log/test.log +0 -0
- data/spec/fixtures/rails_apps/no_cap/public/404.html +30 -0
- data/spec/fixtures/rails_apps/no_cap/public/422.html +30 -0
- data/spec/fixtures/rails_apps/no_cap/public/500.html +30 -0
- data/spec/fixtures/rails_apps/no_cap/public/favicon.ico +0 -0
- data/spec/fixtures/rails_apps/no_cap/public/images/rails.png +0 -0
- data/spec/fixtures/rails_apps/no_cap/public/index.html +275 -0
- data/spec/fixtures/rails_apps/no_cap/public/javascripts/application.js +2 -0
- data/spec/fixtures/rails_apps/no_cap/public/javascripts/controls.js +963 -0
- data/spec/fixtures/rails_apps/no_cap/public/javascripts/dragdrop.js +973 -0
- data/spec/fixtures/rails_apps/no_cap/public/javascripts/effects.js +1128 -0
- data/spec/fixtures/rails_apps/no_cap/public/javascripts/prototype.js +4320 -0
- data/spec/fixtures/rails_apps/no_cap/public/robots.txt +5 -0
- data/spec/fixtures/rails_apps/no_cap/script/about +4 -0
- data/spec/fixtures/rails_apps/no_cap/script/console +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/dbconsole +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/destroy +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/generate +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/performance/benchmarker +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/performance/profiler +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/plugin +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/runner +3 -0
- data/spec/fixtures/rails_apps/no_cap/script/server +3 -0
- data/spec/fixtures/rails_apps/no_cap/test/performance/browsing_test.rb +9 -0
- data/spec/fixtures/rails_apps/no_cap/test/test_helper.rb +38 -0
- data/spec/fixtures/rails_apps/single_stage/Capfile +4 -0
- data/spec/fixtures/rails_apps/single_stage/README +243 -0
- data/spec/fixtures/rails_apps/single_stage/Rakefile +10 -0
- data/spec/fixtures/rails_apps/single_stage/app/controllers/application_controller.rb +10 -0
- data/spec/fixtures/rails_apps/single_stage/app/helpers/application_helper.rb +3 -0
- data/spec/fixtures/rails_apps/single_stage/config/boot.rb +110 -0
- data/spec/fixtures/rails_apps/single_stage/config/captivate.yml +2 -0
- data/spec/fixtures/rails_apps/single_stage/config/database.yml +22 -0
- data/spec/fixtures/rails_apps/single_stage/config/deploy.rb +24 -0
- data/spec/fixtures/rails_apps/single_stage/config/environment.rb +41 -0
- data/spec/fixtures/rails_apps/single_stage/config/environments/development.rb +17 -0
- data/spec/fixtures/rails_apps/single_stage/config/environments/production.rb +28 -0
- data/spec/fixtures/rails_apps/single_stage/config/environments/test.rb +28 -0
- data/spec/fixtures/rails_apps/single_stage/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/fixtures/rails_apps/single_stage/config/initializers/inflections.rb +10 -0
- data/spec/fixtures/rails_apps/single_stage/config/initializers/mime_types.rb +5 -0
- data/spec/fixtures/rails_apps/single_stage/config/initializers/new_rails_defaults.rb +21 -0
- data/spec/fixtures/rails_apps/single_stage/config/initializers/session_store.rb +15 -0
- data/spec/fixtures/rails_apps/single_stage/config/locales/en.yml +5 -0
- data/spec/fixtures/rails_apps/single_stage/config/routes.rb +43 -0
- data/spec/fixtures/rails_apps/single_stage/db/seeds.rb +7 -0
- data/spec/fixtures/rails_apps/single_stage/doc/README_FOR_APP +2 -0
- data/spec/fixtures/rails_apps/single_stage/log/development.log +0 -0
- data/spec/fixtures/rails_apps/single_stage/log/production.log +0 -0
- data/spec/fixtures/rails_apps/single_stage/log/server.log +0 -0
- data/spec/fixtures/rails_apps/single_stage/log/test.log +0 -0
- data/spec/fixtures/rails_apps/single_stage/public/404.html +30 -0
- data/spec/fixtures/rails_apps/single_stage/public/422.html +30 -0
- data/spec/fixtures/rails_apps/single_stage/public/500.html +30 -0
- data/spec/fixtures/rails_apps/single_stage/public/favicon.ico +0 -0
- data/spec/fixtures/rails_apps/single_stage/public/images/rails.png +0 -0
- data/spec/fixtures/rails_apps/single_stage/public/index.html +275 -0
- data/spec/fixtures/rails_apps/single_stage/public/javascripts/application.js +2 -0
- data/spec/fixtures/rails_apps/single_stage/public/javascripts/controls.js +963 -0
- data/spec/fixtures/rails_apps/single_stage/public/javascripts/dragdrop.js +973 -0
- data/spec/fixtures/rails_apps/single_stage/public/javascripts/effects.js +1128 -0
- data/spec/fixtures/rails_apps/single_stage/public/javascripts/prototype.js +4320 -0
- data/spec/fixtures/rails_apps/single_stage/public/robots.txt +5 -0
- data/spec/fixtures/rails_apps/single_stage/script/about +4 -0
- data/spec/fixtures/rails_apps/single_stage/script/console +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/dbconsole +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/destroy +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/generate +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/performance/benchmarker +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/performance/profiler +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/plugin +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/runner +3 -0
- data/spec/fixtures/rails_apps/single_stage/script/server +3 -0
- data/spec/fixtures/rails_apps/single_stage/test/performance/browsing_test.rb +9 -0
- data/spec/fixtures/rails_apps/single_stage/test/test_helper.rb +38 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +8 -0
- metadata +294 -0
data/lib/captivate.rb
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
require 'tempfile'
|
|
2
|
+
module Captivate
|
|
3
|
+
class Command
|
|
4
|
+
attr_reader :path
|
|
5
|
+
|
|
6
|
+
def initialize(args = nil)
|
|
7
|
+
@args = args
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def invoke
|
|
11
|
+
config = Config.new(config_options).to_hash
|
|
12
|
+
@host = config['host']
|
|
13
|
+
@path = config['working_path']
|
|
14
|
+
@remote_dir = RemoteDir.new(:host => @host, :path => @path)
|
|
15
|
+
@remote_dir.upload('Capfile')
|
|
16
|
+
@remote_config_dir = @remote_dir.new_subdir('config')
|
|
17
|
+
@remote_config_dir.upload('config/deploy.rb')
|
|
18
|
+
@remote_config_dir.upload('config/deploy') if File.directory?('config/deploy')
|
|
19
|
+
@remote_script = RemoteScript.new(:command => command, :remote_path => @path)
|
|
20
|
+
remote_script_path = @remote_script.local_path
|
|
21
|
+
File.chmod(0555, remote_script_path)
|
|
22
|
+
@remote_dir.upload(remote_script_path, 'captivate-remote.sh')
|
|
23
|
+
SystemCall.new(%{ssh -t #{@host} screen #{@path}/captivate-remote.sh})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def self.remote_script_path
|
|
29
|
+
File.join(File.dirname(__FILE__), '/../bin/captivate-remote.sh')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def command
|
|
33
|
+
@args * ' '
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def config_options
|
|
37
|
+
{:command => command, :rails_root => rails_root}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def rails_root
|
|
41
|
+
FileUtils.pwd
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Config
|
|
46
|
+
def initialize(options = {})
|
|
47
|
+
@command = options[:command] or raise(ConfigError, "Missing 'command' option")
|
|
48
|
+
@rails_root = options[:rails_root] or raise(ConfigError, "Missing 'rails_root' option")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_hash
|
|
52
|
+
validate_yaml_config
|
|
53
|
+
{ 'working_path' => working_path }.merge(yaml_config[environment])
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def working_path
|
|
59
|
+
'/tmp/captivate.%s' % [timestamp, app_name, environment, user].join('.')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def timestamp
|
|
63
|
+
Time.now.to_i
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def app_name
|
|
67
|
+
File.basename(@rails_root)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def environment
|
|
71
|
+
if config_stages.include?(commands.first) && multistage_stages.include?(commands.first)
|
|
72
|
+
commands.first
|
|
73
|
+
else
|
|
74
|
+
'production'
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def config_stages
|
|
79
|
+
yaml_config.keys
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def multistage_stages
|
|
83
|
+
if multistage?
|
|
84
|
+
set_stages_code = deploy_file_contents[/set :stages,\s?([^\n]+)\n/, 1] or
|
|
85
|
+
raise(ConfigError, "'set :stages' definition not found")
|
|
86
|
+
stages_array = eval(set_stages_code)
|
|
87
|
+
else
|
|
88
|
+
[]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def deploy_file_contents
|
|
93
|
+
File.read(deploy_file_path)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def deploy_file_path
|
|
97
|
+
"#{@rails_root}/config/deploy.rb"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def multistage?
|
|
101
|
+
deploy_file_contents.match(%r(\A\s*require (['"])capistrano/ext/multistage\1)) ||
|
|
102
|
+
deploy_file_contents.match(%r(\n\s*require (['"])capistrano/ext/multistage\1))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def commands
|
|
106
|
+
@command.split(/\s+/)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def user
|
|
110
|
+
Etc.getlogin
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def validate_yaml_config
|
|
114
|
+
raise(ConfigError, yaml_config_path) unless File.exists?(yaml_config_path)
|
|
115
|
+
raise(ConfigError, "Missing #{environment} environment") unless yaml_config.keys.include?(environment)
|
|
116
|
+
unless yaml_config[environment].keys.include?('host')
|
|
117
|
+
raise(ConfigError, "Missing 'host' for #{environment} environment")
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def yaml_config
|
|
122
|
+
@yaml_config ||= YAML.load_file(yaml_config_path)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def yaml_config_path
|
|
126
|
+
File.join(@rails_root, 'config/captivate.yml')
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class RemoteDir
|
|
131
|
+
def initialize(options)
|
|
132
|
+
@host, @path = options[:host], options[:path]
|
|
133
|
+
SystemCall.new("ssh #{@host} mkdir #{@path}")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def upload(file_path, dest_filename = file_path)
|
|
137
|
+
raise(Errno::ENOENT, file_path) unless File.exists?(file_path)
|
|
138
|
+
if File.directory?(file_path)
|
|
139
|
+
SystemCall.new("scp -r #{file_path} #{@host}:#{@path}/#{File.basename(dest_filename)}")
|
|
140
|
+
else
|
|
141
|
+
SystemCall.new("scp #{file_path} #{@host}:#{@path}/#{File.basename(dest_filename)}")
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def new_subdir(dirname)
|
|
146
|
+
self.class.new(:host => @host, :path => "#{@path}/#{dirname}")
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
class RemoteScript
|
|
151
|
+
attr_reader :local_path, :remote_path, :command
|
|
152
|
+
def initialize(options)
|
|
153
|
+
@command = options[:command]
|
|
154
|
+
@remote_path = options[:remote_path]
|
|
155
|
+
Tempfile.open('captivate_remote_script') do |f|
|
|
156
|
+
@local_path = f.path
|
|
157
|
+
f.write(converted_template)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
def converted_template
|
|
164
|
+
template.gsub('### CAP COMMANDS ###', command).gsub('### REMOTE PATH ###', remote_path)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def template
|
|
168
|
+
<<-EOS
|
|
169
|
+
#!/usr/bin/env bash
|
|
170
|
+
# Deploys app. Executed remotely from deploy host.
|
|
171
|
+
if [ -f ~/.bashrc ]; then
|
|
172
|
+
. ~/.bashrc
|
|
173
|
+
fi
|
|
174
|
+
if [ -f ~/.profile ]; then
|
|
175
|
+
. ~/.profile
|
|
176
|
+
fi
|
|
177
|
+
if [ -f ~/.bash_profile ]; then
|
|
178
|
+
. ~/.bash_profile
|
|
179
|
+
fi
|
|
180
|
+
trap 'echo "--> Aborted. Press Enter to continue..."; read; exit' ERR
|
|
181
|
+
cd ### REMOTE PATH ###
|
|
182
|
+
echo "--> Executing: "
|
|
183
|
+
echo "cap ### CAP COMMANDS ###"
|
|
184
|
+
cap ### CAP COMMANDS ###
|
|
185
|
+
echo "--> Completed. Press Enter to continue..."
|
|
186
|
+
read
|
|
187
|
+
EOS
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
class SystemCall
|
|
192
|
+
def initialize(command)
|
|
193
|
+
system(command)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
class ConfigError < StandardError
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Captivate do
|
|
4
|
+
FIXTURES_PATH = File.expand_path(File.join(File.dirname(__FILE__), '/fixtures'))
|
|
5
|
+
|
|
6
|
+
before do
|
|
7
|
+
# FIXME: FileUtils.rm_rf isn't deleting.
|
|
8
|
+
# FileUtils.rm_rf('/tmp/captivate*')
|
|
9
|
+
system('rm -fr /tmp/captivate*')
|
|
10
|
+
@file_to_appear = '/tmp/captivate-test-file'
|
|
11
|
+
@touched_file_exists = lambda { File.exists?(@file_to_appear) }
|
|
12
|
+
@command = Captivate::Command.new([%{ROLES=app COMMAND='touch #{@file_to_appear}' invoke}])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context 'when non-multistage rails app' do
|
|
16
|
+
before do
|
|
17
|
+
@rails_root = File.join(FIXTURES_PATH, 'rails_apps/single_stage')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should execute cap task and write log file" do
|
|
21
|
+
lambda do
|
|
22
|
+
FileUtils.cd(@rails_root)
|
|
23
|
+
@command.invoke
|
|
24
|
+
end.should change(@touched_file_exists, :call).from(false).to(true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should write log file" do
|
|
28
|
+
pending
|
|
29
|
+
FileUtils.cd(@rails_root)
|
|
30
|
+
@command.invoke
|
|
31
|
+
log_file_path = File.join(@command.path, 'captivate.log')
|
|
32
|
+
File.exists?(log_file_path).should be_true
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when multistage rails app' do
|
|
37
|
+
before do
|
|
38
|
+
@rails_root = File.join(FIXTURES_PATH, 'rails_apps/multi_stage')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "should fail when attempting cap task to nonexistent server" do
|
|
42
|
+
lambda do
|
|
43
|
+
FileUtils.cd(@rails_root)
|
|
44
|
+
Captivate::Command.new([%{ROLES=nonexistent COMMAND='touch #{@file_to_appear}' invoke}]).invoke
|
|
45
|
+
end.should_not raise_error
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should execute cap task via screen" do
|
|
49
|
+
lambda do
|
|
50
|
+
FileUtils.cd(@rails_root)
|
|
51
|
+
Captivate::Command.new([%{staging ROLES=app COMMAND='touch #{@file_to_appear}' invoke}]).invoke
|
|
52
|
+
end.should change(@touched_file_exists, :call).from(false).to(true)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Captivate::Command do
|
|
4
|
+
before do
|
|
5
|
+
Captivate::Config.stub!(:new).and_return('host' => 'the_host', 'working_path' => 'the_path')
|
|
6
|
+
@remote_dir = mock('remote_dir')
|
|
7
|
+
@remote_dir.stub!(:upload)
|
|
8
|
+
@remote_subdir = mock('subdir')
|
|
9
|
+
@remote_subdir.stub!(:upload)
|
|
10
|
+
@remote_dir.stub!(:new_subdir).and_return(@remote_subdir)
|
|
11
|
+
Captivate::RemoteDir.stub!(:new).and_return(@remote_dir)
|
|
12
|
+
Captivate::SystemCall.stub!(:new)
|
|
13
|
+
@remote_script = mock('remote_script')
|
|
14
|
+
@remote_script.stub!(:local_path)
|
|
15
|
+
Captivate::RemoteScript.stub!(:new).and_return(@remote_script)
|
|
16
|
+
@command = Captivate::Command.new(['some cap commands'])
|
|
17
|
+
File.stub!(:chmod)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should pass rails_root and command to config" do
|
|
21
|
+
FileUtils.stub!(:pwd).and_return('path_to_my_app')
|
|
22
|
+
Captivate::Config.should_receive(:new).with(:command => 'some cap commands', :rails_root => 'path_to_my_app')
|
|
23
|
+
@command.invoke
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should create a remote working directory" do
|
|
27
|
+
Captivate::Config.should_receive(:new).and_return('host' => 'the_host', 'working_path' => 'the_path')
|
|
28
|
+
Captivate::RemoteDir.should_receive(:new).with(:host => 'the_host', :path => 'the_path').and_return(@remote_dir)
|
|
29
|
+
@command.invoke
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should upload Capfile" do
|
|
33
|
+
@remote_dir.should_receive(:upload).with('Capfile')
|
|
34
|
+
@command.invoke
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should create a config subdirectory" do
|
|
38
|
+
@remote_dir.should_receive(:new_subdir).with('config')
|
|
39
|
+
@command.invoke
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should upload deploy.rb to config" do
|
|
43
|
+
@remote_subdir.should_receive(:upload).with('config/deploy.rb')
|
|
44
|
+
@command.invoke
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "shouldn't upload config/deploy if doesn't exist" do
|
|
48
|
+
File.should_receive(:directory?).with('config/deploy').and_return(false)
|
|
49
|
+
@remote_subdir.should_not_receive(:upload).with('config/deploy')
|
|
50
|
+
@command.invoke
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should upload config/deploy if exists" do
|
|
54
|
+
File.should_receive(:directory?).with('config/deploy').and_return(true)
|
|
55
|
+
@remote_subdir.should_receive(:upload).with('config/deploy')
|
|
56
|
+
@command.invoke
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should write remote script locally" do
|
|
60
|
+
Captivate::RemoteScript.should_receive(:new).with(:command => 'some cap commands', :remote_path => 'the_path')
|
|
61
|
+
@command.invoke
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should upload remote script" do
|
|
65
|
+
@remote_script.should_receive(:local_path).and_return('path_to_local_script')
|
|
66
|
+
@remote_dir.should_receive(:upload).with('path_to_local_script', 'captivate-remote.sh')
|
|
67
|
+
@command.invoke
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should invoke remote script" do
|
|
71
|
+
Captivate::SystemCall.should_receive(:new).with(%{ssh -t the_host screen the_path/captivate-remote.sh})
|
|
72
|
+
@command.invoke
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "should make remote script executable" do
|
|
76
|
+
@remote_script.should_receive(:local_path).and_return('some_path')
|
|
77
|
+
File.should_receive(:chmod).with(0555, 'some_path')
|
|
78
|
+
@command.invoke
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe Captivate::Config do
|
|
83
|
+
before do
|
|
84
|
+
@rails_root = 'path/to/my_app'
|
|
85
|
+
@config = Captivate::Config.new(:command => 'some cap commands', :rails_root => @rails_root)
|
|
86
|
+
@yaml_config_path = 'path/to/my_app/config/captivate.yml'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should raise an error when 'command' not passed in" do
|
|
90
|
+
lambda do
|
|
91
|
+
Captivate::Config.new(:rails_root => 'the_path').invoke.should
|
|
92
|
+
end.should raise_error(Captivate::ConfigError, "Missing 'command' option")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should raise an error when 'rails_root' not passed in" do
|
|
96
|
+
lambda do
|
|
97
|
+
Captivate::Config.new(:command => 'the_command').invoke.should
|
|
98
|
+
end.should raise_error(Captivate::ConfigError, "Missing 'rails_root' option")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context 'without YAML' do
|
|
102
|
+
it "should raise error" do
|
|
103
|
+
lambda { @config.to_hash }.should raise_error(Captivate::ConfigError, @yaml_config_path)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
context 'with YAML' do
|
|
108
|
+
before do
|
|
109
|
+
File.should_receive(:exists?).with(@yaml_config_path).and_return(true)
|
|
110
|
+
@multistage_config = {
|
|
111
|
+
'production' => {'host' => 'the_host'},
|
|
112
|
+
'staging' => {'host' => 'another_host'},
|
|
113
|
+
}
|
|
114
|
+
YAML.stub!(:load_file).with(@yaml_config_path).and_return(@multistage_config)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should return a hash" do
|
|
118
|
+
@config.to_hash.should be_a(Hash)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should retrieve host from YAML config, defaulting to production" do
|
|
122
|
+
@config.to_hash['host'].should == 'the_host'
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should not accept non-production stages if multistage gem not present" do
|
|
126
|
+
@config = Captivate::Config.new(:command => 'staging some cap commands', :rails_root => @rails_root)
|
|
127
|
+
@config.stub!(:deploy_file_contents).and_return('deploy_config')
|
|
128
|
+
@config.to_hash['host'].should == 'the_host'
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "should raise error if multistage gem present but stages not found" do
|
|
132
|
+
@config = Captivate::Config.new(:command => 'staging some cap commands', :rails_root => @rails_root)
|
|
133
|
+
@config.stub!(:deploy_file_contents).and_return(<<-EOS)
|
|
134
|
+
require 'capistrano/ext/multistage'
|
|
135
|
+
some cap commands
|
|
136
|
+
EOS
|
|
137
|
+
lambda { @config.to_hash }.should raise_error(Captivate::ConfigError, "'set :stages' definition not found")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "should detect valid stage as first parameter if valid stage and multistage gem present" do
|
|
141
|
+
@config = Captivate::Config.new(:command => 'staging some cap commands', :rails_root => @rails_root)
|
|
142
|
+
@config.stub!(:deploy_file_contents).and_return(<<-EOS)
|
|
143
|
+
require 'capistrano/ext/multistage'
|
|
144
|
+
set :stages, %w(production staging showcase)
|
|
145
|
+
some cap commands
|
|
146
|
+
EOS
|
|
147
|
+
@config.to_hash['host'].should == 'another_host'
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "should detect valid stage as first parameter if valid stage and multistage gem present but commented out" do
|
|
151
|
+
@config = Captivate::Config.new(:command => 'staging some cap commands', :rails_root => @rails_root)
|
|
152
|
+
@config.stub!(:deploy_file_contents).and_return(<<-EOS)
|
|
153
|
+
# require 'capistrano/ext/multistage'
|
|
154
|
+
some cap commands
|
|
155
|
+
EOS
|
|
156
|
+
@config.to_hash['host'].should == 'the_host'
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Not defined in YAML config (@multistage_config['showcase'])
|
|
160
|
+
it "should ignore invalid stage as first parameter" do
|
|
161
|
+
@config = Captivate::Config.new(:command => 'showcase some cap commands', :rails_root => @rails_root)
|
|
162
|
+
@config.to_hash['host'].should == 'the_host'
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should contain timestamp, app_name, environment, user in remote working directory name" do
|
|
166
|
+
Etc.stub!(:getlogin).and_return('the_user')
|
|
167
|
+
@config.to_hash['working_path'].should == "/tmp/captivate.#{Time.now.to_i}.my_app.production.the_user"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "should merge YAML config" do
|
|
171
|
+
@config.stub!(:yaml_config).and_return('production' => {'host' => 'the_host', 'working_path' => 'custom_path'})
|
|
172
|
+
@config.to_hash['working_path'].should == 'custom_path'
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "should raise an error if missing config for environment" do
|
|
176
|
+
YAML.stub!(:load_file).with(@yaml_config_path).and_return({})
|
|
177
|
+
lambda do
|
|
178
|
+
@config.to_hash
|
|
179
|
+
end.should raise_error(Captivate::ConfigError, 'Missing production environment')
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "should raise an error if host missing from config" do
|
|
183
|
+
YAML.stub!(:load_file).with(@yaml_config_path).and_return('production' => {})
|
|
184
|
+
lambda do
|
|
185
|
+
@config.to_hash
|
|
186
|
+
end.should raise_error(Captivate::ConfigError, "Missing 'host' for production environment")
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe Captivate::RemoteDir do
|
|
192
|
+
before do
|
|
193
|
+
Captivate::SystemCall.stub!(:new)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "should create a directory" do
|
|
197
|
+
Captivate::SystemCall.should_receive(:new).with('ssh the_host mkdir the_path')
|
|
198
|
+
Captivate::RemoteDir.new(:host => 'the_host', :path => 'the_path')
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it "should raise an error if missing file" do
|
|
202
|
+
@remote_dir = Captivate::RemoteDir.new(:host => 'the_host', :path => 'the_path')
|
|
203
|
+
lambda do
|
|
204
|
+
@remote_dir.upload('local_path/a_file')
|
|
205
|
+
end.should raise_error(Errno::ENOENT, 'No such file or directory - local_path/a_file')
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "should create a subdirectory" do
|
|
209
|
+
@remote_dir = Captivate::RemoteDir.new(:host => 'the_host', :path => 'the_path')
|
|
210
|
+
Captivate::RemoteDir.should_receive(:new).with(:host => 'the_host', :path => 'the_path/a_dir')
|
|
211
|
+
@remote_dir.new_subdir('a_dir')
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context 'when uploading file which exists' do
|
|
215
|
+
before do
|
|
216
|
+
File.stub!(:exists?).and_return(true)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "should upload a file" do
|
|
220
|
+
@remote_dir = Captivate::RemoteDir.new(:host => 'the_host', :path => 'the_path')
|
|
221
|
+
Captivate::SystemCall.should_receive(:new).with('scp local_path/a_file the_host:the_path/a_file')
|
|
222
|
+
@remote_dir.upload('local_path/a_file')
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "should upload a file and rename" do
|
|
226
|
+
@remote_dir = Captivate::RemoteDir.new(:host => 'the_host', :path => 'the_path')
|
|
227
|
+
Captivate::SystemCall.should_receive(:new).with('scp a_file the_host:the_path/new_name')
|
|
228
|
+
@remote_dir.upload('a_file', 'new_name')
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
it "should upload a directory" do
|
|
232
|
+
@remote_dir = Captivate::RemoteDir.new(:host => 'the_host', :path => 'the_path')
|
|
233
|
+
Captivate::SystemCall.should_receive(:new).with('scp -r local_path/a_dir the_host:the_path/a_dir')
|
|
234
|
+
File.should_receive(:directory?).with('local_path/a_dir').and_return(true)
|
|
235
|
+
@remote_dir.upload('local_path/a_dir')
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
describe Captivate::RemoteScript do
|
|
241
|
+
before do
|
|
242
|
+
@file = mock('file')
|
|
243
|
+
@file.stub!(:path)
|
|
244
|
+
@file.stub!(:write)
|
|
245
|
+
Tempfile.should_receive(:open).and_yield(@file)
|
|
246
|
+
@remote_script = lambda do
|
|
247
|
+
Captivate::RemoteScript.new(:command => 'some cap commands', :remote_path => 'the_remote_path')
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
it "should contain cap command in script" do
|
|
252
|
+
@file.should_receive(:write) do |shell_script|
|
|
253
|
+
shell_script.should include('some cap commands')
|
|
254
|
+
end
|
|
255
|
+
@remote_script.call
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
it "should contain remote script path in script" do
|
|
259
|
+
@file.should_receive(:write) do |shell_script|
|
|
260
|
+
shell_script.should include('the_remote_path')
|
|
261
|
+
end
|
|
262
|
+
@remote_script.call
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "should return path" do
|
|
266
|
+
@file.should_receive(:path).and_return('some_path')
|
|
267
|
+
@remote_script.call
|
|
268
|
+
end
|
|
269
|
+
end
|