OpsWorks isn\'t precompiling assets on deploy. I found this recipe in this thread but I think it\'s not complete though, or missing something because I get an error about
Before OpsWorks supports the Asset Pipeline out of the box, you could do this.
Create a file deploy/before_symlink.rb
with the following content in your rails application.
run "cd #{release_path} && RAILS_ENV=production bundle exec rake assets:precompile"
If you deploy your Rails application to a different environment, change the RAILS_ENV.
If you use a NGINX/Unicorn stack, you have to modify the /assets
resource.
Just copy the following content in a file named unicorn/templates/default/nginx_unicorn_web_app.erb
in your cookbooks.
upstream unicorn_<%= @application[:domains].first %> {
server unix:<%= @application[:deploy_to]%>/shared/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name <%= @application[:domains].join(" ") %> <%= node[:hostname] %>;
access_log <%= node[:nginx][:log_dir] %>/<%= @application[:domains].first %>.access.log;
keepalive_timeout 5;
root <%= @application[:absolute_document_root] %>;
<% if @application[:nginx] && @application[:nginx][:client_max_body_size] %>
client_max_body_size <%= @application[:nginx][:client_max_body_size] %>;
<% end %>
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
# If you don't find the filename in the static files
# Then request it from the unicorn server
if (!-f $request_filename) {
proxy_pass http://unicorn_<%= @application[:domains].first %>;
break;
}
}
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;
add_header ETag "";
break;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root <%= @application[:absolute_document_root] %>;
}
}
<% if @application[:ssl_support] %>
server {
listen 443;
server_name <%= @application[:domains].join(" ") %> <%= node[:hostname] %>;
access_log <%= node[:nginx][:log_dir] %>/<%= @application[:domains].first %>-ssl.access.log;
ssl on;
ssl_certificate /etc/nginx/ssl/<%= @application[:domains].first %>.crt;
ssl_certificate_key /etc/nginx/ssl/<%= @application[:domains].first %>.key;
<% if @application[:ssl_certificate_ca] -%>
ssl_client_certificate /etc/nginx/ssl/<%= @application[:domains].first %>.ca;
<% end -%>
keepalive_timeout 5;
root <%= @application[:absolute_document_root] %>;
<% if @application[:nginx] && @application[:nginx][:client_max_body_size] %>
client_max_body_size <%= @application[:nginx][:client_max_body_size] %>;
<% end %>
location / {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
# If you don't find the filename in the static files
# Then request it from the unicorn server
if (!-f $request_filename) {
proxy_pass http://unicorn_<%= @application[:domains].first %>;
break;
}
}
location ~ ^/assets/ {
expires 1y;
add_header Cache-Control public;
add_header ETag "";
break;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root <%= @application[:absolute_document_root] %>;
}
}
<% end %>
If you use a Apache2/Passenger stack, you have to modify the /assets
resource.
Just copy the following content in a file named
passenger_apache2/templates/default/web_app.conf.erb
in your cookbooks.
<VirtualHost *:80>
ServerName <%= @params[:server_name] %>
<% if @params[:server_aliases] && !@params[:server_aliases].empty? -%>
ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %>
<% end -%>
<% if @params[:mounted_at] -%>
DocumentRoot /var/www
<%= @params[:deploy][:passenger_handler] -%>BaseURI <%= @params[:mounted_at] %>
<% else -%>
DocumentRoot <%= @params[:docroot] %>
<%= @params[:deploy][:passenger_handler] -%>BaseURI /
<% end -%>
<%= @params[:deploy][:passenger_handler] -%>Env <%= @params[:rails_env] %>
<Directory <%= @params[:docroot] %>>
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
</Directory>
<Directory ~ "\.svn">
Order allow,deny
Deny from all
</Directory>
<Directory ~ "\.git">
Order allow,deny
Deny from all
</Directory>
<LocationMatch "^/assets/.*$">
Header unset ETag
FileETag None
# RFC says only cache for 1 year
ExpiresActive On
ExpiresDefault "access plus 1 year"
</LocationMatch>
LogLevel info
ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-error.log
CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-access.log combined
CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-ganglia.log ganglia
FileETag none
RewriteEngine On
Include <%= @params[:rewrite_config] %>*
RewriteLog <%= node[:apache][:log_dir] %>/<%= @application_name %>-rewrite.log
RewriteLogLevel 0
# Canonical host
#RewriteCond %{HTTP_HOST} !^<%= @params[:server_name] %> [NC]
#RewriteCond %{HTTP_HOST} !^$
#RewriteRule ^/(.*)$ http://<%= @params[:server_name] %>/$1 [L,R=301]
RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|jpeg|png)$
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [L]
Include <%= @params[:local_config] %>*
</VirtualHost>
<% if node[:deploy][@application_name][:ssl_support] -%>
<VirtualHost *:443>
ServerName <%= @params[:server_name] %>
<% if @params[:server_aliases] && !@params[:server_aliases].empty? -%>
ServerAlias <% @params[:server_aliases].each do |a| %><%= "#{a}" %> <% end %>
<% end -%>
SSLEngine on
SSLProxyEngine on
SSLCertificateFile <%= node[:apache][:dir] %>/ssl/<%= @params[:server_name] %>.crt
SSLCertificateKeyFile <%= node[:apache][:dir] %>/ssl/<%= @params[:server_name] %>.key
<% if @params[:ssl_certificate_ca] -%>
SSLCACertificateFile <%= node[:apache][:dir] %>/ssl/<%= @params[:server_name] %>.ca
<% end -%>
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
<% if @params[:mounted_at] -%>
DocumentRoot /var/www
<%= @params[:deploy][:passenger_handler] -%>BaseURI <%= @params[:mounted_at] %>
<% else -%>
DocumentRoot <%= @params[:docroot] %>
<%= @params[:deploy][:passenger_handler] -%>BaseURI /
<% end -%>
<%= @params[:deploy][:passenger_handler] -%>Env <%= @params[:rails_env] %>
<Directory <%= @params[:docroot] %>>
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
</Directory>
<Directory ~ "\.svn">
Order allow,deny
Deny from all
</Directory>
<Directory ~ "\.git">
Order allow,deny
Deny from all
</Directory>
<LocationMatch "^/assets/.*$">
Header unset ETag
FileETag None
# RFC says only cache for 1 year
ExpiresActive On
ExpiresDefault "access plus 1 year"
</LocationMatch>
LogLevel info
ErrorLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-ssl-error.log
CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-ssl-access.log combined
CustomLog <%= node[:apache][:log_dir] %>/<%= @params[:name] %>-ssl-ganglia.log ganglia
FileETag none
RewriteEngine On
Include <%= @params[:rewrite_config] %>-ssl*
RewriteLog <%= node[:apache][:log_dir] %>/<%= @application_name %>-ssl-rewrite.log
RewriteLogLevel 0
# Canonical host
#RewriteCond %{HTTP_HOST} !^<%= @params[:server_name] %> [NC]
#RewriteCond %{HTTP_HOST} !^$
#RewriteRule ^/(.*)$ http://<%= @params[:server_name] %>/$1 [L,R=301]
RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|jpeg|png)$
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.html -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.html
RewriteRule ^.*$ /system/maintenance.html [L]
Include <%= @params[:local_config] %>-ssl*
</VirtualHost>
<% end -%>
If you have questions feel free to ask.
Best Daniel
EDIT:
Or you just copy over this cookbooks https://github.com/neonlex/massive-octo-computing-machine I developed quickly. But OpsWorks should support this by default in the future.
Note that if you're passing environment variables to your Rails app using the new OpsWorks feature, you'll need to include these variables in your rake
invocation (as they don't get permanently sourced into the deploy user's environment).
I do the following (based on this article and this recipe) in deploy/before_migrate.rb
:
Chef::Log.info("Precompiling assets for RAILS_ENV=" \
"#{new_resource.environment['RAILS_ENV']}...")
execute 'rake assets:precompile' do
cwd release_path
command 'bundle exec rake assets:precompile'
environment new_resource.environment
end
I know very little about OpsWorks and Chef, but here's what I did to get it working.
First, I had to create a rails recipe that runs during the setup
event to create the symlink directory for the assets. This sits in a public repo that OpsWorks can access.
cookbooks/rails/recipes/symlink_assets.rb:
node[:deploy].each do |application, deploy|
Chef::Log.info("Ensuring shared/assets directory for #{application} app...")
directory "#{deploy[:deploy_to]}/shared/assets" do
group deploy[:group]
owner deploy[:user]
mode 0775
action :create
recursive true
end
end
Then, in my app, I had to create deploy/before_migrate.rb:
Chef::Log.info("Running deploy/before_migrate.rb...")
Chef::Log.info("Symlinking #{release_path}/public/assets to #{new_resource.deploy_to}/shared/assets")
link "#{release_path}/public/assets" do
to "#{new_resource.deploy_to}/shared/assets"
end
rails_env = new_resource.environment["RAILS_ENV"]
Chef::Log.info("Precompiling assets for RAILS_ENV=#{rails_env}...")
execute "rake assets:precompile" do
cwd release_path
command "bundle exec rake assets:precompile"
environment "RAILS_ENV" => rails_env
end
This get called during the deployment process and compiles the assets.
In AWS Opsworks I use the following recipe:
execute 'rake assets:precompile' do
cwd "#{node[:deploy_to]}/current"
user 'root'
command 'bundle exec rake assets:precompile'
environment 'RAILS_ENV' => node[:environment_variables][:RAILS_ENV]
end
I run this command as root because the instance needs the right permission to write to the release
path. Running the command as the deploy
user renders a permission denied error.