engineyard-serverside 1.3.7 → 1.4.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/bin/engineyard-serverside +1 -1
- data/lib/engineyard-serverside.rb +37 -33
- data/lib/engineyard-serverside/bundle_installer.rb +3 -1
- data/lib/engineyard-serverside/cli.rb +196 -194
- data/lib/engineyard-serverside/configuration.rb +109 -107
- data/lib/engineyard-serverside/deploy.rb +273 -271
- data/lib/engineyard-serverside/deploy_hook.rb +57 -55
- data/lib/engineyard-serverside/deprecation.rb +27 -0
- data/lib/engineyard-serverside/lockfile_parser.rb +80 -78
- data/lib/engineyard-serverside/logged_output.rb +56 -54
- data/lib/engineyard-serverside/server.rb +67 -64
- data/lib/engineyard-serverside/strategies/git.rb +110 -108
- data/lib/engineyard-serverside/task.rb +48 -45
- data/lib/engineyard-serverside/version.rb +3 -1
- data/spec/custom_deploy_spec.rb +5 -5
- data/spec/deploy_hook_spec.rb +3 -3
- data/spec/deprecation_spec.rb +25 -0
- data/spec/git_strategy_spec.rb +1 -1
- data/spec/lockfile_parser_spec.rb +4 -4
- data/spec/real_deploy_spec.rb +13 -7
- data/spec/restart_spec.rb +4 -4
- data/spec/server_spec.rb +24 -24
- data/spec/spec_helper.rb +15 -13
- metadata +8 -5
@@ -2,144 +2,146 @@ require 'json'
|
|
2
2
|
require 'thor'
|
3
3
|
|
4
4
|
module EY
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
module Serverside
|
6
|
+
class Deploy::Configuration
|
7
|
+
DEFAULT_CONFIG = Thor::CoreExt::HashWithIndifferentAccess.new({
|
8
|
+
"branch" => "master",
|
9
|
+
"strategy" => "Git",
|
10
|
+
})
|
11
|
+
|
12
|
+
attr_reader :configuration
|
13
|
+
alias :c :configuration
|
14
|
+
|
15
|
+
attr_writer :release_path
|
16
|
+
|
17
|
+
def initialize(opts={})
|
18
|
+
@release_path = opts[:release_path]
|
19
|
+
config = JSON.parse(opts["config"] || "{}")
|
20
|
+
@configuration = DEFAULT_CONFIG.merge(config).merge(opts)
|
21
|
+
end
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
23
|
+
# Delegate to the configuration objects
|
24
|
+
def method_missing(meth, *args, &blk)
|
25
|
+
c.key?(meth.to_s) ? c[meth.to_s] : super
|
26
|
+
end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
28
|
+
def respond_to?(meth, include_private=false)
|
29
|
+
c.key?(meth.to_s) ? true : super
|
30
|
+
end
|
26
31
|
|
27
|
-
|
28
|
-
|
29
|
-
|
32
|
+
def [](key)
|
33
|
+
if respond_to?(key.to_sym)
|
34
|
+
send(key.to_sym)
|
35
|
+
else
|
36
|
+
c[key]
|
37
|
+
end
|
38
|
+
end
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
def has_key?(key)
|
41
|
+
if respond_to?(key.to_sym)
|
42
|
+
true
|
43
|
+
else
|
44
|
+
c.has_key?(key)
|
45
|
+
end
|
36
46
|
end
|
37
|
-
end
|
38
47
|
|
39
|
-
|
40
|
-
|
41
|
-
true
|
42
|
-
else
|
43
|
-
c.has_key?(key)
|
48
|
+
def to_json
|
49
|
+
configuration.to_json
|
44
50
|
end
|
45
|
-
end
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
def node
|
53
|
+
EY::Serverside.node
|
54
|
+
end
|
50
55
|
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
def revision
|
57
|
+
IO.read(File.join(latest_release, 'REVISION'))
|
58
|
+
end
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
def repository_cache
|
61
|
+
configuration['repository_cache'] || File.join(deploy_to, "/shared/cached-copy")
|
62
|
+
end
|
58
63
|
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
def deploy_to
|
65
|
+
configuration['deploy_to'] || "/data/#{app}"
|
66
|
+
end
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
|
68
|
+
def migrate?
|
69
|
+
!!configuration['migrate']
|
70
|
+
end
|
66
71
|
|
67
|
-
|
68
|
-
|
69
|
-
|
72
|
+
def migration_command
|
73
|
+
configuration['migrate'] == "migrate" ? DEFAULT_CONFIG["migrate"] : configuration['migrate']
|
74
|
+
end
|
70
75
|
|
71
|
-
|
72
|
-
|
73
|
-
|
76
|
+
def user
|
77
|
+
configuration['user'] || ENV['USER']
|
78
|
+
end
|
74
79
|
|
75
|
-
|
76
|
-
|
77
|
-
|
80
|
+
def group
|
81
|
+
configuration['group'] || user
|
82
|
+
end
|
78
83
|
|
79
|
-
|
80
|
-
|
81
|
-
|
84
|
+
def role
|
85
|
+
node['instance_role']
|
86
|
+
end
|
82
87
|
|
83
|
-
|
84
|
-
|
85
|
-
|
88
|
+
def current_role
|
89
|
+
current_roles.first
|
90
|
+
end
|
86
91
|
|
87
|
-
|
88
|
-
|
89
|
-
|
92
|
+
def copy_exclude
|
93
|
+
@copy_exclude ||= Array(configuration.fetch("copy_exclude", []))
|
94
|
+
end
|
90
95
|
|
91
|
-
|
92
|
-
|
93
|
-
|
96
|
+
def environment
|
97
|
+
configuration['framework_env']
|
98
|
+
end
|
94
99
|
|
95
|
-
|
96
|
-
|
97
|
-
|
100
|
+
def latest_release
|
101
|
+
all_releases.last
|
102
|
+
end
|
98
103
|
|
99
|
-
|
100
|
-
|
101
|
-
|
104
|
+
def previous_release(current=latest_release)
|
105
|
+
index = all_releases.index(current)
|
106
|
+
all_releases[index-1]
|
107
|
+
end
|
102
108
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
109
|
+
def oldest_release
|
110
|
+
all_releases.first
|
111
|
+
end
|
107
112
|
|
108
|
-
|
109
|
-
|
110
|
-
|
113
|
+
def all_releases
|
114
|
+
Dir.glob("#{release_dir}/*").sort
|
115
|
+
end
|
111
116
|
|
112
|
-
|
113
|
-
|
114
|
-
|
117
|
+
def binstubs_path
|
118
|
+
release_path + '/ey_bundler_binstubs'
|
119
|
+
end
|
115
120
|
|
116
|
-
|
117
|
-
|
118
|
-
|
121
|
+
def framework_envs
|
122
|
+
"RAILS_ENV=#{environment} RACK_ENV=#{environment} MERB_ENV=#{environment}"
|
123
|
+
end
|
119
124
|
|
120
|
-
|
121
|
-
|
122
|
-
|
125
|
+
def current_path
|
126
|
+
File.join(deploy_to, "current")
|
127
|
+
end
|
123
128
|
|
124
|
-
|
125
|
-
|
126
|
-
|
129
|
+
def shared_path
|
130
|
+
File.join(deploy_to, "shared")
|
131
|
+
end
|
127
132
|
|
128
|
-
|
129
|
-
|
130
|
-
|
133
|
+
def release_dir
|
134
|
+
File.join(deploy_to, "releases")
|
135
|
+
end
|
131
136
|
|
132
|
-
|
133
|
-
|
134
|
-
|
137
|
+
def release_path
|
138
|
+
@release_path ||= File.join(release_dir, Time.now.utc.strftime("%Y%m%d%H%M%S"))
|
139
|
+
end
|
135
140
|
|
136
|
-
|
137
|
-
|
138
|
-
|
141
|
+
def exclusions
|
142
|
+
copy_exclude.map { |e| %|--exclude="#{e}"| }.join(' ')
|
143
|
+
end
|
139
144
|
|
140
|
-
def exclusions
|
141
|
-
copy_exclude.map { |e| %|--exclude="#{e}"| }.join(' ')
|
142
145
|
end
|
143
|
-
|
144
146
|
end
|
145
147
|
end
|
@@ -4,337 +4,339 @@ require 'fileutils'
|
|
4
4
|
require 'json'
|
5
5
|
|
6
6
|
module EY
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
module Serverside
|
8
|
+
class DeployBase < Task
|
9
|
+
include LoggedOutput
|
10
|
+
|
11
|
+
# default task
|
12
|
+
def deploy
|
13
|
+
debug "Starting deploy at #{Time.now.asctime}"
|
14
|
+
update_repository_cache
|
15
|
+
cached_deploy
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
def cached_deploy
|
19
|
+
debug "Deploying app from cached copy at #{Time.now.asctime}"
|
20
|
+
require_custom_tasks
|
21
|
+
push_code
|
22
|
+
|
23
|
+
info "~> Starting full deploy"
|
24
|
+
copy_repository_cache
|
25
|
+
|
26
|
+
with_failed_release_cleanup do
|
27
|
+
create_revision_file
|
28
|
+
run_with_callbacks(:bundle)
|
29
|
+
symlink_configs
|
30
|
+
conditionally_enable_maintenance_page
|
31
|
+
run_with_callbacks(:migrate)
|
32
|
+
callback(:before_symlink)
|
33
|
+
symlink
|
34
|
+
end
|
21
35
|
|
22
|
-
|
23
|
-
|
36
|
+
callback(:after_symlink)
|
37
|
+
run_with_callbacks(:restart)
|
38
|
+
disable_maintenance_page
|
39
|
+
|
40
|
+
cleanup_old_releases
|
41
|
+
debug "Finished deploy at #{Time.now.asctime}"
|
42
|
+
rescue Exception
|
43
|
+
debug "Finished failing to deploy at #{Time.now.asctime}"
|
44
|
+
puts_deploy_failure
|
45
|
+
raise
|
46
|
+
end
|
24
47
|
|
25
|
-
|
26
|
-
|
27
|
-
run_with_callbacks(:bundle)
|
28
|
-
symlink_configs
|
48
|
+
def restart_with_maintenance_page
|
49
|
+
require_custom_tasks
|
29
50
|
conditionally_enable_maintenance_page
|
30
|
-
|
31
|
-
|
32
|
-
symlink
|
51
|
+
restart
|
52
|
+
disable_maintenance_page
|
33
53
|
end
|
34
54
|
|
35
|
-
|
36
|
-
|
37
|
-
|
55
|
+
def enable_maintenance_page
|
56
|
+
maintenance_page_candidates = [
|
57
|
+
"public/maintenance.html.custom",
|
58
|
+
"public/maintenance.html.tmp",
|
59
|
+
"public/maintenance.html",
|
60
|
+
"public/system/maintenance.html.default",
|
61
|
+
].map do |file|
|
62
|
+
File.join(c.latest_release, file)
|
63
|
+
end
|
38
64
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
raise
|
45
|
-
end
|
65
|
+
# this one is guaranteed to exist
|
66
|
+
maintenance_page_candidates << File.expand_path(
|
67
|
+
"default_maintenance_page.html",
|
68
|
+
File.dirname(__FILE__)
|
69
|
+
)
|
46
70
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
disable_maintenance_page
|
52
|
-
end
|
71
|
+
# put in the maintenance page
|
72
|
+
maintenance_file = maintenance_page_candidates.detect do |file|
|
73
|
+
File.exists?(file)
|
74
|
+
end
|
53
75
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
File.join(c.latest_release, file)
|
76
|
+
@maintenance_up = true
|
77
|
+
roles :app_master, :app, :solo do
|
78
|
+
maint_page_dir = File.join(c.shared_path, "system")
|
79
|
+
visible_maint_page = File.join(maint_page_dir, "maintenance.html")
|
80
|
+
run Escape.shell_command(['mkdir', '-p', maint_page_dir])
|
81
|
+
run Escape.shell_command(['cp', maintenance_file, visible_maint_page])
|
82
|
+
end
|
62
83
|
end
|
63
84
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
)
|
69
|
-
|
70
|
-
# put in the maintenance page
|
71
|
-
maintenance_file = maintenance_page_candidates.detect do |file|
|
72
|
-
File.exists?(file)
|
85
|
+
def conditionally_enable_maintenance_page
|
86
|
+
if c.migrate? || required_downtime_stack?
|
87
|
+
enable_maintenance_page
|
88
|
+
end
|
73
89
|
end
|
74
90
|
|
75
|
-
|
76
|
-
|
77
|
-
maint_page_dir = File.join(c.shared_path, "system")
|
78
|
-
visible_maint_page = File.join(maint_page_dir, "maintenance.html")
|
79
|
-
run Escape.shell_command(['mkdir', '-p', maint_page_dir])
|
80
|
-
run Escape.shell_command(['cp', maintenance_file, visible_maint_page])
|
91
|
+
def required_downtime_stack?
|
92
|
+
%w[ nginx_mongrel glassfish ].include? c.stack
|
81
93
|
end
|
82
|
-
end
|
83
94
|
|
84
|
-
|
85
|
-
|
86
|
-
|
95
|
+
def disable_maintenance_page
|
96
|
+
@maintenance_up = false
|
97
|
+
roles :app_master, :app, :solo do
|
98
|
+
run "rm -f #{File.join(c.shared_path, "system", "maintenance.html")}"
|
99
|
+
end
|
87
100
|
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def required_downtime_stack?
|
91
|
-
%w[ nginx_mongrel glassfish ].include? c.stack
|
92
|
-
end
|
93
101
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
102
|
+
def run_with_callbacks(task)
|
103
|
+
callback("before_#{task}")
|
104
|
+
send(task)
|
105
|
+
callback("after_#{task}")
|
98
106
|
end
|
99
|
-
end
|
100
107
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
108
|
+
# task
|
109
|
+
def push_code
|
110
|
+
info "~> Pushing code to all servers"
|
111
|
+
barrier *(EY::Serverside::Server.all.map do |server|
|
112
|
+
need_later { server.sync_directory(config.repository_cache) }
|
113
|
+
end)
|
114
|
+
end
|
106
115
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
116
|
+
# task
|
117
|
+
def restart
|
118
|
+
@restart_failed = true
|
119
|
+
info "~> Restarting app servers"
|
120
|
+
roles :app_master, :app, :solo do
|
121
|
+
run(restart_command)
|
122
|
+
end
|
123
|
+
@restart_failed = false
|
124
|
+
end
|
114
125
|
|
115
|
-
|
116
|
-
|
117
|
-
@restart_failed = true
|
118
|
-
info "~> Restarting app servers"
|
119
|
-
roles :app_master, :app, :solo do
|
120
|
-
run(restart_command)
|
126
|
+
def restart_command
|
127
|
+
"/engineyard/bin/app_#{c.app} deploy"
|
121
128
|
end
|
122
|
-
@restart_failed = false
|
123
|
-
end
|
124
129
|
|
125
|
-
|
126
|
-
|
127
|
-
|
130
|
+
# task
|
131
|
+
def bundle
|
132
|
+
if File.exist?("#{c.release_path}/Gemfile")
|
133
|
+
info "~> Gemfile detected, bundling gems"
|
134
|
+
lockfile = File.join(c.release_path, "Gemfile.lock")
|
128
135
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
136
|
+
bundler_installer = if File.exist?(lockfile)
|
137
|
+
get_bundler_installer(lockfile)
|
138
|
+
else
|
139
|
+
warn_about_missing_lockfile
|
140
|
+
bundler_09_installer(default_09_bundler)
|
141
|
+
end
|
134
142
|
|
135
|
-
|
136
|
-
get_bundler_installer(lockfile)
|
137
|
-
else
|
138
|
-
warn_about_missing_lockfile
|
139
|
-
bundler_09_installer(default_09_bundler)
|
140
|
-
end
|
143
|
+
sudo "#{$0} _#{EY::Serverside::VERSION}_ install_bundler #{bundler_installer.version}"
|
141
144
|
|
142
|
-
|
145
|
+
run "cd #{c.release_path} && bundle _#{bundler_installer.version}_ install #{bundler_installer.options}"
|
146
|
+
end
|
147
|
+
end
|
143
148
|
|
144
|
-
|
149
|
+
# task
|
150
|
+
def cleanup_old_releases
|
151
|
+
@cleanup_failed = true
|
152
|
+
info "~> Cleaning up old releases"
|
153
|
+
sudo "ls #{c.release_dir} | head -n -3 | xargs -I{} rm -rf #{c.release_dir}/{}"
|
154
|
+
@cleanup_failed = false
|
145
155
|
end
|
146
|
-
end
|
147
156
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
@cleanup_failed = false
|
154
|
-
end
|
157
|
+
# task
|
158
|
+
def rollback
|
159
|
+
if c.all_releases.size > 1
|
160
|
+
rolled_back_release = c.latest_release
|
161
|
+
c.release_path = c.previous_release(rolled_back_release)
|
155
162
|
|
156
|
-
|
157
|
-
|
158
|
-
if c.all_releases.size > 1
|
159
|
-
rolled_back_release = c.latest_release
|
160
|
-
c.release_path = c.previous_release(rolled_back_release)
|
161
|
-
|
162
|
-
revision = File.read(File.join(c.release_path, 'REVISION')).strip
|
163
|
-
info "~> Rolling back to previous release: #{short_log_message(revision)}"
|
164
|
-
|
165
|
-
run_with_callbacks(:symlink)
|
166
|
-
sudo "rm -rf #{rolled_back_release}"
|
167
|
-
bundle
|
168
|
-
info "~> Restarting with previous release"
|
169
|
-
with_maintenance_page { run_with_callbacks(:restart) }
|
170
|
-
else
|
171
|
-
info "~> Already at oldest release, nothing to roll back to"
|
172
|
-
exit(1)
|
173
|
-
end
|
174
|
-
end
|
163
|
+
revision = File.read(File.join(c.release_path, 'REVISION')).strip
|
164
|
+
info "~> Rolling back to previous release: #{short_log_message(revision)}"
|
175
165
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
166
|
+
run_with_callbacks(:symlink)
|
167
|
+
sudo "rm -rf #{rolled_back_release}"
|
168
|
+
bundle
|
169
|
+
info "~> Restarting with previous release"
|
170
|
+
with_maintenance_page { run_with_callbacks(:restart) }
|
171
|
+
else
|
172
|
+
info "~> Already at oldest release, nothing to roll back to"
|
173
|
+
exit(1)
|
174
|
+
end
|
184
175
|
end
|
185
|
-
end
|
186
176
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
177
|
+
# task
|
178
|
+
def migrate
|
179
|
+
return unless c.migrate?
|
180
|
+
@migrations_reached = true
|
181
|
+
roles :app_master, :solo do
|
182
|
+
cmd = "cd #{c.release_path} && PATH=#{c.binstubs_path}:$PATH #{c.framework_envs} #{c.migration_command}"
|
183
|
+
info "~> Migrating: #{cmd}"
|
184
|
+
run(cmd)
|
185
|
+
end
|
186
|
+
end
|
191
187
|
|
192
|
-
|
193
|
-
|
194
|
-
|
188
|
+
# task
|
189
|
+
def copy_repository_cache
|
190
|
+
info "~> Copying to #{c.release_path}"
|
191
|
+
run("mkdir -p #{c.release_path} && rsync -aq #{c.exclusions} #{c.repository_cache}/ #{c.release_path}")
|
195
192
|
|
196
|
-
|
197
|
-
|
198
|
-
|
193
|
+
info "~> Ensuring proper ownership"
|
194
|
+
sudo("chown -R #{c.user}:#{c.group} #{c.deploy_to}")
|
195
|
+
end
|
199
196
|
|
200
|
-
|
201
|
-
|
202
|
-
[ "chmod -R g+w #{release_to_link}",
|
203
|
-
"rm -rf #{release_to_link}/log #{release_to_link}/public/system #{release_to_link}/tmp/pids",
|
204
|
-
"mkdir -p #{release_to_link}/tmp",
|
205
|
-
"ln -nfs #{c.shared_path}/log #{release_to_link}/log",
|
206
|
-
"mkdir -p #{release_to_link}/public",
|
207
|
-
"mkdir -p #{release_to_link}/config",
|
208
|
-
"ln -nfs #{c.shared_path}/system #{release_to_link}/public/system",
|
209
|
-
"ln -nfs #{c.shared_path}/pids #{release_to_link}/tmp/pids",
|
210
|
-
"find #{c.shared_path}/config -type f -exec ln -s {} #{release_to_link}/config \\;",
|
211
|
-
"ln -nfs #{c.shared_path}/config/database.yml #{release_to_link}/config/database.yml",
|
212
|
-
"ln -nfs #{c.shared_path}/config/mongrel_cluster.yml #{release_to_link}/config/mongrel_cluster.yml",
|
213
|
-
].each do |cmd|
|
214
|
-
run cmd
|
197
|
+
def create_revision_file
|
198
|
+
run create_revision_file_command
|
215
199
|
end
|
216
200
|
|
217
|
-
|
218
|
-
|
219
|
-
|
201
|
+
def symlink_configs(release_to_link=c.release_path)
|
202
|
+
info "~> Symlinking configs"
|
203
|
+
[ "chmod -R g+w #{release_to_link}",
|
204
|
+
"rm -rf #{release_to_link}/log #{release_to_link}/public/system #{release_to_link}/tmp/pids",
|
205
|
+
"mkdir -p #{release_to_link}/tmp",
|
206
|
+
"ln -nfs #{c.shared_path}/log #{release_to_link}/log",
|
207
|
+
"mkdir -p #{release_to_link}/public",
|
208
|
+
"mkdir -p #{release_to_link}/config",
|
209
|
+
"ln -nfs #{c.shared_path}/system #{release_to_link}/public/system",
|
210
|
+
"ln -nfs #{c.shared_path}/pids #{release_to_link}/tmp/pids",
|
211
|
+
"find #{c.shared_path}/config -type f -exec ln -s {} #{release_to_link}/config \\;",
|
212
|
+
"ln -nfs #{c.shared_path}/config/database.yml #{release_to_link}/config/database.yml",
|
213
|
+
"ln -nfs #{c.shared_path}/config/mongrel_cluster.yml #{release_to_link}/config/mongrel_cluster.yml",
|
214
|
+
].each do |cmd|
|
215
|
+
run cmd
|
216
|
+
end
|
220
217
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
218
|
+
sudo "chown -R #{c.user}:#{c.group} #{release_to_link}"
|
219
|
+
run "if [ -f \"#{c.shared_path}/config/newrelic.yml\" ]; then ln -nfs #{c.shared_path}/config/newrelic.yml #{release_to_link}/config/newrelic.yml; fi"
|
220
|
+
end
|
221
|
+
|
222
|
+
# task
|
223
|
+
def symlink(release_to_link=c.release_path)
|
224
|
+
info "~> Symlinking code"
|
225
|
+
run "rm -f #{c.current_path} && ln -nfs #{release_to_link} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
|
226
|
+
@symlink_changed = true
|
227
|
+
rescue Exception
|
228
|
+
sudo "rm -f #{c.current_path} && ln -nfs #{c.previous_release(release_to_link)} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
|
229
|
+
@symlink_changed = false
|
230
|
+
raise
|
231
|
+
end
|
231
232
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
233
|
+
def callback(what)
|
234
|
+
@callbacks_reached ||= true
|
235
|
+
if File.exist?("#{c.release_path}/deploy/#{what}.rb")
|
236
|
+
run Escape.shell_command(base_callback_command_for(what)) do |server, cmd|
|
237
|
+
per_instance_args = [
|
238
|
+
'--current-roles', server.roles.join(' '),
|
239
|
+
'--config', c.to_json,
|
240
|
+
]
|
241
|
+
per_instance_args << '--current-name' << server.name.to_s if server.name
|
242
|
+
cmd << " " << Escape.shell_command(per_instance_args)
|
243
|
+
end
|
242
244
|
end
|
243
245
|
end
|
244
|
-
end
|
245
246
|
|
246
|
-
|
247
|
+
protected
|
247
248
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
249
|
+
def base_callback_command_for(what)
|
250
|
+
[$0, version_specifier, 'hook', what.to_s,
|
251
|
+
'--app', config.app.to_s,
|
252
|
+
'--release-path', config.release_path.to_s,
|
253
|
+
'--framework-env', c.environment.to_s,
|
254
|
+
].compact
|
255
|
+
end
|
255
256
|
|
256
|
-
|
257
|
-
|
258
|
-
|
257
|
+
def version_specifier
|
258
|
+
"_#{EY::Serverside::VERSION}_"
|
259
|
+
end
|
259
260
|
|
260
261
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
262
|
+
def puts_deploy_failure
|
263
|
+
if @cleanup_failed
|
264
|
+
info "~> [Relax] Your site is running new code, but cleaning up old deploys failed"
|
265
|
+
elsif @maintenance_up
|
266
|
+
info "~> [Attention] Maintenance page still up, consider the following before removing:"
|
267
|
+
info " * any deploy hooks ran, be careful if they were destructive" if @callbacks_reached
|
268
|
+
info " * any migrations ran, be careful if they were destructive" if @migrations_reached
|
269
|
+
if @symlink_changed
|
270
|
+
info " * your new code is symlinked as current"
|
271
|
+
else
|
272
|
+
info " * your old code is still symlinked as current"
|
273
|
+
end
|
274
|
+
info " * application servers failed to restart" if @restart_failed
|
270
275
|
else
|
271
|
-
info "
|
276
|
+
info "~> [Relax] Your site is still running old code and nothing destructive could have occurred"
|
272
277
|
end
|
273
|
-
info " * application servers failed to restart" if @restart_failed
|
274
|
-
else
|
275
|
-
info "~> [Relax] Your site is still running old code and nothing destructive could have occurred"
|
276
278
|
end
|
277
|
-
end
|
278
279
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
280
|
+
def with_maintenance_page
|
281
|
+
conditionally_enable_maintenance_page
|
282
|
+
yield if block_given?
|
283
|
+
disable_maintenance_page
|
284
|
+
end
|
284
285
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
286
|
+
def with_failed_release_cleanup
|
287
|
+
yield
|
288
|
+
rescue Exception
|
289
|
+
sudo "rm -rf #{c.release_path}"
|
290
|
+
raise
|
291
|
+
end
|
291
292
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
293
|
+
def warn_about_missing_lockfile
|
294
|
+
info "!>"
|
295
|
+
info "!> WARNING: Gemfile.lock is missing!"
|
296
|
+
info "!> You can get different gems in production than what you tested with."
|
297
|
+
info "!> You can get different gems on every deployment even if your Gemfile hasn't changed."
|
298
|
+
info "!> Deploying may take a long time."
|
299
|
+
info "!>"
|
300
|
+
info "!> Fix this by running \"git add Gemfile.lock; git commit\" and deploying again."
|
301
|
+
info "!> If you don't have a Gemfile.lock, run \"bundle lock\" to create one."
|
302
|
+
info "!>"
|
303
|
+
info "!> This deployment will use bundler #{default_09_bundler} to run 'bundle install'."
|
304
|
+
info "!>"
|
305
|
+
end
|
305
306
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
307
|
+
def get_bundler_installer(lockfile)
|
308
|
+
parser = LockfileParser.new(File.read(lockfile))
|
309
|
+
case parser.lockfile_version
|
310
|
+
when :bundler09
|
311
|
+
bundler_09_installer(parser.bundler_version || default_09_bundler)
|
312
|
+
when :bundler10
|
313
|
+
bundler_10_installer(parser.bundler_version || default_10_bundler)
|
314
|
+
else
|
315
|
+
raise "Unknown lockfile version #{parser.lockfile_version}"
|
316
|
+
end
|
315
317
|
end
|
316
|
-
|
317
|
-
public :get_bundler_installer
|
318
|
+
public :get_bundler_installer
|
318
319
|
|
319
|
-
|
320
|
-
|
321
|
-
|
320
|
+
def bundler_09_installer(version)
|
321
|
+
BundleInstaller.new(version, '--without=development --without=test')
|
322
|
+
end
|
322
323
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
324
|
+
def bundler_10_installer(version)
|
325
|
+
BundleInstaller.new(version,
|
326
|
+
"--deployment --path #{c.shared_path}/bundled_gems --binstubs #{c.binstubs_path} --without development test")
|
327
|
+
end
|
327
328
|
|
328
|
-
|
329
|
-
|
329
|
+
def default_09_bundler() "0.9.26" end
|
330
|
+
def default_10_bundler() "1.0.0" end
|
330
331
|
|
331
|
-
|
332
|
+
end # DeployBase
|
332
333
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
334
|
+
class Deploy < DeployBase
|
335
|
+
def self.new(config)
|
336
|
+
# include the correct fetch strategy
|
337
|
+
include EY::Serverside::Strategies.const_get(config.strategy)::Helpers
|
338
|
+
super
|
339
|
+
end
|
338
340
|
end
|
339
341
|
|
340
342
|
end
|