Better Rails 3 Asset Handling for `cap deploy` - ruby-on-rails

I notice the default "deploy" task does the asset precompilation on the remote machine, which has these ill effects:
Weird glitchy assets during the time it's precompiling (on the live site)
Errors in the config cause downtime with a 500 response code
The task to take a long time
I've seen this, which mitigates the problem a tiny bit by not causing you to precompile when you don't have anything to precompile: Speed up assets:precompile with Rails 3.1/3.2 Capistrano deployment
But there must be a better solution.
Has anyone tried these:
Always deploy to a "staging" location, where you can test everything out, then add some kind of cap enliven task that somehow tells the web server frontend to start using the other port? (I could manually manage this with editing the nginx upstream and restarting it, then I could automate that a bit with an include in the nginx.conf and a cap task to tie it together.)
Precompile locally then simply push the files over via rsync. I prefer #1, but this would be a smaller step that would probably work as a better default than the current behavior.
Am I missing something obvious? I'm am new to Rails assets + Capistrano deployment, but it seems like the best practices of deployment aren't available right out of the box on this one.

You can:
precompile assets before deploy
compile it server-side in background
leave it to delayed_job or another queue management system

Related

heroku precompile assets is it necessary

I've started learning rails and I've already built two apps, one simple blog app and one store app. Now I ran into a term precompile assets when uploading to heroku, can someone explain it to me is that necessary when deploying an app to production, because i've uploaded my store app to heroku without any problems?
Assets is your css + JS. Precompile assets mean that they get joined into single .css and another single .js. file (to load it in one HTTP request). And special mechanism of minifying get applied to both these files (to make them smaller). Rails by default is setup in a way, that it uses average files in dev and compiled files in prod. You can easily change this in configs, but you shouldn't do this unless you really know what you do.
If you want you can compile this files locally running rake assets:precompile and then put it into git. I think that you can disable/enable precompile during heroku deploy in heroku config. But, in general, I would stick with the very defaults.
More info on asset pipeline: http://guides.rubyonrails.org/asset_pipeline.html
Rails has an assets pipeline which consists of Sprockets and the assets helpers.
The assets pipeline will concat and minify your CSS and javascript and takes care of setting the correct paths to images and other assets. This is known as compiling the assets.
In development this is done on the fly for each request which lets you immediately see changes.
In production this would be far to slow so instead the assets should be compiled once at deploy time. Heroku does this automatically for you in a post-commit hook.
Pre-compiling is when you run rake assets:precompile locally and then upload or push the result to a server. This is done if you are deploying to a server without the support for the assets pipeline. For example if the production server does not have a javascript runtime which is required to run uglifier.
It adds tons of noise to the git change history and manually doing anything is a common source of user error. So pretty much it sucks and you only do it if you have to.

Rails Openshift Hot Deploy assets not served

i have a Rails application deployed on Openshift. I added marker for hot deploing and hot deploying itself works fine, but during the time application is hot deployed css and js files are not served. When hot deployment ends these files work fine again. I also use Bootstrap and Sass in this application (gem 'bootstrap-sass'). Do you have any idea why this happens?
The files are being served by Apache via the Passenger module. The files are being replaced "in place" which causes them to be removed/rebuilt, which is causing them to not be served during that time, and since they are static assets, they are not stored in memory. Unfortunately there is currently no way to make hot deploy work fully with Rails to keep the site 100% working while it is deployed.
One solution is to have your assets in a separate running project, since there is no easy way to have them available as all times as #developercorey explains..
It's probably not the best solution, but would be a simple patch-solution which is not tightly coupled to one particular hosting platform.
I fixed this issue, and it works now. I will explain what I did, maybe it will help somebody.
Basically there is need to precompile your assets locally, and commit and push them. This is done byrake assets:precompile RAILS_ENV=production
But there is a gotcha!!! Locally precompiled assets doesn't match those that are generated on Openshift. How is this possible? There is a bug on Openshift, that assets are generated on production with RAILS_ENV=development :/ More info here:
https://github.com/openshift/origin-community-cartridges/issues/8
so there is need to add environmental variable on your application:
rhc set-env RAILS_ENV=production -a app_name
then generated assets match.
So after fixing it, when during changes to assets, we need to precompile them again. And to make them work during hot deploying there is need to have both old precompiled assets and new precompiled assets in repo. For example:
If you have old file:
application-10770925dc8abd4ceab34119af4032163cc5a94f3523d60d321f33a999171d58.cssand new precoimpiled file:
application-82f6fca47056cbda52cb32086051f031b880e2630a137f0e41e96cb2eef923ee.css
they both have to be in repository. During hot deploying old asset is still referenced, so it has to be in repository. After hot deploying ends, new asset is referenced. In the next commit and push old asset may be removed.
So basically this issue is fixed for me, and hot deploying works fine now.

Rails Deployment - Run assets:precompile once per deployed environment or only once per build?

TL;DR: Running assets:precompile injects production asset hosts into the generated assets for non-prod environments.
Background: The current way we deploy our Rails app is that the CI server deploys each successful build to the integration env as a tarball. And this tarball keeps getting promoted all the way to the prod env. But even before we tar the app to promote to different environments, we run
rake assets:precompile
Once this command is run before tarring, we ended up with the compiled assets as a part of the tarball and this saves deployment time for individual environments (The precompile is sloow).
Problem:
This arrangement worked fine until we introduced the asset_host property in the production environment. Because the assets:precompile is run in Production env by default, and the sass files refer to image assets using the image-url tag, the asset host started getting picked up by the precompile and the generated assets started having direct URL references to the production asset_host's servers. Obviously this is not acceptable.
Searching on the internet led to this Github Issue which is a pretty close description of the problem I'm having. Seeing the reaction of the gem maintainers, it seems like running assets:precompile once for ALL environments instead of once PER environment seems like a bad idea. But given slow precompile times, it seems to be the only way our for us.
So how are other Rails deployments dealing with this issue?
I was solving very similar problem. Our solution was to run the pre-compilation for every individual environment - in our case that were staging and production environments. The reason was the same - different asset hosts.
If you can, run the pre-compilation only in environments where it is necessary and where assets are really needed.
We are using Capistrano to do deployments along with the capistrano-ext gem. This allows us to specify different stages and to each stage settings and methods that are specific for the stage (or environments if stage == environment).
check public/assets/manifest.yml into source control

Should I precompile assets locallly before deployment on on the server?

I have the option to precompile my assets locally in development or on my production server. I deploy with git, so I'd prefer not to have to check in all these assets (especially if they're using cache-busting digests).
Is there any advantage to precompiling assets locally (other than lacking write-access on the production machine)?
Your site doesn't need to be down when you precompile assets. If you use capistrano or similar tools, you precompile the assets in the server, then (after this and more steps have been completed) restart the app. Meanwhile the app is being served from the old code (and assets).
On the other side, I disagree about the "cache-busting" comment. Git is smart enough to understand a diff between two differently named files if possible. So the result would be exactly the same whether the names changed or not. In which case I completely agree that it's nonsensical to load the repository with generated data, like compiled assets.
I've found that compiling assets locally is much faster and then your site is down for a shorter period.
Of course, that depends on your server setup etc...

Conditionally disable asset precompile in Capistrano

I've seen various convoluted and generally ineffective solutions to performing lazy asset precompile in Rails. As a backend developer I don't particularly want to recompile assets I never touch every time the program deploys, but because assets are loaded in Capfile via load 'deploy/assets', and not by defining a task in deploy.rb, I can't think of a way to conditionally disable it.
The behaviour I'm after is to use cap deploy for regular with-precompile deployment, and to use cap deploy:no_assets to skip asset deployment.
Both turbo-sporocket-rails and the that auto-skip scripts have some pitfalls (I will mention later). So I use the following hack, so I can pass a parameter to skip asset pre-compile at my will:
callback = callbacks[:after].find{|c| c.source == "deploy:assets:precompile" }
callbacks[:after].delete(callback)
after 'deploy:update_code', 'deploy:assets:precompile' unless fetch(:skip_assets, false)
This script will change the built-in asset-precompile hook, so it will be hooked based on the skip_assets parameter. I can call cap deploy -S skip_assets=true to skip asset precompile as a whole.
For me, turbo-sporocket-rails still takes minutes to do the checking when nothing has changed. This can be crucial when I need to push a fix to the server asap. Therefore I need my force-skipping method.
rails4 addresses this issue with it's new version of sprockets, by only precompiling assets that have changed. In the mean time, for your rails3 apps I recommend the turbo-sprockets-rails3 gem.
This gem started out as a set of patches for sprockets-rails by Nathan Broadbent, which were not merged into master because the problem was already addressed in rails4. From the README:
Speeds up your Rails 3 rake assets:precompile by only recompiling changed assets, based on a hash of their source files
Only compiles once to generate both fingerprinted and non-fingerprinted assets
And:
turbo-sprockets-rails3 should work out of the box with the latest version of Capistrano.
I can confirm that it works well for me on rails-3.2.x apps deploying with Capistrano.
As a side note for GitHubbers, the original pull request is an excellent example of how to submit code to an open source project, even if it wasn't merged.
This gist looks very promising https://gist.github.com/3072362
It checks your git log from the last deploy to now to see if there are any changes in %w(app/assets lib/assets vendor/assets Gemfile.lock config/routes.rb) and if so, only precompiles then.

Resources