Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
bummr (1.1.1a)
bummr (1.2.0)
rainbow
thor

Expand Down
17 changes: 9 additions & 8 deletions lib/bummr/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
7 changes: 7 additions & 0 deletions lib/bummr/git.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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}'")
Expand Down
39 changes: 26 additions & 13 deletions lib/bummr/updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/bummr/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Bummr
VERSION = "1.1.1a"
VERSION = "1.2.0"
end
2 changes: 1 addition & 1 deletion spec/lib/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
91 changes: 55 additions & 36 deletions spec/lib/updater_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,36 @@

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)
end
end
end

# ----------------

describe "#update_gem" do
# Ensure this directory exists to be added
before do
Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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
Expand Down