diff --git a/Library/Homebrew/cask/lib/hbc/cask.rb b/Library/Homebrew/cask/lib/hbc/cask.rb index 3f8da25b7f..cf5f2b37a9 100644 --- a/Library/Homebrew/cask/lib/hbc/cask.rb +++ b/Library/Homebrew/cask/lib/hbc/cask.rb @@ -89,6 +89,30 @@ module Hbc metadata_master_container_path.join(*installed_version, "Casks", "#{token}.rb") end + def outdated?(greedy = false) + !outdated_versions(greedy).empty? + end + + def outdated_versions(greedy = false) + # special case: tap version is not available + return [] if version.nil? + + if greedy + return versions if version.latest? + elsif auto_updates + return [] + end + + installed = versions + current = installed.last + + # not outdated unless there is a different version on tap + return [] if current == version + + # collect all installed versions that are different than tap version and return them + installed.select { |v| v != version } + end + def to_s @token end diff --git a/Library/Homebrew/cask/lib/hbc/cli.rb b/Library/Homebrew/cask/lib/hbc/cli.rb index 27bea06ef1..aa795fc590 100644 --- a/Library/Homebrew/cask/lib/hbc/cli.rb +++ b/Library/Homebrew/cask/lib/hbc/cli.rb @@ -15,6 +15,7 @@ require "hbc/cli/home" require "hbc/cli/info" require "hbc/cli/install" require "hbc/cli/list" +require "hbc/cli/outdated" require "hbc/cli/reinstall" require "hbc/cli/search" require "hbc/cli/style" diff --git a/Library/Homebrew/cask/lib/hbc/cli/outdated.rb b/Library/Homebrew/cask/lib/hbc/cli/outdated.rb new file mode 100644 index 0000000000..d608beab50 --- /dev/null +++ b/Library/Homebrew/cask/lib/hbc/cli/outdated.rb @@ -0,0 +1,39 @@ +module Hbc + class CLI + class Outdated < Base + def self.run(*args) + greedy = args.include?("--greedy") + verbose = ($stdout.tty? || CLI.verbose?) && !args.include?("--quiet") + + cask_tokens = cask_tokens_from(args) + casks_to_check = if cask_tokens.empty? + Hbc.installed + else + cask_tokens.map { |token| Hbc.load(token) } + end + + casks_to_check.each do |cask| + odebug "Checking update info of Cask #{cask}" + list_if_outdated(cask, greedy, verbose) + end + end + + def self.list_if_outdated(cask, greedy, verbose) + return unless cask.outdated?(greedy) + + if verbose + outdated_versions = cask.outdated_versions(greedy) + outdated_info = "#{cask.token} (#{outdated_versions.join(", ")})" + current_version = cask.version.to_s + puts "#{outdated_info} != #{current_version}" + else + puts cask.token + end + end + + def self.help + "list the outdated installed Casks" + end + end + end +end diff --git a/Library/Homebrew/manpages/brew-cask.1.md b/Library/Homebrew/manpages/brew-cask.1.md index 56781bd769..41b04dbb70 100644 --- a/Library/Homebrew/manpages/brew-cask.1.md +++ b/Library/Homebrew/manpages/brew-cask.1.md @@ -85,6 +85,17 @@ names, and other aspects of this manual are still subject to change. If is given, summarize the staged files associated with the given Cask. + + * `outdated` [--greedy] [--verbose|--quiet] [ ...]: + Without token arguments, display all the installed Casks that have newer + versions available in the tap; otherwise check only the tokens given + in the command line. + If `--greedy` is given then also include in the output the Casks having + `auto_updates true` or `version :latest`. Otherwise they are skipped + because there is no reliable way to know when updates are available for + them.
+ `--verbose` forces the display of the outdated and latest version.
+ `--quiet` suppresses the display of versions. * `reinstall` [ ... ]: Reinstall the given Cask. diff --git a/Library/Homebrew/test/cask/cask_spec.rb b/Library/Homebrew/test/cask/cask_spec.rb index d76f2dce95..3971d3f9c0 100644 --- a/Library/Homebrew/test/cask/cask_spec.rb +++ b/Library/Homebrew/test/cask/cask_spec.rb @@ -89,4 +89,89 @@ describe Hbc::Cask, :cask do expect(c.metadata_versioned_container_path.to_s).to eq(metadata_path.to_s) end end + + describe "outdated" do + it "ignores the Casks that have auto_updates true (without --greedy)" do + c = Hbc.load("auto-updates") + expect(c).not_to be_outdated + expect(c.outdated_versions).to be_empty + end + + it "ignores the Casks that have version :latest (without --greedy)" do + c = Hbc.load("version-latest-string") + expect(c).not_to be_outdated + expect(c.outdated_versions).to be_empty + end + + describe "versioned casks" do + let(:cask) { described_class.new("basic-cask") } + subject { cask.outdated_versions } + + shared_examples "versioned casks" do |tap_version, expectations| + expectations.each do |installed_versions, expected_output| + context "when versions #{installed_versions.inspect} are installed and the tap version is #{tap_version}" do + it { + allow(cask).to receive(:versions).and_return(installed_versions) + allow(cask).to receive(:version).and_return(Hbc::DSL::Version.new(tap_version)) + expect(cask).to receive(:outdated_versions).and_call_original + is_expected.to eq expected_output + } + end + end + end + + describe "installed version is equal to tap version => not outdated" do + include_examples "versioned casks", "1.2.3", + ["1.2.3"] => [], + ["1.2.4", "1.2.3"] => [] + end + + describe "installed version is different than tap version => outdated" do + include_examples "versioned casks", "1.2.4", + ["1.2.3"] => ["1.2.3"], + ["1.2.4", "1.2.3"] => ["1.2.3"], + ["1.2.2", "1.2.3"] => ["1.2.2", "1.2.3"], + ["1.2.2", "1.2.4", "1.2.3"] => ["1.2.2", "1.2.3"] + end + end + + describe ":latest casks" do + let(:cask) { described_class.new("basic-cask") } + + shared_examples ":latest cask" do |greedy, tap_version, expectations| + expectations.each do |installed_version, expected_output| + context "when versions #{installed_version} are installed and the tap version is #{tap_version}, #{greedy ? "" : "not"} greedy" do + subject { cask.outdated_versions greedy } + it { + allow(cask).to receive(:versions).and_return(installed_version) + allow(cask).to receive(:version).and_return(Hbc::DSL::Version.new(tap_version)) + expect(cask).to receive(:outdated_versions).and_call_original + is_expected.to eq expected_output + } + end + end + end + + describe ":latest version installed, :latest version in tap" do + include_examples ":latest cask", false, "latest", + ["latest"] => [] + include_examples ":latest cask", true, "latest", + ["latest"] => ["latest"] + end + + describe "numbered version installed, :latest version in tap" do + include_examples ":latest cask", false, "latest", + ["1.2.3"] => ["1.2.3"] + include_examples ":latest cask", true, "latest", + ["1.2.3"] => ["1.2.3"] + end + + describe "latest version installed, numbered version in tap" do + include_examples ":latest cask", false, "1.2.3", + ["latest"] => ["latest"] + include_examples ":latest cask", true, "1.2.3", + ["latest"] => ["latest"] + end + end + end end diff --git a/Library/Homebrew/test/cask/cli/outdated_spec.rb b/Library/Homebrew/test/cask/cli/outdated_spec.rb new file mode 100644 index 0000000000..a0f13009de --- /dev/null +++ b/Library/Homebrew/test/cask/cli/outdated_spec.rb @@ -0,0 +1,80 @@ +describe Hbc::CLI::Outdated, :cask do + let(:installed) do + [ + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/basic-cask.rb"), + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/outdated/local-caffeine.rb"), + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/outdated/local-transmission.rb"), + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/version-latest-string.rb"), + Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/outdated/auto-updates.rb"), + ] + end + + before do + shutup do + installed.each { |cask| InstallHelper.install_with_caskfile(cask) } + end + allow(Hbc::CLI).to receive(:verbose?).and_return(true) + end + + describe 'without --greedy it ignores the Casks with "vesion latest" or "auto_updates true"' do + it "checks all the installed Casks when no token is provided" do + expect { + Hbc::CLI::Outdated.run + }.to output(<<-EOS.undent).to_stdout + local-caffeine (1.2.2) != 1.2.3 + local-transmission (2.60) != 2.61 + EOS + end + + it "checks only the tokens specified in the command line" do + expect { + Hbc::CLI::Outdated.run("local-caffeine") + }.to output(<<-EOS.undent).to_stdout + local-caffeine (1.2.2) != 1.2.3 + EOS + end + + it 'ignores "auto_updates" and "latest" Casks even when their tokens are provided in the command line' do + expect { + Hbc::CLI::Outdated.run("local-caffeine", "auto-updates", "version-latest-string") + }.to output(<<-EOS.undent).to_stdout + local-caffeine (1.2.2) != 1.2.3 + EOS + end + end + + it "lists only the names (no versions) of the outdated Casks with --quiet" do + expect { + Hbc::CLI::Outdated.run("--quiet") + }.to output(<<-EOS.undent).to_stdout + local-caffeine + local-transmission + EOS + end + + describe "with --greedy it checks additional Casks" do + it 'includes the Casks with "auto_updates true" or "version latest" with --greedy' do + expect { + Hbc::CLI::Outdated.run("--greedy") + }.to output(<<-EOS.undent).to_stdout + auto-updates (2.57) != 2.61 + local-caffeine (1.2.2) != 1.2.3 + local-transmission (2.60) != 2.61 + version-latest-string (latest) != latest + EOS + end + + it 'does not include the Casks with "auto_updates true" when the version did not change' do + cask = Hbc::CaskLoader.load_from_file(TEST_FIXTURE_DIR/"cask/Casks/auto-updates.rb") + InstallHelper.install_with_caskfile(cask) + + expect { + Hbc::CLI::Outdated.run("--greedy") + }.to output(<<-EOS.undent).to_stdout + local-caffeine (1.2.2) != 1.2.3 + local-transmission (2.60) != 2.61 + version-latest-string (latest) != latest + EOS + end + end +end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/auto-updates.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/auto-updates.rb new file mode 100644 index 0000000000..e202f5a163 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/auto-updates.rb @@ -0,0 +1,11 @@ +cask 'auto-updates' do + version '2.57' + sha256 'e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68' + + url "file://#{TEST_FIXTURE_DIR}/cask/transmission-2.61.dmg" + homepage 'http://example.com/auto-updates' + + auto_updates true + + app 'Transmission.app' +end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/local-caffeine.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/local-caffeine.rb new file mode 100644 index 0000000000..2c7f3e6ec3 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/local-caffeine.rb @@ -0,0 +1,9 @@ +cask 'local-caffeine' do + version '1.2.2' + sha256 '67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94' + + url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip" + homepage 'http://example.com/local-caffeine' + + app 'Caffeine.app' +end diff --git a/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/local-transmission.rb b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/local-transmission.rb new file mode 100644 index 0000000000..903f152d27 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/cask/Casks/outdated/local-transmission.rb @@ -0,0 +1,9 @@ +cask 'local-transmission' do + version '2.60' + sha256 'e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68' + + url "file://#{TEST_FIXTURE_DIR}/cask/transmission-2.61.dmg" + homepage 'http://example.com/local-transmission' + + app 'Transmission.app' +end diff --git a/manpages/brew-cask.1 b/manpages/brew-cask.1 index c5708f7cbd..8a7b557c08 100644 --- a/manpages/brew-cask.1 +++ b/manpages/brew-cask.1 @@ -88,6 +88,16 @@ Without any arguments, list all installed Casks\. With \fB\-1\fR, always format If \fItoken\fR is given, summarize the staged files associated with the given Cask\. . .TP +\fBoutdated\fR [\-\-greedy] [\-\-verbose|\-\-quiet] [ \fItoken\fR \.\.\.] +Without token arguments, display all the installed Casks that have newer versions available in the tap; otherwise check only the tokens given in the command line\. If \fB\-\-greedy\fR is given then also include in the output the Casks having \fBauto_updates true\fR or \fBversion :latest\fR\. Otherwise they are skipped because there is no reliable way to know when updates are available for them\. +. +.br +\fB\-\-verbose\fR forces the display of the outdated and latest version\. +. +.br +\fB\-\-quiet\fR suppresses the display of versions\. +. +.TP \fBreinstall\fR \fItoken\fR [ \fItoken\fR \.\.\. ] Reinstall the given Cask\. .