Merge pull request #4072 from reitermarkus/cask-refactoring

Cask Refactoring
This commit is contained in:
Markus Reiter 2018-04-17 06:17:04 +02:00 committed by GitHub
commit 6233b9d199
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 65 additions and 175 deletions

View File

@ -19,8 +19,6 @@ require "hbc/locations"
require "hbc/config" require "hbc/config"
require "hbc/macos" require "hbc/macos"
require "hbc/pkg" require "hbc/pkg"
require "hbc/qualified_token"
require "hbc/scopes"
require "hbc/staged" require "hbc/staged"
require "hbc/system_command" require "hbc/system_command"
require "hbc/topological_hash" require "hbc/topological_hash"
@ -31,7 +29,6 @@ require "hbc/version"
module Hbc module Hbc
include Locations include Locations
include Scopes
include Utils include Utils
def self.init def self.init

View File

@ -3,11 +3,20 @@ require "hbc/metadata"
module Hbc module Hbc
class Cask class Cask
extend Enumerable
extend Forwardable extend Forwardable
include Metadata include Metadata
attr_reader :token, :sourcefile_path, :config attr_reader :token, :sourcefile_path, :config
def self.each
return to_enum unless block_given?
Tap.flat_map(&:cask_files).each do |f|
yield CaskLoader::FromTapPathLoader.new(f).load
end
end
def tap def tap
return super if block_given? # Object#tap return super if block_given? # Object#tap
@tap @tap
@ -43,11 +52,13 @@ module Hbc
end end
def full_name def full_name
if @tap.nil? || @tap == Hbc.default_tap return token if tap == Hbc.default_tap
token qualified_token
else end
"#{@tap}/#{token}"
end def qualified_token
return token if tap.nil?
"#{tap.name}/#{token}"
end end
def installed? def installed?

View File

@ -15,5 +15,17 @@ module Hbc
SystemCommand.run("/usr/sbin/chown", args: [Utils.current_user, Hbc.caskroom], sudo: sudo) SystemCommand.run("/usr/sbin/chown", args: [Utils.current_user, Hbc.caskroom], sudo: sudo)
SystemCommand.run("/usr/bin/chgrp", args: ["admin", Hbc.caskroom], sudo: sudo) SystemCommand.run("/usr/bin/chgrp", args: ["admin", Hbc.caskroom], sudo: sudo)
end end
def casks
Pathname.glob(Hbc.caskroom.join("*")).sort.select(&:directory?).map do |path|
token = path.basename.to_s
if tap_path = CaskLoader.tap_paths(token).first
next CaskLoader::FromTapPathLoader.new(tap_path).load
end
CaskLoader::FromPathLoader.new(Pathname.glob(path.join(".metadata/*/*/*/*.rb")).first).load
end
end
end end
end end

View File

@ -9,7 +9,7 @@ module Hbc
end end
def run def run
failed_casks = casks(alternative: -> { Hbc.all }) failed_casks = casks(alternative: -> { Cask.to_a })
.reject { |cask| audit(cask) } .reject { |cask| audit(cask) }
return if failed_casks.empty? return if failed_casks.empty?

View File

@ -60,16 +60,12 @@ module Hbc
end end
def self.repo_info(cask) def self.repo_info(cask)
user, repo, token = QualifiedToken.parse(Hbc.all_tokens.detect { |t| t.split("/").last == cask.token }) return if cask.tap.nil?
return if user.nil? || repo.nil? url = if cask.tap.custom_remote? && !cask.tap.remote.nil?
cask.tap.remote
remote_tap = Tap.fetch(user, repo)
url = if remote_tap.custom_remote? && !remote_tap.remote.nil?
remote_tap.remote
else else
"#{remote_tap.default_remote}/blob/master/Casks/#{token}.rb" "#{cask.tap.default_remote}/blob/master/Casks/#{cask.token}.rb"
end end
puts "From: #{Formatter.url(url)}" puts "From: #{Formatter.url(url)}"

View File

@ -58,7 +58,7 @@ module Hbc
@stanza = :artifacts @stanza = :artifacts
end end
casks(alternative: -> { Hbc.all }).each do |cask| casks(alternative: -> { Cask.to_a }).each do |cask|
print "#{cask}\t" if table? print "#{cask}\t" if table?
begin begin

View File

@ -37,7 +37,7 @@ module Hbc
end end
def list_installed def list_installed
installed_casks = Hbc.installed installed_casks = Caskroom.casks
if one? if one?
puts installed_casks.map(&:to_s) puts installed_casks.map(&:to_s)

View File

@ -10,7 +10,7 @@ module Hbc
end end
def run def run
casks(alternative: -> { Hbc.installed }).each do |cask| casks(alternative: -> { Caskroom.casks }).each do |cask|
odebug "Checking update info of Cask #{cask}" odebug "Checking update info of Cask #{cask}"
self.class.list_if_outdated(cask, greedy?, verbose?) self.class.list_if_outdated(cask, greedy?, verbose?)
end end

View File

@ -3,7 +3,7 @@ module Hbc
class Search < AbstractCommand class Search < AbstractCommand
def run def run
if args.empty? if args.empty?
puts Formatter.columns(CLI.nice_listing(Hbc.all_tokens)) puts Formatter.columns(CLI.nice_listing(Cask.map(&:qualified_token)))
else else
results = self.class.search(*args) results = self.class.search(*args)
self.class.render_results(*results) self.class.render_results(*results)
@ -43,7 +43,7 @@ module Hbc
partial_matches = [] partial_matches = []
search_term = arguments.join(" ") search_term = arguments.join(" ")
search_regexp = extract_regexp arguments.first search_regexp = extract_regexp arguments.first
all_tokens = CLI.nice_listing(Hbc.all_tokens) all_tokens = CLI.nice_listing(Cask.map(&:qualified_token))
if search_regexp if search_regexp
search_term = arguments.first search_term = arguments.first
partial_matches = all_tokens.grep(/#{search_regexp}/i) partial_matches = all_tokens.grep(/#{search_regexp}/i)

View File

@ -27,7 +27,7 @@ module Hbc
def cask_paths def cask_paths
@cask_paths ||= if args.empty? @cask_paths ||= if args.empty?
Hbc.all_tapped_cask_dirs Tap.map(&:cask_dir).select(&:directory?)
elsif args.any? { |file| File.exist?(file) } elsif args.any? { |file| File.exist?(file) }
args args
else else

View File

@ -13,7 +13,7 @@ module Hbc
def run def run
outdated_casks = casks(alternative: lambda { outdated_casks = casks(alternative: lambda {
Hbc.installed.select do |cask| Caskroom.casks.select do |cask|
cask.outdated?(greedy?) cask.outdated?(greedy?)
end end
}).select { |cask| cask.outdated?(true) } }).select { |cask| cask.outdated?(true) }

View File

@ -238,26 +238,35 @@ module OS
# TODO: There should be a way to specify a containing # TODO: There should be a way to specify a containing
# directory under which nothing can be deleted. # directory under which nothing can be deleted.
UNDELETABLE_DIRS = [ UNDELETABLE_PATHS = [
"~/", "~/",
"~/Applications", "~/Applications",
"~/Applications/.localized",
"~/Desktop", "~/Desktop",
"~/Desktop/.localized",
"~/Documents", "~/Documents",
"~/Documents/.localized",
"~/Downloads", "~/Downloads",
"~/Downloads/.localized",
"~/Mail", "~/Mail",
"~/Movies", "~/Movies",
"~/Movies/.localized",
"~/Music", "~/Music",
"~/Music/.localized",
"~/Music/iTunes", "~/Music/iTunes",
"~/Music/iTunes/iTunes Music", "~/Music/iTunes/iTunes Music",
"~/Music/iTunes/Album Artwork", "~/Music/iTunes/Album Artwork",
"~/News", "~/News",
"~/Pictures", "~/Pictures",
"~/Pictures/.localized",
"~/Pictures/Desktops", "~/Pictures/Desktops",
"~/Pictures/Photo Booth", "~/Pictures/Photo Booth",
"~/Pictures/iChat Icons", "~/Pictures/iChat Icons",
"~/Pictures/iPhoto Library", "~/Pictures/iPhoto Library",
"~/Public", "~/Public",
"~/Public/.localized",
"~/Sites", "~/Sites",
"~/Sites/.localized",
"~/Library", "~/Library",
"~/Library/.localized", "~/Library/.localized",
"~/Library/Accessibility", "~/Library/Accessibility",
@ -364,17 +373,17 @@ module OS
"~/Library/Widgets", "~/Library/Widgets",
"~/Library/Workflows", "~/Library/Workflows",
] ]
.map { |x| Pathname(x.sub(%r{^~(?=(/|$))}, Dir.home)).expand_path } .map { |path| Pathname(path.sub(%r{^~(?=(/|$))}, Dir.home)).expand_path }
.to_set .to_set
.union(SYSTEM_DIRS) .union(SYSTEM_DIRS)
.freeze .freeze
def system_dir?(dir) def system_dir?(dir)
SYSTEM_DIRS.include?(Pathname.new(dir).expand_path) SYSTEM_DIRS.include?(Pathname.new(dir).expand_path)
end end
def undeletable?(dir) def undeletable?(path)
UNDELETABLE_DIRS.include?(Pathname.new(dir).expand_path) UNDELETABLE_PATHS.include?(Pathname.new(path).expand_path)
end end
end end
end end

View File

@ -1,11 +0,0 @@
module Hbc
module QualifiedToken
def self.parse(arg)
return nil unless arg.is_a?(String)
return nil unless match = arg.downcase.match(HOMEBREW_TAP_CASK_REGEX)
user, repo, token = match.captures
odebug "[user, repo, token] might be [#{user}, #{repo}, #{token}]"
[user, repo, token]
end
end
end

View File

@ -1,47 +0,0 @@
module Hbc
module Scopes
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def all
all_tokens.map(&CaskLoader.public_method(:load))
end
def all_tapped_cask_dirs
Tap.map(&:cask_dir).select(&:directory?)
end
def all_tokens
Tap.flat_map do |t|
t.cask_files.map do |p|
"#{t.name}/#{File.basename(p, ".rb")}"
end
end
end
def installed
# CaskLoader.load has some DWIM which is slow. Optimize here
# by spoon-feeding CaskLoader.load fully-qualified paths.
# TODO: speed up Hbc::Source::Tapped (main perf drag is calling Hbc.all_tokens repeatedly)
# TODO: ability to specify expected source when calling CaskLoader.load (minor perf benefit)
Pathname.glob(caskroom.join("*"))
.sort
.map do |caskroom_path|
token = caskroom_path.basename.to_s
path_to_cask = all_tapped_cask_dirs.find do |tap_dir|
tap_dir.join("#{token}.rb").exist?
end
if path_to_cask
CaskLoader.load(path_to_cask.join("#{token}.rb"))
else
CaskLoader.load(token)
end
end
end
end
end
end

View File

@ -4,17 +4,6 @@ require "stringio"
BUG_REPORTS_URL = "https://github.com/caskroom/homebrew-cask#reporting-bugs".freeze BUG_REPORTS_URL = "https://github.com/caskroom/homebrew-cask#reporting-bugs".freeze
class Buffer < StringIO
extend Predicable
attr_predicate :tty?
def initialize(tty = false)
super()
@tty = tty
end
end
# global methods # global methods
def odebug(title, *sput) def odebug(title, *sput)

View File

@ -19,7 +19,7 @@ describe Hbc::Artifact::Artifact, :cask do
context "without target" do context "without target" do
it "fails to load" do it "fails to load" do
expect { expect {
Hbc::CaskLoader.load(cask_path("with-generic-artifact-no-target")) Hbc::CaskLoader.load(cask_path("invalid/invalid-generic-artifact-no-target"))
}.to raise_error(Hbc::CaskInvalidError, /target required for Generic Artifact/) }.to raise_error(Hbc::CaskInvalidError, /target required for Generic Artifact/)
end end
end end

View File

@ -1,9 +0,0 @@
describe Hbc::Artifact::App, :cask do
# FIXME: Doesn't actually raise because the `app` stanza is not evaluated on load.
# it "must raise" do
# lambda {
# Hbc::CaskLoader.load(cask_path("with-two-apps-incorrect"))
# }.must_raise
# # TODO: later give the user a nice exception for this case and check for it here
# end
end

View File

@ -70,14 +70,6 @@ describe Hbc::Cask, :cask do
end end
end end
describe "all_tokens" do
it "returns a token for every Cask" do
all_cask_tokens = Hbc.all_tokens
expect(all_cask_tokens.count).to be > 20
all_cask_tokens.each { |token| expect(token).to be_kind_of(String) }
end
end
describe "metadata" do describe "metadata" do
it "proposes a versioned metadata directory name for each instance" do it "proposes a versioned metadata directory name for each instance" do
cask_token = "local-caffeine" cask_token = "local-caffeine"

View File

@ -7,9 +7,7 @@ describe Hbc::CLI::Audit, :cask do
describe "selection of Casks to audit" do describe "selection of Casks to audit" do
it "audits all Casks if no tokens are given" do it "audits all Casks if no tokens are given" do
expect(cask).to be_a Hbc::Cask allow(Hbc::Cask).to receive(:to_a).and_return([cask, cask])
allow(Hbc).to receive(:all).and_return([cask, cask])
expect(Hbc::Auditor).to receive(:audit).twice.and_return(true) expect(Hbc::Auditor).to receive(:audit).twice.and_return(true)

View File

@ -83,11 +83,13 @@ describe Hbc::CLI::Style, :cask do
context "when no cask tokens are given" do context "when no cask tokens are given" do
let(:tokens) { [] } let(:tokens) { [] }
before do matcher :a_path_ending_with do |end_string|
allow(Hbc).to receive(:all_tapped_cask_dirs).and_return(%w[Casks MoreCasks]) match do |actual|
expect(actual.to_s).to end_with(end_string)
end
end end
it { is_expected.to eq(%w[Casks MoreCasks]) } it { is_expected.to contain_exactly(a_path_ending_with("/caskroom/homebrew-spec/Casks"), a_path_ending_with("/third-party/homebrew-tap/Casks")) }
end end
context "when at least one cask token is a path that exists" do context "when at least one cask token is a path that exists" do

View File

@ -72,7 +72,7 @@ describe Hbc::DSL, :cask do
end end
context "when it contains a deprecated DSL version", :needs_compat do context "when it contains a deprecated DSL version", :needs_compat do
let(:token) { "with-dsl-version" } let(:token) { "compat/with-dsl-version" }
it "may use deprecated DSL version hash syntax" do it "may use deprecated DSL version hash syntax" do
allow(ENV).to receive(:[]).with("HOMEBREW_DEVELOPER").and_return(nil) allow(ENV).to receive(:[]).with("HOMEBREW_DEVELOPER").and_return(nil)

View File

@ -1,37 +0,0 @@
describe Hbc::Scopes, :cask do
describe "installed" do
it "returns a list installed Casks by loading Casks for all the dirs that exist in the caskroom" do
allow(Hbc::CaskLoader).to receive(:load) { |token| "loaded-#{token}" }
Hbc.caskroom.join("cask-bar").mkpath
Hbc.caskroom.join("cask-foo").mkpath
installed_casks = Hbc.installed
expect(Hbc::CaskLoader).to have_received(:load).with("cask-bar")
expect(Hbc::CaskLoader).to have_received(:load).with("cask-foo")
expect(installed_casks).to eq(
%w[
loaded-cask-bar
loaded-cask-foo
],
)
end
it "optimizes performance by resolving to a fully qualified path before calling Hbc::CaskLoader.load" do
fake_tapped_cask_dir = Pathname.new(Dir.mktmpdir).join("Casks")
absolute_path_to_cask = fake_tapped_cask_dir.join("some-cask.rb")
allow(Hbc::CaskLoader).to receive(:load)
allow(Hbc).to receive(:all_tapped_cask_dirs) { [fake_tapped_cask_dir] }
Hbc.caskroom.join("some-cask").mkdir
fake_tapped_cask_dir.mkdir
FileUtils.touch(absolute_path_to_cask)
Hbc.installed
expect(Hbc::CaskLoader).to have_received(:load).with(absolute_path_to_cask)
end
end
end

View File

@ -1,3 +0,0 @@
cask 'generic-artifact-no-target' do
artifact 'Caffeine.app'
end

View File

@ -1,4 +1,4 @@
cask 'with-generic-artifact-no-target' do cask 'invalid-generic-artifact-no-target' do
version '1.2.3' version '1.2.3'
sha256 '67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94' sha256 '67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94'

View File

@ -1,9 +0,0 @@
cask 'with-two-apps-incorrect' do
version '1.2.3'
sha256 '67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94'
url "file://#{TEST_FIXTURE_DIR}/cask/caffeine.zip"
homepage 'http://example.com/local-caffeine'
app 'Caffeine.app', 'Caffeine.app/Contents/MacOS/Caffeine'
end