diff --git a/Gemfile.lock b/Gemfile.lock index 79d897c..2343afb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - bummr (1.1.1a) + bummr (1.2.0) rainbow thor diff --git a/lib/bummr/cli.rb b/lib/bummr/cli.rb index a444fc2..c5afa1d 100644 --- a/lib/bummr/cli.rb +++ b/lib/bummr/cli.rb @@ -15,13 +15,14 @@ def check(fullcheck=true) end # :nocov: end - desc "update", - "Update outdated gems, run tests, bisect if tests fail\n\n" + - "--all: Update indirect dependencies\n" + - "--group: Specify a group from the Gemfile to update\n" + - "--gem: Specify a specific gem to update\n" + - "\n" - + desc "update", "Update outdated gems, run tests, bisect if tests fail (options: --all, --group, --gem)" + long_desc <<-LONGDESC + Update outdated gems > Interactive rebase to pick-and-choose finalized gem updates > Run tests > Bisect if any test fails + \x5Options: + \x5 --all: Update indirect dependencies + \x5 --group: Specify a group from the Gemfile to update + \x5 --gem: Specify a specific gem to update + LONGDESC method_option :all, type: :boolean, default: false method_option :group, type: :string @@ -42,7 +43,7 @@ def update if outdated_gems.empty? puts "No outdated gems to update".color(:green) else - Bummr::Updater.new(outdated_gems).update_gems + Bummr::Updater.new(outdated_gems).update_outdated_gems git.rebase_interactive(BASE_BRANCH) test diff --git a/lib/bummr/git.rb b/lib/bummr/git.rb index 6a832e8..3153ed5 100644 --- a/lib/bummr/git.rb +++ b/lib/bummr/git.rb @@ -11,6 +11,13 @@ def add(files) system("git add #{files}") end + # NOTE: during active development this will catch any other files you may be working on + # so don't leave random files staged without committing them + def files_staged? + # exit code 1 when there are files staged for commit + !system("git diff --staged --quiet") + end + def commit(message) log "Commit: #{message}".color(:green) system("#{git_commit} -m '#{message}'") diff --git a/lib/bummr/updater.rb b/lib/bummr/updater.rb index 827f854..3879b09 100644 --- a/lib/bummr/updater.rb +++ b/lib/bummr/updater.rb @@ -7,7 +7,7 @@ def initialize(outdated_gems) @outdated_gems = outdated_gems end - def update_gems + def update_outdated_gems puts "Updating outdated gems".color(:green) @outdated_gems.each_with_index do |gem, index| @@ -19,31 +19,44 @@ def update_gem(gem, index) puts "Updating #{gem[:name]}: #{index + 1} of #{@outdated_gems.count}" system("bundle update #{gem[:name]}") - updated_version = updated_version_for(gem) + # Get the current (maybe new?) bundled version for this gem + bundled_version = bundled_version_for(gem) - message = if updated_version - "Update #{gem[:name]} from #{gem[:installed]} to #{updated_version}" - else - "Update dependencies for #{gem[:name]}" - end - - if gem[:installed] == updated_version + # If the gem could not be updated at all + if gem[:installed] == bundled_version log("#{gem[:name]} not updated") - return - end + # might still be dependency updates, so cannot stop here - if gem[:newest] != updated_version + # If the gem was updated, but not to latest + elsif gem[:newest] != bundled_version log("#{gem[:name]} not updated from #{gem[:installed]} to latest: #{gem[:newest]}") end git.add("Gemfile") git.add("Gemfile.lock") git.add("vendor/cache") + + return unless git.files_staged? + # ... something was changed + + # When the targeted gem itself is not modified, one of its dependencies must have been + if gem[:installed] == bundled_version + message = "Update #{gem[:name]} dependencies" + else + message = "Update #{gem[:name]} from #{gem[:installed]} to #{bundled_version}" + end git.commit(message) end - def updated_version_for(gem) + # NOTE: cannot be private method because it is tested specifically by updater_spec.rb + def bundled_version_for(gem) string = %x{bundle list --paths | grep "#{gem[:name]}"} + if string.empty? + # :nocov: We don't need to test when an exception happens + # Raise a understandable error message + raise "FATAL: Unable to find '#{gem[:name]}' within 'bundle list --paths'." + # :nocov: end + end string.match(/#{File::SEPARATOR}#{gem[:name]}-(.*)$/)[1] end end diff --git a/lib/bummr/version.rb b/lib/bummr/version.rb index 20eccd7..8907d82 100644 --- a/lib/bummr/version.rb +++ b/lib/bummr/version.rb @@ -1,3 +1,3 @@ module Bummr - VERSION = "1.1.1a" + VERSION = "1.2.0" end diff --git a/spec/lib/cli_spec.rb b/spec/lib/cli_spec.rb index 497cb13..783199c 100644 --- a/spec/lib/cli_spec.rb +++ b/spec/lib/cli_spec.rb @@ -33,7 +33,7 @@ context "when user agrees to move forward" do def mock_bummr_standard_flow updater = double - allow(updater).to receive(:update_gems) + allow(updater).to receive(:update_outdated_gems) expect(cli).to receive(:display_info) expect(cli).to receive(:yes?).and_return(true) diff --git a/spec/lib/updater_spec.rb b/spec/lib/updater_spec.rb index 5f0dbbe..c3bb72d 100644 --- a/spec/lib/updater_spec.rb +++ b/spec/lib/updater_spec.rb @@ -7,25 +7,27 @@ let(:outdated_gems) { [ - { name: "myGem", installed: "0.3.2", newest: "0.3.5" }, + { name: "myGem", installed: "0.3.2", newest: "0.3.5" }, { name: "otherGem", installed: "1.3.2.23", newest: "1.6.5" }, - { name: "thirdGem", installed: "4.3.4", newest: "5.6.45" }, + { name: "thirdGem", installed: "4.3.4", newest: "5.6.45" }, ] } let(:gem) { outdated_gems[0] } + let(:updater) { described_class.new(outdated_gems) } - let(:newest) { outdated_gems[0][:newest] } - let(:installed) { outdated_gems[0][:installed] } + + let(:newest_version) { outdated_gems[0][:newest] } + let(:installed_version) { outdated_gems[0][:installed] } let(:intermediate_version) { "0.3.4" } - let(:update_cmd) { "bundle update #{gem[:name]}" } + let(:git) { Bummr::Git.instance } - describe "#update_gems" do + describe "#update_outdated_gems" do it "calls update_gem on each gem" do allow(updater).to receive(:update_gem) allow(updater).to receive(:puts) # NOOP this function call - updater.update_gems + updater.update_outdated_gems outdated_gems.each_with_index do |gem, index| expect(updater).to have_received(:update_gem).with(gem, index) @@ -33,6 +35,8 @@ end end + # ---------------- + describe "#update_gem" do # Ensure this directory exists to be added before do @@ -42,54 +46,68 @@ %x{rm -rf vendor/cache} end - def mock_log_commit_puts + def mock_system_log_commit_puts + allow(updater).to receive(:system) allow(updater).to receive(:log) allow(updater).to receive(:puts) # NOOP this function call allow(git).to receive(:commit) end it "attempts to update the gem" do - allow(updater).to receive(:system).with(update_cmd) - allow(updater).to receive(:updated_version_for).with(gem).and_return installed - mock_log_commit_puts + allow(updater).to receive(:bundled_version_for).with(gem).and_return installed_version + mock_system_log_commit_puts updater.update_gem(gem, 0) + + # NOTE: No assertions, so this is just a smoke-test (ensure the code runs without an exception) end - context "not updated at all" do - it "logs that it's not updated to the latest" do - allow(updater).to receive(:system).with(update_cmd) - allow(updater).to receive(:updated_version_for).with(gem).and_return installed - mock_log_commit_puts + context "gem not updated" do + before(:each) do + allow(updater).to receive(:bundled_version_for).with(gem).and_return installed_version + mock_system_log_commit_puts + end + it "logs that it was not updated" do updater.update_gem(gem, 0) expect(updater).to have_received(:log).with("#{gem[:name]} not updated") end - it "doesn't commit anything" do - allow(updater).to receive(:system).with(update_cmd) - allow(updater).to receive(:updated_version_for).with(gem).and_return installed - mock_log_commit_puts + context "and no dependencies were updated" do + it "doesn't commit anything" do + updater.update_gem(gem, 0) - updater.update_gem(gem, 0) + expect(git).to_not have_received(:commit) + end + end + context "and dependencies were updated" do + it "commits" do + commit_message = + "Update #{gem[:name]} dependencies" + + allow(git).to receive(:add) + allow(git).to receive(:files_staged?).and_return true - expect(git).to_not have_received(:commit) + updater.update_gem(gem, 0) + + expect(git).to have_received(:add).with("Gemfile") + expect(git).to have_received(:add).with("Gemfile.lock") + expect(git).to have_received(:add).with("vendor/cache") + expect(git).to have_received(:commit).with(commit_message) + end end end - context "not updated to the newest version" do + context "gem not updated to the newest version" do before(:each) do - allow(updater).to receive(:updated_version_for).with(gem).and_return( - intermediate_version - ) + allow(updater).to receive(:bundled_version_for).with(gem).and_return intermediate_version + mock_system_log_commit_puts end it "logs that it's not updated to the latest" do not_latest_message = "#{gem[:name]} not updated from #{gem[:installed]} to latest: #{gem[:newest]}" - allow(updater).to receive(:system) - mock_log_commit_puts updater.update_gem(gem, 0) @@ -100,9 +118,8 @@ def mock_log_commit_puts commit_message = "Update #{gem[:name]} from #{gem[:installed]} to #{intermediate_version}" - allow(updater).to receive(:system) allow(git).to receive(:add) - mock_log_commit_puts + allow(git).to receive(:files_staged?).and_return true updater.update_gem(gem, 0) @@ -113,18 +130,18 @@ def mock_log_commit_puts end end - context "updated the gem to the latest" do + context "gem updated to the latest" do before(:each) do - allow(updater).to receive(:updated_version_for).and_return newest + allow(updater).to receive(:bundled_version_for).with(gem).and_return newest_version + mock_system_log_commit_puts end it "commits" do commit_message = "Update #{gem[:name]} from #{gem[:installed]} to #{gem[:newest]}" - allow(updater).to receive(:system) allow(git).to receive(:add) - mock_log_commit_puts + allow(git).to receive(:files_staged?).and_return true updater.update_gem(gem, 0) @@ -136,13 +153,15 @@ def mock_log_commit_puts end end # end #update_gem - describe "#updated_version_for" do + # ---------------- + + describe "#bundled_version_for" do it "returns the correct version from bundle list" do allow(updater).to receive(:`).with( "bundle list --paths | grep \"#{gem[:name]}\"" ).and_return("asdf/asdf/asdf/#{gem[:name]}-3.5.2") - expect(updater.updated_version_for(gem)).to eq "3.5.2" + expect(updater.bundled_version_for(gem)).to eq "3.5.2" end it "returns the correct version when there are similarly named gems" do