I have 2 environments for my app on heroku: staging and production, and I'm using Rails 3.1 assets pipeline.
I have set a custom config.action_controller.asset_host property to make my assets fetched from cloudfront on http://assets.myapp-staging.com or http://assets.myapp.com
The issue is that on Heroku, assets are always precompiled with the production environment. Which means that all the urls present in my css or js files target http://assets.myapp.com even in staging.
Any idea?
Your best bet is to do one of two things:
1) Use the new user-env-config labs add-on which makes your environment variables available during the slug compilation phase.
To use this simply:
$ heroku labs:enable user-env-compile -a myapp
-----> Enabling user-env-compile for myapp... done
WARNING: This feature is experimental and may change or be removed without notice.
For more information on this see here:
http://devcenter.heroku.com/articles/labs-user-env-compile
or
2) Don't use those assets. With the Asset Sync gem you can run the compilation of the assets once the application has been spun up in it's evironment. This will then sync your assets to S3.
For more information on this, check out:
http://devcenter.heroku.com/articles/cdn-asset-host-rails31
Related
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.
I'm fairly new to the whole asset pipeline concept, I put the third party js file (as well as the css and image files) in the vendor/assets folder, allow me to give a quick example how I did it:
where the file is stored:
vendor/assets/javascript/plugins/isotope/isotope.pkgd.min.js
how it's referenced in the application.js:
//= require plugins/isotope/isotope.pkgd.min
and how it's referenced in the index.html.erb
<script src="/assets/plugins/isotope/isotope.pkgd.min.js"></script>
it works just fine locally, but after I deployed the app onto heroku, I got http://myapp.herokuapp.com/assets/plugins/isotope/isotope.pkgd.min.js not found error, without it, my app does not work...I kinda know part of the asset pipeline's job is to compress all the js files (the ones referenced in the application.js) into one single js file, does not mean I will need to refer isotope.pkgd.min.js differently in my html.erb?
There are several steps in debugging an asset pipeline problem. You didn't mention you'd done an asset pre-compilation before deploy. Heroku does not compile assets upon deploy automatically. Here's their text on the matter
If a public/assets/manifest.yml is detected in your app, Heroku will assume you are handling asset compilation yourself and will not attempt to compile your assets. Rails 4 uses a file called public/assets/manifest-.json instead. On both versions you can generate this file by running $ rake assets:precompile locally and checking the resultant files into Git.
https://devcenter.heroku.com/articles/rails-asset-pipeline
A simple test would be to precompile your assets (and git checkin, if you're using git push Heroku) before Heroku deploy and see if that doesn't resolve the issue.
On your dev platform:
bundle rake exec assets:precompile RAILS_ENV=production
My rails app is working but its slug size is huge (220mb).
I am storing all assets on cloudfront with config.assets.digest = true and config.action_controller.asset_host = "something.cloudfront.net"
heroku run bash
In the slug, there is 30 times application-onedigest.js in public/assets, and same thing for stylesheets and images, so now the slug size is 220 mb.
Maybe one of my gem is doing middleware things (Rails asset_host, cloudfront and heroku) or maybe heroku is trying to optimize things (https://devcenter.heroku.com/changelog-items/328), but i don't think so. I don't want to exceed the maximum heroku slug size that is 300 mb, and i feel that is not normal.
How to remove old rails assets on heroku ?
EDIT
I made little commits in a css file, push to heroku, and compared slug. In the deploy process, there is those lines:
Running: rake assets:precompile
INFO -- : Writing /tmp/build_ff4eb6d7-303e-444d-9c88-938ab504ea8a/public/assets/application-700c7e1849c55312a94a353e60312500.css
Asset precompilation completed (9.60s)
Cleaning assets
Running: rake assets:clean
INFO -- : Removed application-48375ba5495e14b36afab9d4b9d97033.css
This is what i want. But when i compare the old and the new slug, there is the new application-700....css, and an another new mysterious application-a9b33ae58934ad161038cb3ebcee146c.css
And this one is actually used is the heroku webapp (when i explore css resources from the browser, after clearing cache).
So i guess precompile is done twice somewhere?
Temporary solution : heroku fork the app, i am back to 40 mb but there is still the problem.
EDIT
It seems it works when i precompile locally, but i don't like it as it violates 12factors.
Some things you'll want to consider:
rake assets:clobber
Heroku's file system
CDN
rake assets:clobber is an inbuilt way to remove old asset files from your public/assets directory. By running this command, either locally or on Heroku, you'll be able to actively remove the precompiled asset files on your system, allowing you to repopulate the folder with new files
Secondly, you have to remember Heroku runs an ephemeral file system. This means that each time you push your application to Heroku, it will just overwrite any of the files you had before, meaning (importantly), that it's not going to store your last assets. Each time you push, it will precompile the assets for you - which should ensure minimal space taken up with them.
--
Finally, if you find that your assets are taking up too much space on your application server, you may consider using a content delivery network (CDN) to serve the assets you need. You can use the asset_sync gem to push your assets to your CDN of choice
Most people tend to use a CDN with storage system to serve their assets. We use S3 with Cloudfront currently, but are looking to move to Rackspace's file serve system!
You can use the Heroku repo plugin (https://github.com/heroku/heroku-repo) and the repo:purge command to clean all the build assets out of your slug.
Using asset_sync gem is not ideal as you can run into deploy timeouts pushing all the assets over to S3 plus you end up with assets in two places. Remember S3 is not a content delivery network, you're better off using a CloudFront distribution and have it cache the assets directly from your application. See https://devcenter.heroku.com/articles/using-amazon-cloudfront-cdn for more details.
This solve it for me, in production.rb :
config.assets.compile = false
Heroku is no longer generating useless assets. I don't know how to remove the old assets in the same heroku app, but i used "heroku fork" to create a brand new heroku app with the same config / database and without old assets.
heroku run rake assets:clobber
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
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...