Problem
Our deployed application has development dependencies in it. We have a lot of development dependencies. This increases the artifact size and the memory consumption in production, as all those dependencies are require
'd. Most instances are deployed in the cloud, so more memory = more money for larger instances. We would like to reduce the size/memory and make a more clear separation between the deployed artifact and the development environment. A particular focus is the need for therubyrhino
in production environments even though our assets are precompiled.
Context
This question has some extremely highly upvoted comments that are asking the same thing (see this one and this one), but I don't actually see any answers.
Looking at the Rails upgrade guide, the following is suggested:
As well, asset precompilation is done with the following command:RAILS_ENV=production rake assets:precompile
As discussed in the linked question, this means that all gems are required in production. Fundamentally this doesn't make sense to me, and I feel like I'm missing something obvious. The whole point of asset precompilation is that we're avoiding doing it in production, so this command (so far as I understand it) should be something along the lines of:RAILS_ENV=development RAILS_ENV_TARGET=production rake assets:precompile
Or some business.
I've read the discussion on an old Rails ticket here, and it seems to leave the question unanswered - how do we get development dependencies out of the production environment? One user in particular sums up the same problem here
It still strikes me that memory-bloat-by-default with things like therubyracer in production is poor, particularly if precompiling is core's recommendation and a widely-held best practice at this point. Many people likely never stop to consider that that's even happening if they came to Rails after the assets group removal or never gave much thought to it serving that purpose -- at least a suggestive comment in the generated Gemfile might be a good idea.
It's now a yak shave for developers to work around this for gems they know are unneeded in production web or worker processes since loading the group was removed from the precompile task. I'm basically now including this as boilerplate in new apps:
namespace :assets do # Override sprockets-rails task to put back assets group require, so as to # avoid memory bloat in web processes :-/ task :environment do Bundler.require(:assets) Rake::Task['environment'].invoke end end
plus restoringBundler.require(*Rails.groups(assets: %w[development test]))
toconfig/application.rb
. What a mess.FYI, du reports therubyracer as 17MB on my machine, and it doesn't use autoload. We're not using any CoffeeScript view templates.
The author of that comment suggests a workaround, but later on in the thread deficiencies are discussed with that strategy, which makes me nervous.
tl;dr:
How do we remove development dependencies from production run-time? Alternatively, what am I missing as to why this ability would be desirable/the default?
How do we remove development dependencies from production run-time?
This comment in the thread you referenced has instructions for enabling the old :assets
group behavior:
Change Bundler.require(*Rails.groups)
to Bundler.require(*Rails.groups(assets: %w[development test]))
in config/application.rb
, and add this to your rake tasks:
namespace :assets do
# Override sprockets-rails task to put back assets group require, so as to
# avoid memory bloat in web processes :-/
task :environment do
Bundler.require(:assets)
Rake::Task['environment'].invoke
end
end
Alternatively, what am I missing as to why this ability would be desirable/the default?
So, strictly speaking, these aren't development dependencies. You shouldn't really think of them that way, because asset precompilation should happen in a production environment, even if it's just during deploy. You definitely shouldn't be precompiling on your developer machine.
On top of that, the line between asset-specific gems and production gems has become more blurred since the earlier versions of the asset pipeline. For instance, many gems now expect a javascript interpreter to be available. Also, many Rails apps now use .coffee
templates in views (instead of .js.erb
), and because those can't be precompiled, coffeescript must be available in production.
Basically, as rails contributors started removing more and more gems from the :assets
group, they realized that it didn't really need to exist anymore, and would simplify things if it just went away. It had only existed in the first place so to avoid unintended compilation-on-demand, but the asset pipeline was updated in Rails 4 to expect to serve only static assets by default.
In the end it may not have been the most memory-optimal decision (since you're require
ing a bunch of gems you don't ever use) but it was the most universally compatible.
Edit: Also refer to this question for more discussion/answers to this question.
来源:https://stackoverflow.com/questions/39756206/remove-development-gems-from-production-with-bundler-and-rails-4