diff options
author | Carlhuda <carlhuda@engineyard.com> | 2010-07-29 18:51:09 -0700 |
---|---|---|
committer | Carlhuda <carlhuda@engineyard.com> | 2010-07-29 18:51:25 -0700 |
commit | cb3ec8eb602f2af410e935cc2ad4a61e777de845 (patch) | |
tree | 5ad5dcd405250b82f45da285f44cbd2e3cb5c35a | |
parent | 0ae574c8dbdfb73c3e02f9d4e1c6d98e491e97df (diff) | |
download | bundler-cb3ec8eb602f2af410e935cc2ad4a61e777de845.tar.gz |
Update --production to yell if the user changed their Gemfile without updating their lockfile
-rw-r--r-- | lib/bundler.rb | 9 | ||||
-rw-r--r-- | lib/bundler/cli.rb | 14 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 66 | ||||
-rw-r--r-- | spec/install/production_spec.rb | 109 |
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 |