summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlhuda <carlhuda@engineyard.com>2010-07-29 18:51:09 -0700
committerCarlhuda <carlhuda@engineyard.com>2010-07-29 18:51:25 -0700
commitcb3ec8eb602f2af410e935cc2ad4a61e777de845 (patch)
tree5ad5dcd405250b82f45da285f44cbd2e3cb5c35a
parent0ae574c8dbdfb73c3e02f9d4e1c6d98e491e97df (diff)
downloadbundler-cb3ec8eb602f2af410e935cc2ad4a61e777de845.tar.gz
Update --production to yell if the user changed their Gemfile without updating their lockfile
-rw-r--r--lib/bundler.rb9
-rw-r--r--lib/bundler/cli.rb14
-rw-r--r--lib/bundler/definition.rb66
-rw-r--r--spec/install/production_spec.rb109
4 files changed, 196 insertions, 2 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb
index de89df213b..26cd342933 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -41,6 +41,7 @@ module Bundler
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 @@ module Bundler
end
end
+ def production?
+ @production
+ end
+
+ def production=(value)
+ @production = value
+ end
+
def ui
@ui ||= UI.new
end
diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb
index 3a067a95a2..fe1b7a69eb 100644
--- a/lib/bundler/cli.rb
+++ b/lib/bundler/cli.rb
@@ -122,8 +122,18 @@ module Bundler
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
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 46a8db9fe5..98e6060c46 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -67,6 +67,10 @@ module Bundler
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 @@ module Bundler
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|
diff --git a/spec/install/production_spec.rb b/spec/install/production_spec.rb
new file mode 100644
index 0000000000..7d74bdb999
--- /dev/null
+++ b/spec/install/production_spec.rb
@@ -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