engineyard-serverside 2.0.7 → 2.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/lib/engineyard-serverside.rb +0 -1
  2. data/lib/engineyard-serverside/cli.rb +44 -42
  3. data/lib/engineyard-serverside/configuration.rb +55 -4
  4. data/lib/engineyard-serverside/dependency_manager.rb +17 -0
  5. data/lib/engineyard-serverside/dependency_manager/base.rb +65 -0
  6. data/lib/engineyard-serverside/dependency_manager/bundler.rb +124 -0
  7. data/lib/engineyard-serverside/dependency_manager/bundler_lock.rb +155 -0
  8. data/lib/engineyard-serverside/dependency_manager/legacy_helpers.rb +24 -0
  9. data/lib/engineyard-serverside/dependency_manager/npm.rb +16 -0
  10. data/lib/engineyard-serverside/deploy.rb +86 -178
  11. data/lib/engineyard-serverside/deprecation.rb +11 -1
  12. data/lib/engineyard-serverside/paths.rb +6 -0
  13. data/lib/engineyard-serverside/propagator.rb +2 -2
  14. data/lib/engineyard-serverside/rails_assets.rb +152 -0
  15. data/lib/engineyard-serverside/rails_assets/strategy.rb +197 -0
  16. data/lib/engineyard-serverside/server.rb +5 -0
  17. data/lib/engineyard-serverside/servers.rb +19 -7
  18. data/lib/engineyard-serverside/shell.rb +7 -5
  19. data/lib/engineyard-serverside/shell/command_result.rb +1 -1
  20. data/lib/engineyard-serverside/strategies/git.rb +14 -4
  21. data/lib/engineyard-serverside/task.rb +1 -0
  22. data/lib/engineyard-serverside/version.rb +1 -1
  23. data/spec/bundler_deploy_spec.rb +36 -33
  24. data/spec/configuration_spec.rb +5 -4
  25. data/spec/custom_deploy_spec.rb +11 -9
  26. data/spec/deploy_hook_spec.rb +10 -3
  27. data/spec/ey_yml_customized_deploy_spec.rb +1 -1
  28. data/spec/fixtures/lockfiles/1.0-no-bundler +1 -1
  29. data/spec/fixtures/lockfiles/1.0.0.rc.1-with-bundler +1 -1
  30. data/spec/fixtures/lockfiles/1.0.18-do_mysql +1 -1
  31. data/spec/fixtures/lockfiles/1.0.18-do_postgres +1 -1
  32. data/spec/fixtures/lockfiles/1.0.18-mysql +1 -1
  33. data/spec/fixtures/lockfiles/1.0.18-mysql2 +1 -1
  34. data/spec/fixtures/lockfiles/1.0.18-pg +1 -1
  35. data/spec/fixtures/lockfiles/1.0.6-no-bundler +2 -2
  36. data/spec/fixtures/lockfiles/1.0.6-with-any-bundler +2 -2
  37. data/spec/fixtures/lockfiles/1.0.6-with-bundler +2 -2
  38. data/spec/fixtures/lockfiles/1.3.1-rails-3.2.13 +112 -0
  39. data/spec/fixtures/repos/{assets_enabled → assets_detected}/Gemfile +1 -2
  40. data/spec/fixtures/repos/{assets_enabled → assets_detected}/Gemfile.lock +1 -3
  41. data/spec/fixtures/repos/{assets_enabled → assets_detected}/README +0 -0
  42. data/spec/fixtures/repos/assets_detected/Rakefile +5 -0
  43. data/spec/fixtures/repos/{assets_enabled → assets_detected}/app/assets/empty +0 -0
  44. data/spec/fixtures/repos/{assets_enabled → assets_detected}/config/application.rb +0 -0
  45. data/spec/fixtures/repos/assets_detected/config/ey.yml +3 -0
  46. data/spec/fixtures/repos/assets_disabled/Gemfile +1 -2
  47. data/spec/fixtures/repos/assets_disabled/Gemfile.lock +1 -3
  48. data/spec/fixtures/repos/assets_disabled/Rakefile +1 -0
  49. data/spec/fixtures/repos/assets_disabled/config/ey.yml +3 -0
  50. data/spec/fixtures/repos/assets_disabled_in_ey_yml/Gemfile +1 -2
  51. data/spec/fixtures/repos/assets_disabled_in_ey_yml/Gemfile.lock +1 -3
  52. data/spec/fixtures/repos/assets_disabled_in_ey_yml/Rakefile +1 -0
  53. data/spec/fixtures/repos/assets_disabled_in_ey_yml/config/ey.yml +1 -0
  54. data/spec/fixtures/repos/assets_enabled_all/Gemfile +1 -2
  55. data/spec/fixtures/repos/assets_enabled_all/Gemfile.lock +1 -3
  56. data/spec/fixtures/repos/assets_enabled_all/Rakefile +1 -0
  57. data/spec/fixtures/repos/assets_enabled_all/config/ey.yml +1 -0
  58. data/spec/fixtures/repos/assets_enabled_in_ey_yml/Gemfile +1 -1
  59. data/spec/fixtures/repos/assets_enabled_in_ey_yml/Gemfile.lock +1 -1
  60. data/spec/fixtures/repos/assets_enabled_in_ey_yml/Rakefile +1 -0
  61. data/spec/fixtures/repos/assets_enabled_util_only/Gemfile +1 -2
  62. data/spec/fixtures/repos/assets_enabled_util_only/Gemfile.lock +1 -3
  63. data/spec/fixtures/repos/assets_enabled_util_only/Rakefile +1 -0
  64. data/spec/fixtures/repos/assets_enabled_util_only/config/ey.yml +1 -0
  65. data/spec/fixtures/repos/assets_in_hook/Gemfile +1 -2
  66. data/spec/fixtures/repos/assets_in_hook/Gemfile.lock +1 -3
  67. data/spec/fixtures/repos/assets_in_hook/config/ey.yml +3 -0
  68. data/spec/fixtures/repos/assets_in_hook/deploy/before_compile_assets.rb +1 -1
  69. data/spec/fixtures/repos/bundle_fails/Gemfile +1 -0
  70. data/spec/fixtures/repos/bundle_fails/README +1 -0
  71. data/spec/fixtures/repos/bundle_fails/deploy/after_bundle.rb +1 -0
  72. data/spec/fixtures/repos/default/Gemfile +1 -2
  73. data/spec/fixtures/repos/default/Gemfile.lock +1 -3
  74. data/spec/fixtures/repos/default/ey.yml +3 -0
  75. data/spec/fixtures/repos/ey_yml/Gemfile +1 -1
  76. data/spec/fixtures/repos/ey_yml/Gemfile.lock +1 -1
  77. data/spec/fixtures/repos/ey_yml/config/ey.yml +11 -7
  78. data/spec/fixtures/repos/ey_yml_alt/Gemfile +1 -1
  79. data/spec/fixtures/repos/ey_yml_alt/Gemfile.lock +1 -1
  80. data/spec/fixtures/repos/no_ey_config/Gemfile +1 -2
  81. data/spec/fixtures/repos/no_ey_config/Gemfile.lock +1 -3
  82. data/spec/fixtures/repos/no_ey_config/ey.yml +3 -0
  83. data/spec/fixtures/repos/no_gemfile_lock/Gemfile +1 -2
  84. data/spec/fixtures/repos/no_gemfile_lock/ey.yml +3 -0
  85. data/spec/fixtures/repos/sqlite3/Gemfile +1 -1
  86. data/spec/fixtures/repos/sqlite3/Gemfile.lock +1 -1
  87. data/spec/lockfile_parser_spec.rb +25 -11
  88. data/spec/rails31_deploy_spec.rb +46 -5
  89. data/spec/restart_spec.rb +3 -3
  90. data/spec/services_deploy_spec.rb +89 -86
  91. data/spec/shell_spec.rb +0 -8
  92. data/spec/spec_helper.rb +81 -36
  93. data/spec/sqlite3_deploy_spec.rb +4 -5
  94. data/spec/support/integration.rb +22 -37
  95. metadata +167 -154
  96. data/lib/engineyard-serverside/lockfile_parser.rb +0 -101
  97. data/lib/engineyard-serverside/rails_asset_support.rb +0 -132
  98. data/spec/fixtures/repos/assets_enabled/Rakefile +0 -5
@@ -0,0 +1,155 @@
1
+ require 'engineyard-serverside/dependency_manager/bundler'
2
+
3
+ module EY
4
+ module Serverside
5
+ module DependencyManager
6
+ class BundlerLock < Bundler
7
+ def detected?
8
+ gemfile? && lockfile?
9
+ end
10
+
11
+ def check
12
+ shell.status "Gemfile and Gemfile.lock found"
13
+
14
+ if config.check_database_adapter? && !lockfile.any_database_adapter?
15
+ shell.warning <<-WARN
16
+ Gemfile.lock does not contain a recognized database adapter.
17
+ A database-adapter gem such as pg, mysql2, mysql, or do_mysql was expected.
18
+ This can prevent applications that use PostgreSQL or MySQL from booting.
19
+
20
+ To fix, add any needed adapter to your Gemfile, bundle, commit, and redeploy.
21
+ Applications not using PostgreSQL or MySQL can safely ignore this warning by
22
+ adding ignore_database_adapter_warning: true to the application's ey.yml file
23
+ under this environment's name and adding the file to your repository.
24
+ WARN
25
+ end
26
+ end
27
+
28
+ def uses_sqlite3?
29
+ lockfile.uses_sqlite3?
30
+ end
31
+
32
+ def check_ey_config
33
+ unless lockfile.has_ey_config?
34
+ shell.warning "Gemfile.lock does not contain ey_config.\nAdd gem 'ey_config' to get access to service configuration through EY::Config."
35
+ end
36
+ end
37
+
38
+ def rails_version
39
+ lockfile.rails_version
40
+ end
41
+
42
+ private
43
+
44
+ def lockfile_path
45
+ paths.gemfile_lock
46
+ end
47
+
48
+ def lockfile
49
+ @lockfile ||= Lockfile.new(lockfile_path.read, self.class.default_version)
50
+ end
51
+
52
+ # deployment mode is not supported without a Gemfile.lock
53
+ def bundle_install_options
54
+ super + ["--deployment"]
55
+ end
56
+
57
+ def bundler_version
58
+ @bundler_version ||= lockfile.bundler_version || super
59
+ end
60
+
61
+ class Lockfile
62
+ attr_reader :bundler_version
63
+
64
+ def initialize(lockfile_contents, default = EY::Serverside::DependencyManager::Bundler.default_version)
65
+ @contents = lockfile_contents
66
+ @default = default
67
+ @default_gem_version = Gem::Version.new(@default)
68
+ parse
69
+ end
70
+
71
+ def has_ey_config?
72
+ !!@contents.index(/^\s+ey_config\s\([^\)]+\)$/)
73
+ end
74
+
75
+ def rails_version
76
+ section = dependencies_section
77
+ if section.empty?
78
+ return nil
79
+ end
80
+ result = scan_gem('rails', section)
81
+ result ? result.last : nil
82
+ end
83
+
84
+ def any_database_adapter?
85
+ any_ruby_adapter = %w[mysql2 mysql do_mysql pg do_postgres].any? do |type|
86
+ @contents.index(/^\s+#{type}\s\([^\)]+\)$/)
87
+ end
88
+
89
+ any_jruby_adapter = %w[mysql postgresql postgres].any? do |type|
90
+ @contents.index(/^\s+jdbc-#{type}\s\([^\)]+\)$/) || @contents.index(/^\s+activerecord-jdbc#{type}-adapter\s\([^\)]+\)$/)
91
+ end
92
+
93
+ any_ruby_adapter || any_jruby_adapter
94
+ end
95
+
96
+ def uses_sqlite3?
97
+ !any_database_adapter? && @contents.index(/^\s+sqlite3\s\([^\)]+\)$/)
98
+ end
99
+
100
+ def parse
101
+ parse_from_metadata ||
102
+ parse_from_dependencies ||
103
+ raise("Malformed or pre bundler-1.0.0 Gemfile.lock: #{@contents[0,50]}...")
104
+ end
105
+
106
+ def slice_section(header)
107
+ if start = @contents.index(/^#{header}/)
108
+ finish = @contents.index(/(^\S|\Z)/, start + header.length)
109
+ @contents.slice(start..finish)
110
+ else
111
+ ""
112
+ end
113
+ end
114
+
115
+ def parse_from_metadata
116
+ section = slice_section('METADATA')
117
+
118
+ if section.empty?
119
+ return nil
120
+ end
121
+
122
+ result = section.scan(/^\s*version:\s*(.*)$/).first
123
+ @bundler_version = result ? result.first : @default
124
+ end
125
+
126
+ def dependencies_section
127
+ @dependencies_section ||= slice_section('DEPENDENCIES')
128
+ end
129
+
130
+ def parse_from_dependencies
131
+ section = dependencies_section
132
+ if section.empty?
133
+ return nil
134
+ end
135
+
136
+ result = scan_gem('bundler', section)
137
+ bundler_version = result ? result.last : nil
138
+ version_qualifier = result ? result.first : nil
139
+ @bundler_version = fetch_version(version_qualifier, bundler_version)
140
+ end
141
+
142
+ def fetch_version(operator, version)
143
+ return version || @default unless operator && version
144
+ req = Gem::Requirement.new(["#{operator} #{version}"])
145
+ req.satisfied_by?(@default_gem_version) ? @default : version
146
+ end
147
+
148
+ def scan_gem(gem, dep_section)
149
+ dep_section.scan(/^\s*#{Regexp.escape(gem)}\s*\((>?=|~>)\s*([^,\)]+)/).first
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,24 @@
1
+ module EY
2
+ module Serverside
3
+ module DependencyManager
4
+ module LegacyHelpers
5
+ [
6
+ :gemfile?,
7
+ :bundler_config,
8
+ :lockfile,
9
+ :check_ruby_bundler,
10
+ :clean_bundle_on_system_version_change,
11
+ :write_system_version,
12
+ :check_node_npm,
13
+ :clean_environment,
14
+ ].each do |meth|
15
+ define_method(meth) do |*a|
16
+ if dependency_manager.respond_to?(meth)
17
+ dependency_manager.send(meth)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module EY
2
+ module Serverside
3
+ module DependencyManager
4
+ class Npm < Base
5
+ def detected?
6
+ paths.package_json.exist?
7
+ end
8
+
9
+ def install
10
+ shell.status "Installing npm packages (package.json detected)"
11
+ run "cd #{paths.active_release} && npm install"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,14 +1,15 @@
1
- # stolen wholesale from capistrano, thanks Jamis!
1
+ # Inspired by capistrano, thanks Jamis!
2
2
  require 'base64'
3
- require 'fileutils'
4
3
  require 'multi_json'
5
- require 'engineyard-serverside/rails_asset_support'
4
+ require 'engineyard-serverside/rails_assets'
6
5
  require 'engineyard-serverside/maintenance'
6
+ require 'engineyard-serverside/dependency_manager'
7
+ require 'engineyard-serverside/dependency_manager/legacy_helpers'
7
8
 
8
9
  module EY
9
10
  module Serverside
10
11
  class DeployBase < Task
11
- include ::EY::Serverside::RailsAssetSupport
12
+ include ::EY::Serverside::DependencyManager::LegacyHelpers
12
13
 
13
14
  # default task
14
15
  def deploy
@@ -31,7 +32,6 @@ module EY
31
32
  create_revision_file
32
33
  run_with_callbacks(:bundle)
33
34
  setup_services
34
- check_for_ey_config
35
35
  symlink_configs
36
36
  setup_sqlite3_if_necessary
37
37
  run_with_callbacks(:compile_assets) # defined in RailsAssetSupport
@@ -72,60 +72,12 @@ module EY
72
72
  strategy.short_log_message(revision)
73
73
  end
74
74
 
75
- def parse_configured_services
76
- result = YAML.load_file "#{paths.shared_config}/ey_services_config_deploy.yml"
77
- return {} unless result.is_a?(Hash)
78
- result
79
- rescue
80
- {}
81
- end
82
-
83
- def check_for_ey_config
84
- if gemfile? && lockfile
85
- configured_services = parse_configured_services
86
- if !configured_services.empty? && !lockfile.has_ey_config?
87
- shell.warning "Gemfile.lock does not contain ey_config. Add it to get EY::Config access to: #{configured_services.keys.join(', ')}."
88
- end
89
- end
90
- end
91
-
92
- # The nodatabase.yml file is dropped by server configuration when there is
93
- # no database in the cluster.
94
- def has_database?
95
- paths.shared_config.join('database.yml').exist? &&
96
- !paths.shared_config.join('nodatabase.yml').exist?
75
+ def check_repository
76
+ check_dependencies
97
77
  end
98
78
 
99
- def check_repository
100
- if gemfile?
101
- shell.status "Gemfile found."
102
- if lockfile
103
- shell.status "Gemfile.lock found."
104
- if !config.ignore_database_adapter_warning? && has_database? && !lockfile.any_database_adapter?
105
- shell.warning <<-WARN
106
- Gemfile.lock does not contain a recognized database adapter.
107
- A database-adapter gem such as pg, mysql2, mysql, or do_mysql was expected.
108
- This can prevent applications that use PostgreSQL or MySQL from booting.
109
-
110
- To fix, add any needed adapter to your Gemfile, bundle, commit, and redeploy.
111
- Applications not using PostgreSQL or MySQL can safely ignore this warning by
112
- adding ignore_database_adapter_warning: true to the application's ey.yml file
113
- under this environment's name and adding the file to your repository.
114
- WARN
115
- end
116
- else
117
- shell.warning <<-WARN
118
- Gemfile.lock is missing!
119
- You can get different versions of gems in production than what you tested with.
120
- You can get different versions of gems on every deployment even if your Gemfile hasn't changed.
121
- Deploying will take longer.
122
-
123
- To fix this problem, commit your Gemfile.lock to your repository and redeploy.
124
- WARN
125
- end
126
- else
127
- shell.status "No Gemfile. Deploying without bundler support."
128
- end
79
+ def check_dependencies
80
+ dependency_manager.check
129
81
  end
130
82
 
131
83
  def restart_with_maintenance_page
@@ -153,7 +105,7 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
153
105
  # task
154
106
  def push_code
155
107
  shell.status "Pushing code to all servers"
156
- servers.remote.run_on_each do |server|
108
+ servers.remote.run_on_each(shell) do |server|
157
109
  cmd = server.sync_directory_command(paths.repository_cache)
158
110
  shell.logged_system(cmd)
159
111
  end
@@ -173,9 +125,8 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
173
125
  %{LANG="en_US.UTF-8" /engineyard/bin/app_#{config.app} deploy}
174
126
  end
175
127
 
176
- # GIT_SSH needs to be defined in the environment for customers with private bundler repos in their Gemfile.
177
- def clean_environment
178
- %Q[export GIT_SSH="#{ssh_executable}" && export LANG="en_US.UTF-8" && unset RUBYOPT BUNDLE_PATH BUNDLE_FROZEN BUNDLE_WITHOUT BUNDLE_BIN BUNDLE_GEMFILE]
128
+ def ensure_git_ssh_wrapper
129
+ ENV['GIT_SSH'] = ssh_executable.to_s
179
130
  end
180
131
 
181
132
  # create ssh wrapper on all servers
@@ -206,18 +157,17 @@ chmod 0700 #{path}
206
157
  SCRIPT
207
158
  end
208
159
 
209
- # task
210
160
  def bundle
211
- roles :app_master, :app, :solo, :util do
212
- check_ruby_bundler
213
- check_node_npm
214
- end
161
+ install_dependencies
162
+ end
163
+
164
+ def install_dependencies
165
+ dependency_manager.install
215
166
  end
216
167
 
217
168
  # task
218
169
  def cleanup_old_releases
219
170
  clean_release_directory(paths.releases)
220
- clean_release_directory(paths.releases_failed)
221
171
  end
222
172
 
223
173
  # Remove all but the most-recent +count+ releases from the specified
@@ -272,39 +222,31 @@ chmod 0700 #{path}
272
222
  def copy_repository_cache
273
223
  shell.status "Copying to #{paths.active_release}"
274
224
  exclusions = Array(config.copy_exclude).map { |e| %|--exclude="#{e}"| }.join(' ')
275
- run("mkdir -p #{paths.active_release} #{paths.releases_failed} #{paths.shared_config} && rsync -aq #{exclusions} #{paths.repository_cache}/ #{paths.active_release}")
225
+ run("mkdir -p #{paths.active_release} #{paths.shared_config} && rsync -aq #{exclusions} #{paths.repository_cache}/ #{paths.active_release}")
276
226
 
277
227
  shell.status "Ensuring proper ownership."
278
- sudo("chown -R #{config.user}:#{config.group} #{paths.active_release} #{paths.releases_failed}")
228
+ ensure_ownership(paths.active_release)
279
229
  end
280
230
 
281
231
  def create_revision_file
282
232
  run create_revision_file_command
283
233
  end
284
234
 
285
- def services_command_check
286
- "which /usr/local/ey_resin/ruby/bin/ey-services-setup >/dev/null 2>&1"
287
- end
288
-
289
- def services_setup_command
290
- "/usr/local/ey_resin/ruby/bin/ey-services-setup #{config.app}"
291
- end
292
-
293
235
  def setup_services
294
236
  shell.status "Setting up external services."
295
- previously_configured_services = parse_configured_services
237
+ previously_configured_services = config.configured_services
296
238
 
297
239
  begin
298
- sudo(services_command_check)
299
- rescue StandardError => e
240
+ sudo(config.services_check_command)
241
+ rescue EY::Serverside::RemoteFailure
300
242
  shell.info "Could not setup services. Upgrade your environment to get services configuration."
301
243
  return
302
244
  end
303
245
 
304
246
  begin
305
- sudo(services_setup_command)
306
- rescue StandardError => e
307
- unless previously_configured_services.empty?
247
+ sudo(config.services_setup_command)
248
+ rescue EY::Serverside::RemoteFailure => e
249
+ if previously_configured_services
308
250
  shell.warning <<-WARNING
309
251
  External services configuration not updated. Using previous version.
310
252
  Deploy again if your services configuration appears incomplete or out of date.
@@ -312,32 +254,40 @@ Deploy again if your services configuration appears incomplete or out of date.
312
254
  WARNING
313
255
  end
314
256
  end
257
+
258
+ if services = config.configured_services
259
+ shell.status "Services configured: #{services.join(', ')}"
260
+ dependency_manager.check_ey_config
261
+ end
315
262
  end
316
263
 
317
264
  def setup_sqlite3_if_necessary
318
- if gemfile? && lockfile && lockfile.uses_sqlite3?
319
- [
320
- ["Create databases directory if needed", "mkdir -p #{paths.shared}/databases"],
321
- ["Creating SQLite database if needed", "touch #{paths.shared}/databases/#{config.framework_env}.sqlite3"],
322
- ["Create config directory if needed", "mkdir -p #{paths.active_release_config}"],
323
- ["Generating SQLite config", <<-WRAP],
265
+ if dependency_manager.uses_sqlite3?
266
+ shell.status "Setting up SQLite3 Database for compatibility with application's chosen adapter"
267
+ shell.warning "SQLite3 cannot persist across servers. Please upgrade to a supported database."
268
+
269
+ shell.substatus "Create databases directory if needed"
270
+ run "mkdir -p #{paths.shared}/databases"
271
+
272
+ shell.substatus "Create SQLite database if needed"
273
+ run "touch #{paths.shared}/databases/#{config.framework_env}.sqlite3"
274
+
275
+ shell.substatus "Create config directory if needed"
276
+ run "mkdir -p #{paths.active_release_config}"
277
+
278
+ shell.substatus "Generating SQLite config"
279
+ run <<-WRAP
324
280
  cat > #{paths.shared_config}/database.sqlite3.yml<<'YML'
325
281
  #{config.framework_env}:
326
- adapter: sqlite3
327
- database: #{paths.shared}/databases/#{config.framework_env}.sqlite3
328
- pool: 5
329
- timeout: 5000
282
+ adapter: sqlite3
283
+ database: #{paths.shared}/databases/#{config.framework_env}.sqlite3
284
+ pool: 5
285
+ timeout: 5000
330
286
  YML
331
- WRAP
332
- ["Symlink database.yml", "ln -nfs #{paths.shared_config}/database.sqlite3.yml #{paths.active_release_config}/database.yml"],
333
- ].each do |what, cmd|
334
- shell.status "#{what}"
335
- run(cmd)
336
- end
287
+ WRAP
337
288
 
338
- owner = [config.user, config.group].join(':')
339
- shell.status "Setting ownership to #{owner}"
340
- sudo "chown -R #{owner} #{paths.active_release}"
289
+ shell.substatus "Symlink database.yml"
290
+ run "ln -nfs #{paths.shared_config}/database.sqlite3.yml #{paths.active_release_config}/database.yml"
341
291
  end
342
292
  end
343
293
 
@@ -347,9 +297,6 @@ WRAP
347
297
  shell.substatus what
348
298
  run(cmd)
349
299
  end
350
- owner = [config.user, config.group].join(':')
351
- shell.status "Setting ownership to #{owner}"
352
- sudo "chown -R #{owner} #{paths.active_release}"
353
300
  end
354
301
 
355
302
  def symlink_tasks
@@ -372,14 +319,31 @@ WRAP
372
319
  # task
373
320
  def symlink
374
321
  shell.status "Symlinking code."
375
- run "rm -f #{paths.current} && ln -nfs #{paths.active_release} #{paths.current} && find #{paths.current} -not -user #{config.user} -or -not -group #{config.group} -exec chown #{config.user}:#{config.group} {} +"
322
+ run move_symlink(paths.active_release, paths.current, "deploying")
323
+ ensure_ownership(paths.current)
376
324
  @symlink_changed = true
377
325
  rescue Exception
378
- sudo "rm -f #{paths.current} && ln -nfs #{paths.previous_release(paths.active_release)} #{paths.current} && chown -R #{config.user}:#{config.group} #{paths.current}"
326
+ sudo move_symlink(paths.previous_release(paths.active_release), paths.current, "reverting")
327
+ ensure_ownership(paths.current)
379
328
  @symlink_changed = false
380
329
  raise
381
330
  end
382
331
 
332
+ def ensure_ownership(*targets)
333
+ sudo "find #{targets.join(' ')} -not -user #{config.user} -or -not -group #{config.group} -exec chown #{config.user}:#{config.group} {} +"
334
+ end
335
+
336
+ # Move a symlink as atomically as we can.
337
+ #
338
+ # mv -T renames 'next' to 'current' instead of moving 'next' to current/next'
339
+ # mv -T isn't available on OS X and maybe elsewhere, so fallback to rm && ln
340
+ def move_symlink(source, link, name)
341
+ next_link = link.dirname.join(name)
342
+ mv_t = "ln -nfs #{source} #{next_link} && mv -T #{next_link} #{link} >/dev/null 2>&1"
343
+ rm_ln = "rm -rf #{next_link} #{link} && ln -nfs #{source} #{link}"
344
+ "((#{mv_t}) || (#{rm_ln}))"
345
+ end
346
+
383
347
  def callback(what)
384
348
  @callbacks_reached ||= true
385
349
  if paths.deploy_hook(what).exist?
@@ -402,7 +366,7 @@ WRAP
402
366
  # Rollback doesn't know about the repository location (nor
403
367
  # should it need to), but it would like to use #short_log_message.
404
368
  def strategy
405
- ENV['GIT_SSH'] = ssh_executable.to_s
369
+ ensure_git_ssh_wrapper
406
370
  @strategy ||= config.strategy_class.new(
407
371
  shell,
408
372
  :verbose => config.verbose,
@@ -412,10 +376,7 @@ WRAP
412
376
  :ref => config[:branch]
413
377
  )
414
378
  end
415
-
416
- def gemfile?
417
- paths.gemfile.exist?
418
- end
379
+ public :strategy
419
380
 
420
381
  def base_callback_command_for(what)
421
382
  cmd = [About.binary, 'hook', what.to_s]
@@ -453,82 +414,29 @@ WRAP
453
414
  end
454
415
  end
455
416
 
456
- def maintenance
457
- @maintenance ||= Maintenance.new(servers, config, shell)
458
- end
459
-
460
417
  def with_failed_release_cleanup
461
418
  yield
462
- rescue Exception
419
+ rescue Exception => e
463
420
  shell.status "Release #{paths.active_release} failed, saving release to #{paths.releases_failed}."
464
- sudo "mv #{paths.active_release} #{paths.releases_failed}"
421
+ run "mkdir -p #{paths.releases_failed}"
422
+ ensure_ownership(paths.active_release, paths.releases_failed)
423
+ run "mv #{paths.active_release} #{paths.releases_failed}"
465
424
  clean_release_directory(paths.releases_failed)
466
- raise
467
- end
468
-
469
- def bundler_config
470
- version = LockfileParser.default_version
471
- options = [
472
- "--gemfile #{paths.gemfile}",
473
- "--path #{paths.bundled_gems}",
474
- "--binstubs #{paths.binstubs}",
475
- "--without #{config.bundle_without}"
476
- ]
477
-
478
- if lockfile
479
- version = lockfile.bundler_version
480
- options.unshift('--deployment') # deployment mode is not supported without a Gemfile.lock
481
- end
482
-
483
- return [version, options.join(' ')]
484
- end
485
-
486
- def lockfile
487
- lockfile_path = paths.gemfile_lock
488
- if lockfile_path.exist?
489
- @lockfile_parser ||= LockfileParser.new(lockfile_path.read)
490
- else
491
- nil
492
- end
493
- end
494
-
495
- def check_ruby_bundler
496
- if gemfile?
497
- shell.status "Bundling gems..."
498
-
499
- clean_bundle_on_system_version_change
500
-
501
- bundler_version, install_switches = bundler_config
502
- sudo "#{clean_environment} && #{About.binary} install_bundler #{bundler_version}"
503
- run "#{clean_environment} && cd #{paths.active_release} && ruby -S bundle _#{bundler_version}_ install #{install_switches}"
504
-
505
- write_system_version
506
- end
425
+ raise e
507
426
  end
508
427
 
509
- def clean_bundle_on_system_version_change
510
- # diff exits with 0 for same and 1/2 for different/file not found.
511
- check_ruby = "#{config.ruby_version_command} | diff - #{paths.ruby_version} >/dev/null 2>&1"
512
- check_system = "#{config.system_version_command} | diff - #{paths.system_version} >/dev/null 2>&1"
513
- say_cleaning = "echo 'New deploy or system version change detected, cleaning bundled gems.'"
514
- clean_bundle = "rm -Rf #{paths.bundled_gems}"
515
-
516
- run "#{check_ruby} && #{check_system} || (#{say_cleaning} && #{clean_bundle})"
428
+ def maintenance
429
+ @maintenance ||= Maintenance.new(servers, config, shell)
517
430
  end
518
431
 
519
- def write_system_version
520
- store_ruby_version = "#{config.ruby_version_command} > #{paths.ruby_version}"
521
- store_system_version = "#{config.system_version_command} > #{paths.system_version}"
522
-
523
- run "mkdir -p #{paths.bundled_gems} && chown #{config.user}:#{config.group} #{paths.bundled_gems}"
524
- run "#{store_ruby_version} && #{store_system_version}"
432
+ def dependency_manager
433
+ ensure_git_ssh_wrapper
434
+ @dependency_manager ||= DependencyManager.detect(servers, config, shell, self)
525
435
  end
436
+ public :dependency_manager # FIXME
526
437
 
527
- def check_node_npm
528
- if paths.package_json.exist?
529
- shell.info "~> package.json detected, installing npm packages"
530
- run "cd #{paths.active_release} && npm install"
531
- end
438
+ def compile_assets
439
+ RailsAssets.detect_and_compile(config, shell, self)
532
440
  end
533
441
  end # DeployBase
534
442