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.
- data/lib/engineyard-serverside.rb +0 -1
- data/lib/engineyard-serverside/cli.rb +44 -42
- data/lib/engineyard-serverside/configuration.rb +55 -4
- data/lib/engineyard-serverside/dependency_manager.rb +17 -0
- data/lib/engineyard-serverside/dependency_manager/base.rb +65 -0
- data/lib/engineyard-serverside/dependency_manager/bundler.rb +124 -0
- data/lib/engineyard-serverside/dependency_manager/bundler_lock.rb +155 -0
- data/lib/engineyard-serverside/dependency_manager/legacy_helpers.rb +24 -0
- data/lib/engineyard-serverside/dependency_manager/npm.rb +16 -0
- data/lib/engineyard-serverside/deploy.rb +86 -178
- data/lib/engineyard-serverside/deprecation.rb +11 -1
- data/lib/engineyard-serverside/paths.rb +6 -0
- data/lib/engineyard-serverside/propagator.rb +2 -2
- data/lib/engineyard-serverside/rails_assets.rb +152 -0
- data/lib/engineyard-serverside/rails_assets/strategy.rb +197 -0
- data/lib/engineyard-serverside/server.rb +5 -0
- data/lib/engineyard-serverside/servers.rb +19 -7
- data/lib/engineyard-serverside/shell.rb +7 -5
- data/lib/engineyard-serverside/shell/command_result.rb +1 -1
- data/lib/engineyard-serverside/strategies/git.rb +14 -4
- data/lib/engineyard-serverside/task.rb +1 -0
- data/lib/engineyard-serverside/version.rb +1 -1
- data/spec/bundler_deploy_spec.rb +36 -33
- data/spec/configuration_spec.rb +5 -4
- data/spec/custom_deploy_spec.rb +11 -9
- data/spec/deploy_hook_spec.rb +10 -3
- data/spec/ey_yml_customized_deploy_spec.rb +1 -1
- data/spec/fixtures/lockfiles/1.0-no-bundler +1 -1
- data/spec/fixtures/lockfiles/1.0.0.rc.1-with-bundler +1 -1
- data/spec/fixtures/lockfiles/1.0.18-do_mysql +1 -1
- data/spec/fixtures/lockfiles/1.0.18-do_postgres +1 -1
- data/spec/fixtures/lockfiles/1.0.18-mysql +1 -1
- data/spec/fixtures/lockfiles/1.0.18-mysql2 +1 -1
- data/spec/fixtures/lockfiles/1.0.18-pg +1 -1
- data/spec/fixtures/lockfiles/1.0.6-no-bundler +2 -2
- data/spec/fixtures/lockfiles/1.0.6-with-any-bundler +2 -2
- data/spec/fixtures/lockfiles/1.0.6-with-bundler +2 -2
- data/spec/fixtures/lockfiles/1.3.1-rails-3.2.13 +112 -0
- data/spec/fixtures/repos/{assets_enabled → assets_detected}/Gemfile +1 -2
- data/spec/fixtures/repos/{assets_enabled → assets_detected}/Gemfile.lock +1 -3
- data/spec/fixtures/repos/{assets_enabled → assets_detected}/README +0 -0
- data/spec/fixtures/repos/assets_detected/Rakefile +5 -0
- data/spec/fixtures/repos/{assets_enabled → assets_detected}/app/assets/empty +0 -0
- data/spec/fixtures/repos/{assets_enabled → assets_detected}/config/application.rb +0 -0
- data/spec/fixtures/repos/assets_detected/config/ey.yml +3 -0
- data/spec/fixtures/repos/assets_disabled/Gemfile +1 -2
- data/spec/fixtures/repos/assets_disabled/Gemfile.lock +1 -3
- data/spec/fixtures/repos/assets_disabled/Rakefile +1 -0
- data/spec/fixtures/repos/assets_disabled/config/ey.yml +3 -0
- data/spec/fixtures/repos/assets_disabled_in_ey_yml/Gemfile +1 -2
- data/spec/fixtures/repos/assets_disabled_in_ey_yml/Gemfile.lock +1 -3
- data/spec/fixtures/repos/assets_disabled_in_ey_yml/Rakefile +1 -0
- data/spec/fixtures/repos/assets_disabled_in_ey_yml/config/ey.yml +1 -0
- data/spec/fixtures/repos/assets_enabled_all/Gemfile +1 -2
- data/spec/fixtures/repos/assets_enabled_all/Gemfile.lock +1 -3
- data/spec/fixtures/repos/assets_enabled_all/Rakefile +1 -0
- data/spec/fixtures/repos/assets_enabled_all/config/ey.yml +1 -0
- data/spec/fixtures/repos/assets_enabled_in_ey_yml/Gemfile +1 -1
- data/spec/fixtures/repos/assets_enabled_in_ey_yml/Gemfile.lock +1 -1
- data/spec/fixtures/repos/assets_enabled_in_ey_yml/Rakefile +1 -0
- data/spec/fixtures/repos/assets_enabled_util_only/Gemfile +1 -2
- data/spec/fixtures/repos/assets_enabled_util_only/Gemfile.lock +1 -3
- data/spec/fixtures/repos/assets_enabled_util_only/Rakefile +1 -0
- data/spec/fixtures/repos/assets_enabled_util_only/config/ey.yml +1 -0
- data/spec/fixtures/repos/assets_in_hook/Gemfile +1 -2
- data/spec/fixtures/repos/assets_in_hook/Gemfile.lock +1 -3
- data/spec/fixtures/repos/assets_in_hook/config/ey.yml +3 -0
- data/spec/fixtures/repos/assets_in_hook/deploy/before_compile_assets.rb +1 -1
- data/spec/fixtures/repos/bundle_fails/Gemfile +1 -0
- data/spec/fixtures/repos/bundle_fails/README +1 -0
- data/spec/fixtures/repos/bundle_fails/deploy/after_bundle.rb +1 -0
- data/spec/fixtures/repos/default/Gemfile +1 -2
- data/spec/fixtures/repos/default/Gemfile.lock +1 -3
- data/spec/fixtures/repos/default/ey.yml +3 -0
- data/spec/fixtures/repos/ey_yml/Gemfile +1 -1
- data/spec/fixtures/repos/ey_yml/Gemfile.lock +1 -1
- data/spec/fixtures/repos/ey_yml/config/ey.yml +11 -7
- data/spec/fixtures/repos/ey_yml_alt/Gemfile +1 -1
- data/spec/fixtures/repos/ey_yml_alt/Gemfile.lock +1 -1
- data/spec/fixtures/repos/no_ey_config/Gemfile +1 -2
- data/spec/fixtures/repos/no_ey_config/Gemfile.lock +1 -3
- data/spec/fixtures/repos/no_ey_config/ey.yml +3 -0
- data/spec/fixtures/repos/no_gemfile_lock/Gemfile +1 -2
- data/spec/fixtures/repos/no_gemfile_lock/ey.yml +3 -0
- data/spec/fixtures/repos/sqlite3/Gemfile +1 -1
- data/spec/fixtures/repos/sqlite3/Gemfile.lock +1 -1
- data/spec/lockfile_parser_spec.rb +25 -11
- data/spec/rails31_deploy_spec.rb +46 -5
- data/spec/restart_spec.rb +3 -3
- data/spec/services_deploy_spec.rb +89 -86
- data/spec/shell_spec.rb +0 -8
- data/spec/spec_helper.rb +81 -36
- data/spec/sqlite3_deploy_spec.rb +4 -5
- data/spec/support/integration.rb +22 -37
- metadata +167 -154
- data/lib/engineyard-serverside/lockfile_parser.rb +0 -101
- data/lib/engineyard-serverside/rails_asset_support.rb +0 -132
- data/spec/fixtures/repos/assets_enabled/Rakefile +0 -5
@@ -6,10 +6,20 @@ module EY
|
|
6
6
|
$stderr.puts "DEPRECATION WARNING: #{msg}\n\t#{caller(2).first}"
|
7
7
|
end
|
8
8
|
|
9
|
+
def self.deprecated_task(receiver, old_task, new_task)
|
10
|
+
if receiver.respond_to?(old_task)
|
11
|
+
deprecation_warning("Task ##{old_task} has been renamed to ##{new_task}.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
9
15
|
def self.const_missing(const)
|
10
|
-
|
16
|
+
case const
|
17
|
+
when :LoggedOutput
|
11
18
|
EY::Serverside.deprecation_warning("EY::Serverside::LoggedOutput has been deprecated. Use EY::Serverside::Shell::Helpers instead.")
|
12
19
|
EY::Serverside::Shell::Helpers
|
20
|
+
when :LockfileParser
|
21
|
+
EY::Serverside.deprecation_warning("EY::Serverside::LockfileParser has been deprecated. Use EY::Serverside::DependencyManager::BundlerLock::Lockfile instead.")
|
22
|
+
EY::Serverside::DependencyManager::BundlerLock::Lockfile
|
13
23
|
else
|
14
24
|
super
|
15
25
|
end
|
@@ -70,6 +70,7 @@ module EY
|
|
70
70
|
def_path :enabled_maintenance_page, [:shared_system, 'maintenance.html']
|
71
71
|
def_path :shared_assets, [:shared, 'assets']
|
72
72
|
def_path :bundled_gems, [:shared, 'bundled_gems']
|
73
|
+
def_path :shared_services_yml, [:shared_config, 'ey_services_config_deploy.yml']
|
73
74
|
def_path :ruby_version, [:bundled_gems, 'RUBY_VERSION']
|
74
75
|
def_path :system_version, [:bundled_gems, 'SYSTEM_VERSION']
|
75
76
|
def_path :latest_revision, [:latest_release, 'REVISION']
|
@@ -127,6 +128,11 @@ module EY
|
|
127
128
|
end
|
128
129
|
end
|
129
130
|
|
131
|
+
def previous_revision
|
132
|
+
rel = previous_release(active_release)
|
133
|
+
rel && rel.join('REVISION')
|
134
|
+
end
|
135
|
+
|
130
136
|
# deploy_root/releases/<latest timestamp>
|
131
137
|
def latest_release
|
132
138
|
all_releases.last
|
@@ -67,8 +67,8 @@ module EY
|
|
67
67
|
|
68
68
|
def propagate
|
69
69
|
shell.status "Propagating #{About.name_with_version} to #{count_servers(servers)}."
|
70
|
-
servers.run_on_each { |server| shell.logged_system(scp_command(server)) }
|
71
|
-
servers.run_on_each { |server| shell.logged_system(server.command_on_server('sudo sh -l -c', install_command)) }
|
70
|
+
servers.run_on_each(shell) { |server| shell.logged_system(scp_command(server)) }
|
71
|
+
servers.run_on_each(shell) { |server| shell.logged_system(server.command_on_server('sudo sh -l -c', install_command)) }
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'engineyard-serverside/rails_assets/strategy'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module EY
|
5
|
+
module Serverside
|
6
|
+
class RailsAssets
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def self.detect_and_compile(*args)
|
10
|
+
new(*args).detect_and_compile
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :config, :shell, :runner
|
14
|
+
|
15
|
+
def initialize(config, shell, runner)
|
16
|
+
@config, @shell, @runner = config, shell, runner
|
17
|
+
end
|
18
|
+
|
19
|
+
def_delegators :config,
|
20
|
+
:paths, :asset_dependencies, :asset_roles,
|
21
|
+
:framework_envs, :precompile_assets?, :skip_precompile_assets?,
|
22
|
+
:precompile_unchanged_assets?, :precompile_assets_task
|
23
|
+
|
24
|
+
def detect_and_compile
|
25
|
+
runner.roles asset_roles do
|
26
|
+
if precompile_assets?
|
27
|
+
if precompile_unchanged_assets?
|
28
|
+
shell.status "Precompiling assets without change detection. (precompile_unchanged_assets: true)"
|
29
|
+
run_precompile_assets_task
|
30
|
+
elsif reuse_assets?
|
31
|
+
shell.status "Reusing existing assets. (configured asset_dependencies unchanged from #{previous_revision[0,7]}..#{active_revision[0,7]})"
|
32
|
+
asset_strategy.reuse
|
33
|
+
else
|
34
|
+
shell.status "Precompiling assets. (precompile_assets: true)"
|
35
|
+
run_precompile_assets_task
|
36
|
+
end
|
37
|
+
elsif skip_precompile_assets?
|
38
|
+
shell.status "Skipping asset precompilation. (precompile_assets: false)"
|
39
|
+
elsif !application_rb_path.readable? || !app_assets_path.directory?
|
40
|
+
# Not a Rails app. Ignore assets completely.
|
41
|
+
elsif app_disables_assets?
|
42
|
+
shell.status "Skipping asset precompilation. ('config/application.rb' disables assets.)"
|
43
|
+
elsif paths.public_assets.exist?
|
44
|
+
shell.status "Skipping asset precompilation. ('public/assets' directory already exists.)"
|
45
|
+
else
|
46
|
+
precompile_detected_assets
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def run_precompile_assets_task
|
52
|
+
asset_strategy.prepare do
|
53
|
+
cd = "cd #{paths.active_release}"
|
54
|
+
task = "PATH=#{paths.binstubs}:$PATH #{framework_envs} rake #{precompile_assets_task} RAILS_GROUPS=assets"
|
55
|
+
runner.run "#{cd} && #{task}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def previous_revision
|
60
|
+
@previous_revision ||= config.previous_revision
|
61
|
+
end
|
62
|
+
|
63
|
+
def active_revision
|
64
|
+
@active_revision ||= config.active_revision
|
65
|
+
end
|
66
|
+
|
67
|
+
# Note on reusing assets when assets may fail silently:
|
68
|
+
# It's difficult and error prone to reuse assets that may have failed
|
69
|
+
# silently in the previous deploy. If the assets are unchanged during
|
70
|
+
# this deploy, but failed last deploy, we would incorrectly reuse
|
71
|
+
# silentely failed assets. Only reusing when assets are enabled
|
72
|
+
# ensures that existing assets were successful.
|
73
|
+
def reuse_assets?
|
74
|
+
previous_revision &&
|
75
|
+
active_revision &&
|
76
|
+
runner.strategy.same?(previous_revision, active_revision, asset_dependencies)
|
77
|
+
end
|
78
|
+
|
79
|
+
def precompile_detected_assets
|
80
|
+
shell.status "Precompiling assets. ('#{app_assets}' exists, 'public/assets' not found, not disabled in config.)"
|
81
|
+
if !runner.dependency_manager.rails_version
|
82
|
+
shell.warning "Precompiling assets even though Rails was not bundled."
|
83
|
+
end
|
84
|
+
|
85
|
+
run_precompile_assets_task
|
86
|
+
|
87
|
+
shell.warning <<-WARN
|
88
|
+
Inferred asset compilation succeeded, but failures may be silently ignored!
|
89
|
+
|
90
|
+
ACTION REQUIRED: Add precompile_assets option to ey.yml.
|
91
|
+
precompile_assets: true # precompile assets when asset changes detected
|
92
|
+
WARN
|
93
|
+
rescue EY::Serverside::RemoteFailure => e
|
94
|
+
# If we are implicitly precompiling, we want to fail non-destructively
|
95
|
+
# because we don't know if the rake task exists or if the user
|
96
|
+
# actually intended for assets to be compiled.
|
97
|
+
if e.to_s =~ /Don't know how to build task '#{precompile_assets_task}'/
|
98
|
+
shell.warning <<-WARN
|
99
|
+
Asset precompilation detected but compilation failure ignored!
|
100
|
+
Rake task '#{precompile_assets_task}' was not found.
|
101
|
+
|
102
|
+
ACTION REQUIRED: Add precompile_assets option to ey.yml.
|
103
|
+
precompile_assets: false # disable assets to avoid this error.
|
104
|
+
WARN
|
105
|
+
else
|
106
|
+
shell.error <<-ERROR
|
107
|
+
Asset precompilation detected but compilation failed!
|
108
|
+
|
109
|
+
ACTION REQUIRED: Add precompile_assets option to ey.yml.
|
110
|
+
precompile_assets: true # precompile assets when asset changes detected
|
111
|
+
precompile_assets: false # disable asset compilation.
|
112
|
+
ERROR
|
113
|
+
raise
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def app_disables_assets?
|
118
|
+
application_rb_path.open do |fd|
|
119
|
+
fd.grep(/^[^#]*config\.assets\.enabled\s*=\s*(false|nil)/).any?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# This check is very expensive, and has been deemed not worth the time.
|
124
|
+
# Leaving this here in case someone comes up with a faster way.
|
125
|
+
#
|
126
|
+
# Runs 'rake -T' to see if there is an assets:precompile task.
|
127
|
+
def app_has_asset_task?
|
128
|
+
# We just run this locally on the app master; everybody else should
|
129
|
+
# have the same code anyway.
|
130
|
+
task_check = "PATH=#{paths.binstubs}:$PATH #{framework_envs} rake -T #{precompile_assets_task} | grep '#{precompile_assets_task}'"
|
131
|
+
cmd = "cd #{paths.active_release} && #{task_check}"
|
132
|
+
shell.logged_system(cmd).success?
|
133
|
+
end
|
134
|
+
|
135
|
+
def application_rb_path
|
136
|
+
paths.active_release.join('config','application.rb')
|
137
|
+
end
|
138
|
+
|
139
|
+
def app_assets
|
140
|
+
File.join('app','assets')
|
141
|
+
end
|
142
|
+
|
143
|
+
def app_assets_path
|
144
|
+
paths.active_release.join(app_assets)
|
145
|
+
end
|
146
|
+
|
147
|
+
def asset_strategy
|
148
|
+
@asset_strategy ||= RailsAssets::Strategy.fetch(config.asset_strategy, paths, runner)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module EY
|
4
|
+
module Serverside
|
5
|
+
class RailsAssets
|
6
|
+
module Strategy
|
7
|
+
def self.all
|
8
|
+
{
|
9
|
+
'shared' => Shared,
|
10
|
+
'cleaning' => Cleaning,
|
11
|
+
'private' => Private,
|
12
|
+
'shifting' => Shifting,
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.fetch(name, *args)
|
17
|
+
(all[name.to_s] || Shifting).new(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# Precompile assets fresh every time. Shared assets are not symlinked
|
22
|
+
# and assets stay with the release that compiled them. The assets of
|
23
|
+
# the previous deploy are symlinked as into the current deploy to
|
24
|
+
# prevent errors during deploy.
|
25
|
+
#
|
26
|
+
# When no assets changes are detected, the deploy uses rsync to copy
|
27
|
+
# the previous release's assets into the current assets directory.
|
28
|
+
class Private
|
29
|
+
attr_reader :paths, :runner
|
30
|
+
|
31
|
+
def initialize(paths, runner)
|
32
|
+
@paths = paths
|
33
|
+
@runner = runner
|
34
|
+
end
|
35
|
+
|
36
|
+
def reuse
|
37
|
+
run("mkdir -p #{paths.public_assets} && rsync -aq #{previous_assets_path}/ #{paths.public_assets}")
|
38
|
+
end
|
39
|
+
|
40
|
+
# ?ink the previous assets into the new public/last_assets/assets
|
41
|
+
# to prevent missing assets during deploy.
|
42
|
+
#
|
43
|
+
# This results in the directory structure:
|
44
|
+
# deploy_root/current/public/last_assets/assets -> deploy_root/releases/<prev>/public/assets
|
45
|
+
def prepare
|
46
|
+
last = paths.public.join('last_assets')
|
47
|
+
run "mkdir -p #{last} && ln -nfs #{previous_assets_path} #{last.join('assets')}"
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def run(cmd)
|
54
|
+
runner.run(cmd)
|
55
|
+
end
|
56
|
+
|
57
|
+
def previous_assets_path
|
58
|
+
paths.previous_release(paths.active_release).join('public','assets')
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Basic shared assets.
|
63
|
+
# Precompiled assets go into a single shared assets directory. The
|
64
|
+
# assets directory is never cleaned, so a deploy hook should be used
|
65
|
+
# to clean assets appropriately.
|
66
|
+
#
|
67
|
+
# When no assets changes are detected, shared directory is only
|
68
|
+
# symlinked and precompile task is not run.
|
69
|
+
class Shared
|
70
|
+
attr_reader :paths, :runner
|
71
|
+
def initialize(paths, runner)
|
72
|
+
@paths = paths
|
73
|
+
@runner = runner
|
74
|
+
end
|
75
|
+
|
76
|
+
def reuse
|
77
|
+
run "mkdir -p #{shared_assets_path} && ln -nfs #{shared_assets_path} #{paths.public}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def prepare
|
81
|
+
reuse
|
82
|
+
yield
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def run(cmd)
|
88
|
+
runner.run(cmd)
|
89
|
+
end
|
90
|
+
|
91
|
+
def shared_assets_path
|
92
|
+
paths.shared_assets
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Precompiled assets are shared across all deploys like Shared.
|
97
|
+
# Before compiling the active deploying assets, all assets that are not
|
98
|
+
# referenced by the manifest.yml from the previous deploy are removed.
|
99
|
+
# After cleaning, the new assets are compiled over the top. The result
|
100
|
+
# is an assets dir that contains the last assets and the current assets.
|
101
|
+
#
|
102
|
+
# When no assets changes are detected, shared directory is only
|
103
|
+
# symlinked and cleaning and precompile tasks are not run.
|
104
|
+
class Cleaning < Shared
|
105
|
+
def prepare
|
106
|
+
reuse
|
107
|
+
remove_old_assets
|
108
|
+
yield
|
109
|
+
rescue
|
110
|
+
# how do you restore back to the old assets if some have been overwritten?
|
111
|
+
# probably just deploy again I suppose.
|
112
|
+
raise
|
113
|
+
end
|
114
|
+
|
115
|
+
protected
|
116
|
+
|
117
|
+
def remove_old_assets
|
118
|
+
return unless manifest_path.readable?
|
119
|
+
|
120
|
+
Dir.chdir(shared_assets_path)
|
121
|
+
|
122
|
+
all_assets_on_disk = Dir.glob(shared_assets_path.join('**','*.*').to_s) - [manifest_path.to_s]
|
123
|
+
$stderr.puts "all_assets_on_disk #{all_assets_on_disk.inspect}"
|
124
|
+
assets_on_disk = all_assets_on_disk.reject {|a| a =~ /\.gz$/}
|
125
|
+
$stderr.puts "assets_on_disk #{assets_on_disk.inspect}"
|
126
|
+
assets_in_manifest = YAML.load_file(manifest_path.to_s).values
|
127
|
+
$stderr.puts "assets_in_manifest #{assets_in_manifest.inspect}"
|
128
|
+
|
129
|
+
remove_assets = []
|
130
|
+
(assets_on_disk - assets_in_manifest).each do |asset|
|
131
|
+
remove_assets << "'#{asset}'"
|
132
|
+
remove_assets << "'#{asset}.gz'" if all_assets_on_disk.include?("#{asset}.gz")
|
133
|
+
end
|
134
|
+
run("rm -rf #{remove_assets.join(' ')}")
|
135
|
+
end
|
136
|
+
|
137
|
+
def manifest_path
|
138
|
+
shared_assets_path.join('manifest.yml')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# The default behavior and the one used since the beginning of asset
|
143
|
+
# support in engineyard-serverside. Assets are compiled into a fresh
|
144
|
+
# shared directory. Previous shared assets are shifted to a last_assets
|
145
|
+
# directory to prevent errors during deploy.
|
146
|
+
#
|
147
|
+
# When no assets changes are detected, the two shared directories are
|
148
|
+
# symlinked into the active release without any changes.
|
149
|
+
class Shifting < Shared
|
150
|
+
# link shared/assets and shared/last_assets into public
|
151
|
+
def reuse
|
152
|
+
run "mkdir -p #{shared_assets_path} #{last_assets_path} && #{link_assets}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def prepare
|
156
|
+
shift_existing_assets
|
157
|
+
yield
|
158
|
+
rescue
|
159
|
+
unshift_existing_assets
|
160
|
+
raise
|
161
|
+
end
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
165
|
+
def last_assets_path
|
166
|
+
paths.shared.join('last_assets')
|
167
|
+
end
|
168
|
+
|
169
|
+
# If there are current shared assets, move them under a 'last_assets' directory.
|
170
|
+
#
|
171
|
+
# To support operations like Unicorn's hot reload, it is useful to have
|
172
|
+
# the prior release's assets as well. Otherwise, while a deploy is running,
|
173
|
+
# clients may request stale assets that you just deleted.
|
174
|
+
# Making use of this requires a properly-configured front-end HTTP server.
|
175
|
+
#
|
176
|
+
# Note: This results in the directory structure:
|
177
|
+
# deploy_root/current/public/assets -> deploy_root/shared/assets
|
178
|
+
# deploy_root/current/public/last_assets -> deploy_root/shared/last_assets
|
179
|
+
# where last_assets has an assets dir under it.
|
180
|
+
# deploy_root/shared/last_assets/assets
|
181
|
+
def shift_existing_assets
|
182
|
+
run "rm -rf #{last_assets_path} && mkdir -p #{shared_assets_path} #{last_assets_path} && mv #{shared_assets_path} #{last_assets_path.join('assets')} && mkdir -p #{shared_assets_path} && #{link_assets}"
|
183
|
+
end
|
184
|
+
|
185
|
+
# Restore shared/last_assets to shared/assets and relink them to the app public
|
186
|
+
def unshift_existing_assets
|
187
|
+
run "rm -rf #{shared_assets_path} && mv #{last_assets_path.join('assets')} #{shared_assets_path} && mkdir -p #{last_assets_path} && #{link_assets}"
|
188
|
+
end
|
189
|
+
|
190
|
+
def link_assets
|
191
|
+
"ln -nfs #{shared_assets_path} #{last_assets_path} #{paths.public}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -64,17 +64,17 @@ module EY
|
|
64
64
|
|
65
65
|
# Run a command on this set of servers.
|
66
66
|
def run(shell, cmd, &block)
|
67
|
-
run_on_each do |server|
|
67
|
+
run_on_each(shell) do |server|
|
68
68
|
exec_cmd = server.command_on_server('sh -l -c', cmd, &block)
|
69
|
-
shell.logged_system(exec_cmd)
|
69
|
+
shell.logged_system(exec_cmd, server)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
73
|
# Run a sudo command on this set of servers.
|
74
74
|
def sudo(shell, cmd, &block)
|
75
|
-
run_on_each do |server|
|
75
|
+
run_on_each(shell) do |server|
|
76
76
|
exec_cmd = server.command_on_server('sudo sh -l -c', cmd, &block)
|
77
|
-
shell.logged_system(exec_cmd)
|
77
|
+
shell.logged_system(exec_cmd, server)
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -93,12 +93,24 @@ module EY
|
|
93
93
|
# Makes a theard for each server and executes the block,
|
94
94
|
# Assumes that the return value of the block is a CommandResult
|
95
95
|
# and ensures that all the command results were successful.
|
96
|
-
def run_on_each(&block)
|
96
|
+
def run_on_each(shell, &block)
|
97
97
|
results = map_in_parallel(&block)
|
98
98
|
failures = results.reject {|result| result.success? }
|
99
|
+
|
99
100
|
if failures.any?
|
100
|
-
|
101
|
-
|
101
|
+
commands = failures.map { |f| f.command }.uniq
|
102
|
+
servers = failures.map { |f| f.server }.compact.map { |s| s.inspect }
|
103
|
+
outputs = failures.map { |f| f.output }.uniq
|
104
|
+
message = "The following command#{commands.size == 1 ? '' : 's'} failed"
|
105
|
+
if servers.any?
|
106
|
+
message << " on server#{servers.size == 1 ? '' : 's'} [#{servers.join(', ')}]"
|
107
|
+
end
|
108
|
+
message << "\n\n"
|
109
|
+
commands.each do |cmd|
|
110
|
+
message << "$ #{cmd}\n"
|
111
|
+
end
|
112
|
+
message << "\n" << outputs.join("\n\n") << "\n"
|
113
|
+
raise EY::Serverside::RemoteFailure.new(message)
|
102
114
|
end
|
103
115
|
end
|
104
116
|
|