Permalink
Browse files

Update --production to yell if the user changed their Gemfile without…

… updating their lockfile
  • Loading branch information...
1 parent 0ae574c commit cb3ec8eb602f2af410e935cc2ad4a61e777de845 Carlhuda committed Jul 30, 2010
Showing with 196 additions and 2 deletions.
  1. +9 −0 lib/bundler.rb
  2. +12 −2 lib/bundler/cli.rb
  3. +66 −0 lib/bundler/definition.rb
  4. +109 −0 spec/install/production_spec.rb
View
@@ -41,6 +41,7 @@ class GemspecError < BundlerError; status_code(14) ; end
class DeprecatedError < BundlerError; status_code(12) ; end
class GemspecError < BundlerError; status_code(14) ; end
class DslError < BundlerError; status_code(15) ; end
+ class ProductionError < BundlerError; status_code(16) ; end
class InvalidOption < DslError ; end
class VersionConflict < BundlerError
@@ -67,6 +68,14 @@ def configure
end
end
+ def production?
+ @production
+ end
+
+ def production=(value)
+ @production = value
+ end
+
def ui
@ui ||= UI.new
end
View
@@ -122,8 +122,18 @@ def install(path = nil)
exit 1
end
- if opts[:production] && Bundler.root.join("vendor/cache").exist?
- opts["local"] = true
+ if opts[:production]
+ Bundler.production = true
+
+ unless Bundler.root.join("Gemfile.lock").exist?
+ raise ProductionError, "The --production flag requires a Gemfile.lock. Please\n" \
+ "make sure you have checked your Gemfile.lock into version\n" \
+ "control before deploying."
+ end
+
+ if Bundler.root.join("vendor/cache").exist?
+ opts["local"] = true
+ end
end
# Can't use Bundler.settings for this because settings needs gemfile.dirname
View
@@ -67,6 +67,10 @@ def initialize(lockfile, dependencies, sources, unlock)
current_platform = Gem.platforms.map { |p| generic(p) }.compact.last
@platforms |= [current_platform]
+ if Bundler.production?
+ ensure_equivalent_gemfile_and_lockfile
+ end
+
converge_sources
converge_dependencies
end
@@ -204,6 +208,68 @@ def to_lock
end
private
+ def ensure_equivalent_gemfile_and_lockfile
+ changes = false
+
+ msg = "You have modified your Gemfile in development but did not check\n" \
+ "the resulting snapshot (Gemfile.lock) into version control"
+
+ added = []
+ deleted = []
+ changed = []
+
+ if @locked_sources != @sources
+ new_sources = @sources - @locked_sources
+ deleted_sources = @locked_sources - @sources
+
+ if new_sources.any?
+ added.concat new_sources.map { |source| "* source: #{source}" }
+ end
+
+ if deleted_sources.any?
+ deleted.concat deleted_sources.map { |source| "* source: #{source}" }
+ end
+
+ changes = true
+ end
+
+ both_sources = Hash.new { |h,k| h[k] = ["no specified source", "no specified source"] }
+ @dependencies.each { |d| both_sources[d.name][0] = d.source if d.source }
+ @locked_deps.each { |d| both_sources[d.name][1] = d.source if d.source }
+ both_sources.delete_if { |k,v| v[0] == v[1] }
+
+ if @dependencies != @locked_deps
+ new_deps = @dependencies - @locked_deps
+ deleted_deps = @locked_deps - @dependencies
+
+ if new_deps.any?
+ added.concat new_deps.map { |d| "* #{pretty_dep(d)}" }
+ end
+
+ if deleted_deps.any?
+ deleted.concat deleted_deps.map { |d| "* #{pretty_dep(d)}" }
+ end
+
+ both_sources.each do |name, sources|
+ changed << "* #{name} from `#{sources[0]}` to `#{sources[1]}`"
+ end
+
+ changes = true
+ end
+
+ msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
+ msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
+ msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
+
+ raise ProductionError, msg if added.any? || deleted.any? || changed.any?
+ end
+
+ def pretty_dep(dep, source = false)
+ msg = "#{dep.name}"
+ msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
+ msg << " from the `#{dep.source}` source" if source && dep.source
+ msg
+ end
def converge_sources
@sources.map! do |source|
@@ -0,0 +1,109 @@
+require "spec_helper"
+
+describe "install with --production" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "fails without a lockfile" do
+ bundle "install --production"
+ out.should include("The --production flag requires a Gemfile.lock")
+ end
+
+ describe "with an existing lockfile" do
+ before do
+ bundle "install"
+ end
+
+ it "works if you didn't change anything" do
+ bundle "install --production", :exit_status => true
+ exitstatus.should == 0
+ end
+
+ it "explodes if you make a change and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle "install --production"
+ out.should include("You have modified your Gemfile")
+ out.should include("You have added to the Gemfile")
+ out.should include("* rack-obama")
+ out.should_not include("You have deleted from the Gemfile")
+ out.should_not include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you remove a gem and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle "install --production"
+ out.should include("You have modified your Gemfile")
+ out.should include("You have added to the Gemfile:\n* activesupport\n\n")
+ out.should include("You have deleted from the Gemfile:\n* rack")
+ out.should_not include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you add a source" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ bundle "install --production"
+ out.should include("You have modified your Gemfile")
+ out.should include("You have added to the Gemfile:\n* source: git://hubz.com (at master)")
+ out.should_not include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source" do
+ build_git "rack"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-1.0")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "install --production"
+ out.should include("You have modified your Gemfile")
+ out.should include("You have deleted from the Gemfile:\n* source: #{lib_path("rack-1.0")} (at master)")
+ out.should_not include("You have added to the Gemfile")
+ out.should_not include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source, leaving it pinned somewhere else" do
+ build_lib "foo", :path => lib_path("rack/foo")
+ build_git "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack")}"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ bundle "install --production"
+ out.should include("You have modified your Gemfile")
+ out.should include("You have changed in the Gemfile:\n* rack from `no specified source` to `#{lib_path("rack")} (at master)`")
+ out.should_not include("You have added to the Gemfile")
+ out.should_not include("You have deleted from the Gemfile")
+ end
+ end
+end

0 comments on commit cb3ec8e

Please sign in to comment.