2016-01-04 16:22:26 -04:00
|
|
|
require "extend/string"
|
2016-07-09 13:51:19 +01:00
|
|
|
require "readall"
|
2016-01-04 16:22:26 -04:00
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# a {Tap} is used to extend the formulae provided by Homebrew core.
|
|
|
|
# Usually, it's synced with a remote git repository. And it's likely
|
|
|
|
# a Github repository with the name of `user/homebrew-repo`. In such
|
|
|
|
# case, `user/repo` will be used as the {#name} of this {Tap}, where
|
|
|
|
# {#user} represents Github username and {#repo} represents repository
|
|
|
|
# name without leading `homebrew-`.
|
2015-06-10 15:39:47 +08:00
|
|
|
class Tap
|
|
|
|
TAP_DIRECTORY = HOMEBREW_LIBRARY/"Taps"
|
|
|
|
|
2015-09-27 16:52:14 +08:00
|
|
|
CACHE = {}
|
|
|
|
|
|
|
|
def self.clear_cache
|
|
|
|
CACHE.clear
|
|
|
|
end
|
|
|
|
|
2015-12-06 21:36:26 +08:00
|
|
|
def self.fetch(*args)
|
|
|
|
case args.length
|
|
|
|
when 1
|
|
|
|
user, repo = args.first.split("/", 2)
|
|
|
|
when 2
|
|
|
|
user = args[0]
|
|
|
|
repo = args[1]
|
|
|
|
end
|
|
|
|
|
2016-07-27 02:53:41 +02:00
|
|
|
if [user, repo].any? { |part| part.nil? || part.include?("/") }
|
|
|
|
raise "Invalid tap name '#{args.join("/")}'"
|
|
|
|
end
|
2015-12-06 21:36:26 +08:00
|
|
|
|
2015-12-02 14:35:42 +08:00
|
|
|
# we special case homebrew so users don't have to shift in a terminal
|
|
|
|
user = "Homebrew" if user == "homebrew"
|
|
|
|
repo = repo.strip_prefix "homebrew-"
|
2015-12-06 21:36:26 +08:00
|
|
|
|
2016-03-06 14:50:33 +08:00
|
|
|
if user == "Homebrew" && (repo == "homebrew" || repo == "core")
|
2016-03-07 18:04:25 +08:00
|
|
|
return CoreTap.instance
|
2015-12-06 21:36:26 +08:00
|
|
|
end
|
|
|
|
|
2015-09-27 16:52:14 +08:00
|
|
|
cache_key = "#{user}/#{repo}".downcase
|
|
|
|
CACHE.fetch(cache_key) { |key| CACHE[key] = Tap.new(user, repo) }
|
|
|
|
end
|
|
|
|
|
2015-06-10 15:39:47 +08:00
|
|
|
extend Enumerable
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# The user name of this {Tap}. Usually, it's the Github username of
|
|
|
|
# this #{Tap}'s remote repository.
|
2015-06-10 15:39:47 +08:00
|
|
|
attr_reader :user
|
2015-06-13 01:53:55 +08:00
|
|
|
|
|
|
|
# The repository name of this {Tap} without leading `homebrew-`.
|
2015-06-10 15:39:47 +08:00
|
|
|
attr_reader :repo
|
2015-06-13 01:53:55 +08:00
|
|
|
|
|
|
|
# The name of this {Tap}. It combines {#user} and {#repo} with a slash.
|
|
|
|
# {#name} is always in lowercase.
|
|
|
|
# e.g. `user/repo`
|
2015-06-10 15:39:47 +08:00
|
|
|
attr_reader :name
|
2015-06-13 01:53:55 +08:00
|
|
|
|
|
|
|
# The local path to this {Tap}.
|
|
|
|
# e.g. `/usr/local/Library/Taps/user/homebrew-repo`
|
2015-06-10 15:39:47 +08:00
|
|
|
attr_reader :path
|
|
|
|
|
2015-12-02 14:35:42 +08:00
|
|
|
# @private
|
2015-06-13 01:57:00 +08:00
|
|
|
def initialize(user, repo)
|
2015-12-02 14:35:42 +08:00
|
|
|
@user = user
|
2015-06-10 15:39:47 +08:00
|
|
|
@repo = repo
|
|
|
|
@name = "#{@user}/#{@repo}".downcase
|
|
|
|
@path = TAP_DIRECTORY/"#{@user}/homebrew-#{@repo}".downcase
|
2016-07-02 09:43:12 +02:00
|
|
|
@path.extend(GitRepositoryExtension)
|
2015-06-13 01:57:00 +08:00
|
|
|
end
|
|
|
|
|
2016-02-06 03:50:06 +08:00
|
|
|
# clear internal cache
|
|
|
|
def clear_cache
|
|
|
|
@remote = nil
|
|
|
|
@formula_dir = nil
|
2016-08-04 14:37:37 +04:00
|
|
|
@cask_dir = nil
|
2016-02-06 03:50:06 +08:00
|
|
|
@formula_files = nil
|
2016-03-23 14:55:42 +08:00
|
|
|
@alias_dir = nil
|
2016-02-06 03:50:06 +08:00
|
|
|
@alias_files = nil
|
|
|
|
@aliases = nil
|
|
|
|
@alias_table = nil
|
|
|
|
@alias_reverse_table = nil
|
|
|
|
@command_files = nil
|
|
|
|
@formula_renames = nil
|
2016-02-25 14:14:33 +08:00
|
|
|
@tap_migrations = nil
|
2016-04-12 11:00:23 +01:00
|
|
|
@config = nil
|
|
|
|
remove_instance_variable(:@private) if instance_variable_defined?(:@private)
|
2016-02-06 03:50:06 +08:00
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# The remote path to this {Tap}.
|
|
|
|
# e.g. `https://github.com/user/homebrew-repo`
|
2015-06-13 01:57:00 +08:00
|
|
|
def remote
|
2016-07-02 09:43:12 +02:00
|
|
|
raise TapUnavailableError, name unless installed?
|
|
|
|
@remote ||= path.git_origin
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2016-04-19 15:25:29 +08:00
|
|
|
# The default remote path to this {Tap}.
|
|
|
|
def default_remote
|
|
|
|
"https://github.com/#{user}/homebrew-#{repo}"
|
|
|
|
end
|
|
|
|
|
2015-07-30 16:51:00 +08:00
|
|
|
# True if this {Tap} is a git repository.
|
|
|
|
def git?
|
2016-07-02 09:43:12 +02:00
|
|
|
path.git?
|
2015-07-30 16:51:00 +08:00
|
|
|
end
|
|
|
|
|
2016-03-05 20:03:43 +08:00
|
|
|
# git HEAD for this {Tap}.
|
|
|
|
def git_head
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2016-07-02 09:43:12 +02:00
|
|
|
path.git_head
|
2016-03-05 20:03:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# git HEAD in short format for this {Tap}.
|
|
|
|
def git_short_head
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2016-07-02 09:43:12 +02:00
|
|
|
path.git_short_head
|
2016-03-05 20:03:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# time since git last commit for this {Tap}.
|
|
|
|
def git_last_commit
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2016-07-02 09:43:12 +02:00
|
|
|
path.git_last_commit
|
2016-03-05 20:03:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# git last commit date for this {Tap}.
|
|
|
|
def git_last_commit_date
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2016-07-02 09:43:12 +02:00
|
|
|
path.git_last_commit_date
|
2016-03-05 20:03:43 +08:00
|
|
|
end
|
|
|
|
|
2015-12-28 13:23:22 +01:00
|
|
|
# The issues URL of this {Tap}.
|
|
|
|
# e.g. `https://github.com/user/homebrew-repo/issues`
|
|
|
|
def issues_url
|
2016-09-23 22:02:23 +02:00
|
|
|
return unless official? || !custom_remote?
|
|
|
|
"https://github.com/#{user}/homebrew-#{repo}/issues"
|
2015-12-28 13:23:22 +01:00
|
|
|
end
|
|
|
|
|
2015-06-10 15:39:47 +08:00
|
|
|
def to_s
|
|
|
|
name
|
|
|
|
end
|
|
|
|
|
2016-10-03 16:12:19 +02:00
|
|
|
def version_string
|
|
|
|
return "N/A" unless installed?
|
|
|
|
pretty_revision = git_short_head
|
|
|
|
return "(no git repository)" unless pretty_revision
|
|
|
|
"(git revision #{pretty_revision}; last commit #{git_last_commit_date})"
|
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# True if this {Tap} is an official Homebrew tap.
|
2015-06-10 15:39:47 +08:00
|
|
|
def official?
|
2015-12-06 20:57:28 +08:00
|
|
|
user == "Homebrew"
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# True if the remote of this {Tap} is a private repository.
|
2015-06-10 15:39:47 +08:00
|
|
|
def private?
|
2016-04-12 11:00:23 +01:00
|
|
|
return @private if instance_variable_defined?(:@private)
|
2016-04-12 18:51:43 +08:00
|
|
|
@private = read_or_set_private_config
|
2016-04-12 11:00:23 +01:00
|
|
|
end
|
|
|
|
|
2016-04-12 18:51:43 +08:00
|
|
|
# {TapConfig} of this {Tap}
|
2016-04-12 11:00:23 +01:00
|
|
|
def config
|
|
|
|
@config ||= begin
|
|
|
|
raise TapUnavailableError, name unless installed?
|
|
|
|
TapConfig.new(self)
|
|
|
|
end
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# True if this {Tap} has been installed.
|
2015-06-10 15:39:47 +08:00
|
|
|
def installed?
|
2015-12-06 20:57:28 +08:00
|
|
|
path.directory?
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2016-04-04 03:18:55 -07:00
|
|
|
# True if this {Tap} is not a full clone.
|
|
|
|
def shallow?
|
|
|
|
(path/".git/shallow").exist?
|
|
|
|
end
|
|
|
|
|
2015-12-07 14:12:57 +08:00
|
|
|
# @private
|
2016-03-07 18:04:25 +08:00
|
|
|
def core_tap?
|
2015-12-07 14:12:57 +08:00
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2015-11-07 16:25:34 +08:00
|
|
|
# install this {Tap}.
|
|
|
|
#
|
|
|
|
# @param [Hash] options
|
|
|
|
# @option options [String] :clone_targe If passed, it will be used as the clone remote.
|
|
|
|
# @option options [Boolean] :full_clone If set as true, full clone will be used.
|
2016-02-25 21:09:50 +08:00
|
|
|
# @option options [Boolean] :quiet If set, suppress all output.
|
2015-11-07 16:25:34 +08:00
|
|
|
def install(options = {})
|
2015-12-19 19:10:22 +08:00
|
|
|
require "descriptions"
|
2015-11-07 16:25:34 +08:00
|
|
|
|
2016-04-04 03:18:55 -07:00
|
|
|
full_clone = options.fetch(:full_clone, false)
|
2016-02-25 21:09:50 +08:00
|
|
|
quiet = options.fetch(:quiet, false)
|
2016-04-19 15:25:29 +08:00
|
|
|
requested_remote = options[:clone_target] || default_remote
|
2016-04-04 03:18:55 -07:00
|
|
|
|
|
|
|
if installed?
|
|
|
|
raise TapAlreadyTappedError, name unless full_clone
|
|
|
|
raise TapAlreadyUnshallowError, name unless shallow?
|
|
|
|
end
|
2016-02-25 21:09:50 +08:00
|
|
|
|
2015-11-07 16:25:34 +08:00
|
|
|
# ensure git is installed
|
|
|
|
Utils.ensure_git_installed!
|
2016-04-04 03:18:55 -07:00
|
|
|
|
|
|
|
if installed?
|
2016-04-19 15:18:54 +08:00
|
|
|
if options[:clone_target] && requested_remote != remote
|
|
|
|
raise TapRemoteMismatchError.new(name, @remote, requested_remote)
|
|
|
|
end
|
|
|
|
|
2016-04-04 03:18:55 -07:00
|
|
|
ohai "Unshallowing #{name}" unless quiet
|
2016-10-22 13:32:46 +01:00
|
|
|
args = %w[fetch --unshallow]
|
2016-04-04 03:18:55 -07:00
|
|
|
args << "-q" if quiet
|
|
|
|
path.cd { safe_system "git", *args }
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
clear_cache
|
|
|
|
|
2016-02-25 21:09:50 +08:00
|
|
|
ohai "Tapping #{name}" unless quiet
|
2016-06-20 13:03:27 +01:00
|
|
|
args = %W[clone #{requested_remote} #{path}]
|
2016-04-04 03:18:55 -07:00
|
|
|
args << "--depth=1" unless full_clone
|
2016-02-25 21:09:50 +08:00
|
|
|
args << "-q" if quiet
|
2015-11-07 16:25:34 +08:00
|
|
|
|
|
|
|
begin
|
|
|
|
safe_system "git", *args
|
2016-09-17 15:32:44 +01:00
|
|
|
unless Readall.valid_tap?(self, aliases: true)
|
2016-08-08 09:31:01 +01:00
|
|
|
unless ARGV.homebrew_developer?
|
|
|
|
raise "Cannot tap #{name}: invalid syntax in tap!"
|
|
|
|
end
|
2016-07-09 13:51:19 +01:00
|
|
|
end
|
|
|
|
rescue Interrupt, ErrorDuringExecution, RuntimeError
|
2015-11-07 16:25:34 +08:00
|
|
|
ignore_interrupts do
|
2016-07-09 13:51:19 +01:00
|
|
|
# wait for git to possibly cleanup the top directory when interrupt happens.
|
|
|
|
sleep 0.1
|
|
|
|
FileUtils.rm_rf path
|
2015-12-06 20:57:28 +08:00
|
|
|
path.parent.rmdir_if_possible
|
2015-11-07 16:25:34 +08:00
|
|
|
end
|
|
|
|
raise
|
|
|
|
end
|
|
|
|
|
2016-09-30 18:22:53 -05:00
|
|
|
link_completions_and_manpages
|
2015-12-08 17:26:53 +00:00
|
|
|
|
2015-11-07 16:25:34 +08:00
|
|
|
formula_count = formula_files.size
|
2017-03-11 11:33:12 +01:00
|
|
|
puts "Tapped #{Formatter.pluralize(formula_count, "formula")} (#{path.abv})" unless quiet
|
2015-11-07 16:25:34 +08:00
|
|
|
Descriptions.cache_formulae(formula_names)
|
|
|
|
|
2016-09-23 22:02:23 +02:00
|
|
|
return if options[:clone_target]
|
|
|
|
return unless private?
|
|
|
|
return if quiet
|
|
|
|
puts <<-EOS.undent
|
|
|
|
It looks like you tapped a private repository. To avoid entering your
|
|
|
|
credentials each time you update, you can use git HTTP credential
|
|
|
|
caching or issue the following command:
|
|
|
|
cd #{path}
|
|
|
|
git remote set-url origin git@github.com:#{user}/homebrew-#{repo}.git
|
|
|
|
EOS
|
2015-11-07 16:25:34 +08:00
|
|
|
end
|
|
|
|
|
2016-09-30 18:22:53 -05:00
|
|
|
def link_completions_and_manpages
|
|
|
|
command = "brew tap --repair"
|
|
|
|
Utils::Link.link_manpages(path, command)
|
|
|
|
Utils::Link.link_completions(path, command)
|
2015-12-08 17:26:53 +00:00
|
|
|
end
|
|
|
|
|
2015-11-07 16:25:34 +08:00
|
|
|
# uninstall this {Tap}.
|
|
|
|
def uninstall
|
2015-12-19 19:10:22 +08:00
|
|
|
require "descriptions"
|
2015-11-07 16:25:34 +08:00
|
|
|
raise TapUnavailableError, name unless installed?
|
|
|
|
|
2015-12-06 20:57:28 +08:00
|
|
|
puts "Untapping #{name}... (#{path.abv})"
|
2015-11-07 16:25:34 +08:00
|
|
|
unpin if pinned?
|
|
|
|
formula_count = formula_files.size
|
|
|
|
Descriptions.uncache_formulae(formula_names)
|
2016-09-30 18:22:53 -05:00
|
|
|
Utils::Link.unlink_manpages(path)
|
|
|
|
Utils::Link.unlink_completions(path)
|
2015-12-06 20:57:28 +08:00
|
|
|
path.rmtree
|
2015-12-08 17:26:53 +00:00
|
|
|
path.parent.rmdir_if_possible
|
2017-03-11 11:33:12 +01:00
|
|
|
puts "Untapped #{Formatter.pluralize(formula_count, "formula")}"
|
2016-02-06 03:50:06 +08:00
|
|
|
clear_cache
|
2015-11-07 16:25:34 +08:00
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# True if the {#remote} of {Tap} is customized.
|
2015-06-10 15:39:47 +08:00
|
|
|
def custom_remote?
|
2015-06-13 01:57:00 +08:00
|
|
|
return true unless remote
|
2016-09-17 15:17:27 +01:00
|
|
|
remote.casecmp(default_remote).nonzero?
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2015-12-06 20:59:31 +08:00
|
|
|
# path to the directory of all {Formula} files for this {Tap}.
|
|
|
|
def formula_dir
|
2016-12-13 01:53:05 +00:00
|
|
|
@formula_dir ||= potential_formula_dirs.detect(&:directory?)
|
|
|
|
end
|
|
|
|
|
|
|
|
def potential_formula_dirs
|
|
|
|
@potential_formula_dirs ||= [path/"Formula", path/"HomebrewFormula", path].freeze
|
2015-12-06 20:59:31 +08:00
|
|
|
end
|
|
|
|
|
2016-08-20 07:02:23 +02:00
|
|
|
# path to the directory of all {Cask} files for this {Tap}.
|
2016-08-04 14:37:37 +04:00
|
|
|
def cask_dir
|
2016-08-25 05:19:14 +02:00
|
|
|
@cask_dir ||= [path/"Casks"].detect(&:directory?)
|
2016-08-04 14:37:37 +04:00
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# an array of all {Formula} files of this {Tap}.
|
2015-06-10 15:39:47 +08:00
|
|
|
def formula_files
|
2015-12-06 20:59:31 +08:00
|
|
|
@formula_files ||= if formula_dir
|
2016-08-25 05:19:14 +02:00
|
|
|
formula_dir.children.select(&method(:formula_file?))
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# an array of all {Cask} files of this {Tap}.
|
|
|
|
def cask_files
|
|
|
|
@cask_files ||= if cask_dir
|
|
|
|
cask_dir.children.select(&method(:cask_file?))
|
2015-09-27 16:52:14 +08:00
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2016-02-15 22:31:47 +08:00
|
|
|
# return true if given path would present a {Formula} file in this {Tap}.
|
|
|
|
# accepts both absolute path and relative path (relative to this {Tap}'s path)
|
|
|
|
# @private
|
|
|
|
def formula_file?(file)
|
|
|
|
file = Pathname.new(file) unless file.is_a? Pathname
|
|
|
|
file = file.expand_path(path)
|
2016-08-02 14:11:40 +01:00
|
|
|
file.extname == ".rb" && file.parent == formula_dir
|
2016-02-15 22:31:47 +08:00
|
|
|
end
|
|
|
|
|
2016-08-25 05:19:14 +02:00
|
|
|
# return true if given path would present a {Cask} file in this {Tap}.
|
2016-08-04 14:37:37 +04:00
|
|
|
# accepts both absolute path and relative path (relative to this {Tap}'s path)
|
|
|
|
# @private
|
|
|
|
def cask_file?(file)
|
|
|
|
file = Pathname.new(file) unless file.is_a? Pathname
|
|
|
|
file = file.expand_path(path)
|
|
|
|
file.extname == ".rb" && file.parent == cask_dir
|
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# an array of all {Formula} names of this {Tap}.
|
2015-06-10 15:39:47 +08:00
|
|
|
def formula_names
|
2015-12-06 21:30:23 +08:00
|
|
|
@formula_names ||= formula_files.map { |f| formula_file_to_name(f) }
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2015-12-06 21:15:43 +08:00
|
|
|
# path to the directory of all alias files for this {Tap}.
|
|
|
|
# @private
|
|
|
|
def alias_dir
|
2016-03-23 14:55:42 +08:00
|
|
|
@alias_dir ||= path/"Aliases"
|
2015-12-06 21:15:43 +08:00
|
|
|
end
|
|
|
|
|
2015-09-12 18:21:22 +08:00
|
|
|
# an array of all alias files of this {Tap}.
|
|
|
|
# @private
|
|
|
|
def alias_files
|
2015-12-06 21:15:43 +08:00
|
|
|
@alias_files ||= Pathname.glob("#{alias_dir}/*").select(&:file?)
|
2015-09-12 18:21:22 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# an array of all aliases of this {Tap}.
|
|
|
|
# @private
|
|
|
|
def aliases
|
2015-12-06 21:30:23 +08:00
|
|
|
@aliases ||= alias_files.map { |f| alias_file_to_name(f) }
|
2015-09-12 18:21:22 +08:00
|
|
|
end
|
|
|
|
|
2015-10-07 17:54:20 +08:00
|
|
|
# a table mapping alias to formula name
|
|
|
|
# @private
|
|
|
|
def alias_table
|
|
|
|
return @alias_table if @alias_table
|
2016-09-17 15:17:27 +01:00
|
|
|
@alias_table = {}
|
2015-10-07 17:54:20 +08:00
|
|
|
alias_files.each do |alias_file|
|
2015-12-06 21:30:23 +08:00
|
|
|
@alias_table[alias_file_to_name(alias_file)] = formula_file_to_name(alias_file.resolved_path)
|
2015-10-07 17:54:20 +08:00
|
|
|
end
|
|
|
|
@alias_table
|
|
|
|
end
|
|
|
|
|
|
|
|
# a table mapping formula name to aliases
|
|
|
|
# @private
|
|
|
|
def alias_reverse_table
|
|
|
|
return @alias_reverse_table if @alias_reverse_table
|
2016-09-17 15:17:27 +01:00
|
|
|
@alias_reverse_table = {}
|
2015-10-07 17:54:20 +08:00
|
|
|
alias_table.each do |alias_name, formula_name|
|
|
|
|
@alias_reverse_table[formula_name] ||= []
|
|
|
|
@alias_reverse_table[formula_name] << alias_name
|
|
|
|
end
|
|
|
|
@alias_reverse_table
|
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# an array of all commands files of this {Tap}.
|
2015-06-10 15:39:47 +08:00
|
|
|
def command_files
|
2015-09-27 16:52:14 +08:00
|
|
|
@command_files ||= Pathname.glob("#{path}/cmd/brew-*").select(&:executable?)
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2015-11-07 16:00:45 +08:00
|
|
|
# path to the pin record for this {Tap}.
|
|
|
|
# @private
|
2015-08-09 22:42:46 +08:00
|
|
|
def pinned_symlink_path
|
2015-12-06 20:57:28 +08:00
|
|
|
HOMEBREW_LIBRARY/"PinnedTaps/#{name}"
|
2015-08-09 22:42:46 +08:00
|
|
|
end
|
|
|
|
|
2015-11-07 16:00:45 +08:00
|
|
|
# True if this {Tap} has been pinned.
|
2015-08-09 22:42:46 +08:00
|
|
|
def pinned?
|
2015-09-27 16:52:14 +08:00
|
|
|
return @pinned if instance_variable_defined?(:@pinned)
|
|
|
|
@pinned = pinned_symlink_path.directory?
|
2015-08-09 22:42:46 +08:00
|
|
|
end
|
|
|
|
|
2015-11-07 16:00:45 +08:00
|
|
|
# pin this {Tap}.
|
2015-08-09 22:42:46 +08:00
|
|
|
def pin
|
|
|
|
raise TapUnavailableError, name unless installed?
|
|
|
|
raise TapPinStatusError.new(name, true) if pinned?
|
2015-12-06 20:57:28 +08:00
|
|
|
pinned_symlink_path.make_relative_symlink(path)
|
2015-09-27 16:52:14 +08:00
|
|
|
@pinned = true
|
2015-08-09 22:42:46 +08:00
|
|
|
end
|
|
|
|
|
2015-11-07 16:00:45 +08:00
|
|
|
# unpin this {Tap}.
|
2015-08-09 22:42:46 +08:00
|
|
|
def unpin
|
|
|
|
raise TapUnavailableError, name unless installed?
|
|
|
|
raise TapPinStatusError.new(name, false) unless pinned?
|
|
|
|
pinned_symlink_path.delete
|
2015-12-08 17:26:53 +00:00
|
|
|
pinned_symlink_path.parent.rmdir_if_possible
|
2015-12-19 20:32:16 +08:00
|
|
|
pinned_symlink_path.parent.parent.rmdir_if_possible
|
2015-09-27 16:52:14 +08:00
|
|
|
@pinned = false
|
2015-08-09 22:42:46 +08:00
|
|
|
end
|
|
|
|
|
2015-06-10 15:39:47 +08:00
|
|
|
def to_hash
|
2015-08-13 14:56:14 +08:00
|
|
|
hash = {
|
2015-12-06 20:57:28 +08:00
|
|
|
"name" => name,
|
|
|
|
"user" => user,
|
|
|
|
"repo" => repo,
|
|
|
|
"path" => path.to_s,
|
2015-06-10 15:39:47 +08:00
|
|
|
"installed" => installed?,
|
|
|
|
"official" => official?,
|
|
|
|
"formula_names" => formula_names,
|
|
|
|
"formula_files" => formula_files.map(&:to_s),
|
2015-08-09 22:42:46 +08:00
|
|
|
"command_files" => command_files.map(&:to_s),
|
2016-09-17 15:17:27 +01:00
|
|
|
"pinned" => pinned?,
|
2015-06-10 15:39:47 +08:00
|
|
|
}
|
2015-08-13 14:56:14 +08:00
|
|
|
|
|
|
|
if installed?
|
|
|
|
hash["remote"] = remote
|
|
|
|
hash["custom_remote"] = custom_remote?
|
2016-07-10 02:27:59 +02:00
|
|
|
hash["private"] = private?
|
2015-08-13 14:56:14 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
hash
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2015-08-09 14:39:46 +03:00
|
|
|
# Hash with tap formula renames
|
|
|
|
def formula_renames
|
2016-11-20 13:00:01 -05:00
|
|
|
require "json"
|
2015-12-19 19:10:22 +08:00
|
|
|
|
2015-08-09 14:39:46 +03:00
|
|
|
@formula_renames ||= if (rename_file = path/"formula_renames.json").file?
|
2016-11-20 13:00:01 -05:00
|
|
|
JSON.parse(rename_file.read)
|
2015-08-09 14:39:46 +03:00
|
|
|
else
|
|
|
|
{}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-02-25 14:14:33 +08:00
|
|
|
# Hash with tap migrations
|
|
|
|
def tap_migrations
|
2016-11-20 13:00:01 -05:00
|
|
|
require "json"
|
2016-02-25 14:14:33 +08:00
|
|
|
|
|
|
|
@tap_migrations ||= if (migration_file = path/"tap_migrations.json").file?
|
2016-11-20 13:00:01 -05:00
|
|
|
JSON.parse(migration_file.read)
|
2016-02-25 14:14:33 +08:00
|
|
|
else
|
|
|
|
{}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-12-06 22:21:27 +08:00
|
|
|
def ==(other)
|
|
|
|
other = Tap.fetch(other) if other.is_a?(String)
|
2016-09-17 15:17:27 +01:00
|
|
|
self.class == other.class && name == other.name
|
2015-12-06 22:21:27 +08:00
|
|
|
end
|
|
|
|
|
2015-06-10 15:39:47 +08:00
|
|
|
def self.each
|
|
|
|
return unless TAP_DIRECTORY.directory?
|
|
|
|
|
|
|
|
TAP_DIRECTORY.subdirs.each do |user|
|
|
|
|
user.subdirs.each do |repo|
|
2015-12-02 14:35:42 +08:00
|
|
|
yield fetch(user.basename.to_s, repo.basename.to_s)
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# an array of all installed {Tap} names.
|
2015-06-10 15:39:47 +08:00
|
|
|
def self.names
|
|
|
|
map(&:name)
|
|
|
|
end
|
2015-12-06 21:30:23 +08:00
|
|
|
|
2016-01-15 20:06:15 +08:00
|
|
|
# @private
|
2015-12-06 21:30:23 +08:00
|
|
|
def formula_file_to_name(file)
|
|
|
|
"#{name}/#{file.basename(".rb")}"
|
|
|
|
end
|
|
|
|
|
2016-01-15 20:06:15 +08:00
|
|
|
# @private
|
2015-12-06 21:30:23 +08:00
|
|
|
def alias_file_to_name(file)
|
|
|
|
"#{name}/#{file.basename}"
|
|
|
|
end
|
2016-04-12 18:51:43 +08:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def read_or_set_private_config
|
|
|
|
case config["private"]
|
|
|
|
when "true" then true
|
|
|
|
when "false" then false
|
|
|
|
else
|
|
|
|
config["private"] = begin
|
|
|
|
if custom_remote?
|
|
|
|
true
|
|
|
|
else
|
|
|
|
GitHub.private_repo?(user, "homebrew-#{repo}")
|
|
|
|
end
|
|
|
|
rescue GitHub::HTTPNotFoundError
|
|
|
|
true
|
|
|
|
rescue GitHub::Error
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
|
2016-03-09 17:55:35 +08:00
|
|
|
# A specialized {Tap} class for the core formulae
|
2016-03-07 18:04:25 +08:00
|
|
|
class CoreTap < Tap
|
2016-03-09 17:55:35 +08:00
|
|
|
if OS.mac?
|
2016-04-19 15:25:29 +08:00
|
|
|
def default_remote
|
|
|
|
"https://github.com/Homebrew/homebrew-core"
|
|
|
|
end
|
2016-03-09 17:55:35 +08:00
|
|
|
else
|
2016-04-19 15:25:29 +08:00
|
|
|
def default_remote
|
|
|
|
"https://github.com/Linuxbrew/homebrew-core"
|
|
|
|
end
|
2016-03-09 17:55:35 +08:00
|
|
|
end
|
|
|
|
|
2016-03-07 18:04:25 +08:00
|
|
|
# @private
|
|
|
|
def initialize
|
2016-03-09 17:55:35 +08:00
|
|
|
super "Homebrew", "core"
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.instance
|
2016-10-03 16:12:19 +02:00
|
|
|
@instance ||= new
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
2016-03-09 17:55:35 +08:00
|
|
|
def self.ensure_installed!(options = {})
|
|
|
|
return if instance.installed?
|
|
|
|
args = ["tap", instance.name]
|
|
|
|
args << "-q" if options.fetch(:quiet, true)
|
|
|
|
safe_system HOMEBREW_BREW_FILE, *args
|
|
|
|
end
|
|
|
|
|
2016-03-07 18:04:25 +08:00
|
|
|
# @private
|
|
|
|
def uninstall
|
|
|
|
raise "Tap#uninstall is not available for CoreTap"
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def pin
|
|
|
|
raise "Tap#pin is not available for CoreTap"
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def unpin
|
|
|
|
raise "Tap#unpin is not available for CoreTap"
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def pinned?
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def core_tap?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def formula_dir
|
2016-03-09 17:55:35 +08:00
|
|
|
@formula_dir ||= begin
|
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def alias_dir
|
2016-03-09 17:55:35 +08:00
|
|
|
@alias_dir ||= begin
|
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def formula_renames
|
2016-03-09 17:55:35 +08:00
|
|
|
@formula_renames ||= begin
|
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def tap_migrations
|
2016-03-09 17:55:35 +08:00
|
|
|
@tap_migrations ||= begin
|
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def formula_file_to_name(file)
|
|
|
|
file.basename(".rb").to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
def alias_file_to_name(file)
|
|
|
|
file.basename.to_s
|
|
|
|
end
|
|
|
|
end
|
2016-04-12 11:00:23 +01:00
|
|
|
|
2016-04-12 18:51:43 +08:00
|
|
|
# Permanent configuration per {Tap} using `git-config(1)`
|
2016-04-12 11:00:23 +01:00
|
|
|
class TapConfig
|
|
|
|
attr_reader :tap
|
|
|
|
|
|
|
|
def initialize(tap)
|
|
|
|
@tap = tap
|
|
|
|
end
|
|
|
|
|
|
|
|
def [](key)
|
|
|
|
return unless tap.git?
|
|
|
|
return unless Utils.git_available?
|
|
|
|
|
|
|
|
tap.path.cd do
|
|
|
|
Utils.popen_read("git", "config", "--local", "--get", "homebrew.#{key}").chuzzle
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def []=(key, value)
|
|
|
|
return unless tap.git?
|
|
|
|
return unless Utils.git_available?
|
|
|
|
|
|
|
|
tap.path.cd do
|
|
|
|
safe_system "git", "config", "--local", "--replace-all", "homebrew.#{key}", value.to_s
|
|
|
|
end
|
|
|
|
value
|
|
|
|
end
|
|
|
|
end
|