engineyard-serverside 2.0.7 → 2.1.0.pre

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.
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