Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[SS-12] [SS-24] Compile and link Rails 3.1 assets during deployment i…

…f needed.
  • Loading branch information...
commit cbef1a268dad036f678771c2a0a3a3f63872c45f 1 parent cd0f57e
@wilson wilson authored
View
2  README.markdown
@@ -25,3 +25,5 @@ Due to the nature of --development, that gem install command can take a VERY lon
If you get tired of waiting, you can manually install the indicated versions of
rake and rspec. Check the gemspec for current requirements.
At the time of this writing, rspec 1.3.0 was the last version known to work on 1.8.6.
+
+Set the 'VERBOSE' environment variable to something to get full output from failing commands.
View
8 lib/engineyard-serverside/deploy.rb
@@ -2,11 +2,13 @@
require 'base64'
require 'fileutils'
require 'json'
+require 'engineyard-serverside/rails_asset_support'
module EY
module Serverside
class DeployBase < Task
include LoggedOutput
+ include ::EY::Serverside::RailsAssetSupport
# default task
def deploy
@@ -29,7 +31,10 @@ def cached_deploy
symlink_configs
conditionally_enable_maintenance_page
run_with_callbacks(:migrate)
+ run_with_callbacks(:compile_assets) # defined in RailsAssetSupport
callback(:before_symlink)
+ # We don't use run_with_callbacks for symlink because we need
+ # to clean up manually if it fails.
symlink
end
@@ -160,7 +165,7 @@ def bundle
end
end
- run "cd #{c.release_path} && bundle _#{bundler_installer.version}_ install #{bundler_installer.options}"
+ run "cd #{c.release_path} && ruby -S bundle _#{bundler_installer.version}_ install #{bundler_installer.options}"
run "mkdir -p #{bundled_gems_path} && ruby -v > #{ruby_version_file} && uname -m > #{system_version_file}"
end
@@ -364,6 +369,5 @@ def self.new(config)
super
end
end
-
end
end
View
68 lib/engineyard-serverside/rails_asset_support.rb
@@ -0,0 +1,68 @@
+module EY
+ module Serverside
+ module RailsAssetSupport
+ def compile_assets
+ roles :app_master, :app, :solo do
+ rails_app = "#{c.release_path}/config/application.rb"
+ asset_dir = "#{c.release_path}/app/assets"
+ if File.exists?(rails_app) && File.directory?(asset_dir)
+ return if app_disables_assets?(rails_app)
+ if app_builds_own_assets?
+ info "~> public/assets already exists, skipping pre-compilation."
+ return
+ end
+ keep_existing_assets
+ cmd = "cd #{c.release_path} && PATH=#{c.binstubs_path}:$PATH #{c.framework_envs} rake assets:precompile"
+ info "~> Precompiling assets for Rails: #{cmd}"
+ run(cmd)
+ end
+ end
+ end
+
+ def app_disables_assets?(path)
+ disabled = nil
+ File.open(path) do |fd|
+ pattern = /^[^#].*config\.assets\.enabled\s+=\s+(false|nil)/
+ contents = fd.read
+ disabled = contents.match(pattern)
+ end
+ disabled
+ end
+
+ def app_builds_own_assets?
+ File.directory?(File.join(c.release_path, 'public', 'assets'))
+ end
+
+ # To support operations like Unicorn's hot reload, it is useful to have
+ # the prior release's assets as well. Otherwise, while a deploy is running,
+ # clients may request stale assets that you just deleted.
+ # Making use of this requires a properly-configured front-end HTTP server.
+ def keep_existing_assets
+ current = File.join(c.shared_path, 'assets')
+ last_asset_path = File.join(c.shared_path, 'last_assets')
+ # If there are current shared assets, move them under a 'last_assets' directory.
+ run <<-COMMAND
+if [ -d #{current} ]; then
+ rm -rf #{last_asset_path} && mkdir #{last_asset_path} && mv #{current} #{last_asset_path} && mkdir -p #{current};
+else
+ mkdir -p #{current} #{last_asset_path};
+fi;
+ln -nfs #{current} #{last_asset_path} #{c.release_path}/public
+ COMMAND
+ end
+
+ def bundled_rails_version(lockfile_path)
+ lockfile = File.open(lockfile_path) {|f| f.read}
+ lockfile.each_line do |line|
+ # scan for gemname (version) toplevel deps.
+ # Likely doesn't handle ancient Bundler versions, but
+ # we only call this when something looks like it is Rails 3.
+ next unless line =~ /^\s{4}([-\w_.0-9]+)\s*\((.*)\)/
+ return $2 if $1 == 'rails'
+ end
+ nil
+ end
+ end
+ end
+end
+
View
2  spec/basic_deploy_spec.rb
@@ -39,6 +39,8 @@
File.exist?(File.join(@deploy_dir, 'current', 'after_bundle.ran' )).should be_true
File.exist?(File.join(@deploy_dir, 'current', 'before_migrate.ran')).should be_true
File.exist?(File.join(@deploy_dir, 'current', 'after_migrate.ran' )).should be_true
+ File.exist?(File.join(@deploy_dir, 'current', 'before_compile_assets.ran')).should be_true
+ File.exist?(File.join(@deploy_dir, 'current', 'after_compile_assets.ran' )).should be_true
File.exist?(File.join(@deploy_dir, 'current', 'before_symlink.ran')).should be_true
File.exist?(File.join(@deploy_dir, 'current', 'after_symlink.ran' )).should be_true
File.exist?(File.join(@deploy_dir, 'current', 'before_restart.ran')).should be_true
View
2  spec/custom_deploy_spec.rb
@@ -24,6 +24,7 @@ def create_revision_file() @call_order << 'create_revision_fil
def bundle() @call_order << 'bundle' end
def symlink_configs() @call_order << 'symlink_configs' end
def migrate() @call_order << 'migrate' end
+ def compile_assets() @call_order << 'compile_assets' end
def symlink() @call_order << 'symlink' end
def restart() @call_order << 'restart' end
def cleanup_old_releases() @call_order << 'cleanup_old_releases' end
@@ -41,6 +42,7 @@ def disable_maintenance_page() @call_order << 'disable_maintenance
symlink_configs
conditionally_enable_maintenance_page
migrate
+ compile_assets
symlink
restart
disable_maintenance_page
View
104 spec/rails31_deploy_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe "Deploying a Rails 3.1 application" do
+ def deploy_test_application(assets_enabled = true)
+ return # pending!
+ $DISABLE_GEMFILE = false
+ $DISABLE_LOCKFILE = false
+ @deploy_dir = File.join(Dir.tmpdir, "serverside-deploy-#{Time.now.to_i}-#{$$}")
+
+ # set up EY::Serverside::Server like we're on a solo
+ EY::Serverside::Server.reset
+ EY::Serverside::Server.add(:hostname => 'localhost', :roles => %w[solo])
+
+ # run a deploy
+ @config = EY::Serverside::Deploy::Configuration.new({
+ "strategy" => "IntegrationSpec",
+ "deploy_to" => @deploy_dir,
+ "group" => `id -gn`.strip,
+ "stack" => 'nginx_passenger',
+ "migrate" => "ruby -e 'puts ENV[\"PATH\"]' > #{@deploy_dir}/path-when-migrating",
+ 'app' => 'rails31',
+ 'framework_env' => 'staging'
+ })
+
+ # pretend there is a shared bundled_gems directory
+ FileUtils.mkdir_p(File.join(@deploy_dir, 'shared', 'bundled_gems'))
+ %w(RUBY_VERSION SYSTEM_VERSION).each do |name|
+ File.open(File.join(@deploy_dir, 'shared', 'bundled_gems', name), "w") { |f| f.write("old\n") }
+ end
+
+ # Set up the application directory to have the requested asset options.
+ prepare_rails31_app(assets_enabled)
+
+ @binpath = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'engineyard-serverside'))
+ @deployer = FullTestDeploy.new(@config)
+ yield if block_given?
+ @deployer.deploy
+ end
+
+ def prepare_rails31_app(assets_enabled)
+ FileUtils.mkdir_p(File.join(@config.release_path, 'config'))
+ app_rb = File.join(@config.release_path, 'config', 'application.rb')
+ app_rb_contents = <<-EOF
+module Rails31
+ class Application < Rails::Application
+ config.assets.enabled = #{assets_enabled ? 'true' : 'false'}
+ end
+end
+EOF
+ File.open(app_rb, 'w') {|f| f.write(app_rb_contents)}
+ rakefile = File.join(@config.release_path, 'Rakefile')
+ rakefile_contents = <<-EOF
+task 'assets:precompile' do
+ sh 'touch precompiled'
+end
+EOF
+ File.open(rakefile, 'w') {|f| f.write(rakefile_contents)}
+ FileUtils.mkdir_p(File.join(@config.release_path, 'app', 'assets'))
+
+ @deployer = FullTestDeploy.new(@config)
+ @deployer.deploy
+ end
+
+ context "with default production settings" do
+ before(:all) do
+ deploy_test_application
+ end
+
+ it "precompiles assets" do
+ pending "Bundler is a party"
+ File.exist?(File.join(@deploy_dir, 'current', 'precompiled')).should be_true
+ end
+ end
+
+ context "with asset support disabled in its config" do
+ before(:all) do
+ deploy_test_application(with_assets = false)
+ end
+
+ it "does not precompile assets" do
+ pending "Bundler is a party"
+ File.exist?(File.join(@deploy_dir, 'current', 'precompiled')).should be_false
+ end
+ end
+
+ context "with existing precompilation in a deploy hook" do
+ before(:all) do
+ deploy_test_application do
+ hook = File.join(@config.release_path, 'deploy', 'before_migrate')
+ hook_contents = %Q[system 'touch custom_compiled && mkdir public/assets']
+ File.open(hook, 'w') {|f| f.puts(hook_contents) }
+ File.chmod(0755, hook)
+ end
+ end
+
+ it "does not replace the public/assets directory" do
+ pending "Bundler is a party"
+ File.exist?(File.join(@deploy_dir, 'current', 'custom_compiled')).should be_true
+ File.exist?(File.join(@deploy_dir, 'current', 'precompiled')).should be_false
+ File.directory?(File.join(@deploy_dir, 'current', 'public', 'assets')).should be_true
+ File.symlink?(File.join(@deploy_dir, 'current', 'public', 'assets')).should be_false
+ end
+ end
+end
View
2  spec/support/integration.rb
@@ -75,7 +75,7 @@ def update_repository_cache
deploy_hook_dir = File.join(cached_copy, 'deploy')
FileUtils.mkdir_p(deploy_hook_dir)
- %w[bundle migrate symlink restart].each do |action|
+ %w[bundle compile_assets migrate symlink restart].each do |action|
%w[before after].each do |prefix|
hook = "#{prefix}_#{action}"
File.open(File.join(deploy_hook_dir, "#{hook}.rb"), 'w') do |f|
Please sign in to comment.
Something went wrong with that request. Please try again.