2023-02-24 10:57:41 +00:00
|
|
|
# typed: true
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2020-02-21 09:59:07 -05:00
|
|
|
require "commands"
|
2021-01-11 12:24:48 -05:00
|
|
|
require "completions"
|
2017-10-07 00:31:28 +02:00
|
|
|
require "extend/cachable"
|
2018-10-13 08:22:51 -07:00
|
|
|
require "description_cache_store"
|
2021-01-12 16:27:25 -05:00
|
|
|
require "settings"
|
2016-01-04 16:22:26 -04:00
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# A {Tap} is used to extend the formulae provided by Homebrew core.
|
2020-11-05 17:17:03 -05:00
|
|
|
# Usually, it's synced with a remote Git repository. And it's likely
|
2018-09-04 09:58:58 +01:00
|
|
|
# a GitHub repository with the name of `user/homebrew-repo`. In such
|
2020-11-05 17:17:03 -05:00
|
|
|
# cases, `user/repo` will be used as the {#name} of this {Tap}, where
|
|
|
|
# {#user} represents the GitHub username and {#repo} represents the repository
|
|
|
|
# name without the leading `homebrew-`.
|
2015-06-10 15:39:47 +08:00
|
|
|
class Tap
|
2017-10-07 00:31:28 +02:00
|
|
|
extend Cachable
|
2015-09-27 16:52:14 +08:00
|
|
|
|
2019-04-19 21:46:20 +09:00
|
|
|
TAP_DIRECTORY = (HOMEBREW_LIBRARY/"Taps").freeze
|
2015-09-27 16:52:14 +08:00
|
|
|
|
2023-04-08 14:10:58 +02:00
|
|
|
HOMEBREW_TAP_CASK_RENAMES_FILE = "cask_renames.json"
|
2020-11-09 08:39:56 -05:00
|
|
|
HOMEBREW_TAP_FORMULA_RENAMES_FILE = "formula_renames.json"
|
|
|
|
HOMEBREW_TAP_MIGRATIONS_FILE = "tap_migrations.json"
|
|
|
|
HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR = "audit_exceptions"
|
2020-11-27 01:23:07 -05:00
|
|
|
HOMEBREW_TAP_STYLE_EXCEPTIONS_DIR = "style_exceptions"
|
2020-11-20 18:14:45 -05:00
|
|
|
HOMEBREW_TAP_PYPI_FORMULA_MAPPINGS = "pypi_formula_mappings.json"
|
2020-11-09 08:39:56 -05:00
|
|
|
|
2023-07-05 17:18:12 +01:00
|
|
|
TAP_MIGRATIONS_STALE_SECONDS = 86400 # 1 day
|
|
|
|
|
2020-11-09 08:39:56 -05:00
|
|
|
HOMEBREW_TAP_JSON_FILES = %W[
|
|
|
|
#{HOMEBREW_TAP_FORMULA_RENAMES_FILE}
|
2023-04-08 14:10:58 +02:00
|
|
|
#{HOMEBREW_TAP_CASK_RENAMES_FILE}
|
2020-11-09 08:39:56 -05:00
|
|
|
#{HOMEBREW_TAP_MIGRATIONS_FILE}
|
|
|
|
#{HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR}/*.json
|
2020-11-27 01:23:07 -05:00
|
|
|
#{HOMEBREW_TAP_STYLE_EXCEPTIONS_DIR}/*.json
|
2020-11-20 18:14:45 -05:00
|
|
|
#{HOMEBREW_TAP_PYPI_FORMULA_MAPPINGS}
|
2020-11-09 08:39:56 -05:00
|
|
|
].freeze
|
|
|
|
|
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
|
2018-09-17 19:44:12 +02:00
|
|
|
user = args.first
|
|
|
|
repo = args.second
|
2015-12-06 21:36:26 +08:00
|
|
|
end
|
|
|
|
|
2019-02-19 13:11:32 +00:00
|
|
|
raise "Invalid tap name '#{args.join("/")}'" if [user, repo].any? { |part| part.nil? || part.include?("/") }
|
2015-12-06 21:36:26 +08:00
|
|
|
|
2017-10-18 23:32:06 -07:00
|
|
|
# We special case homebrew and linuxbrew so that users don't have to shift in a terminal.
|
|
|
|
user = user.capitalize if ["homebrew", "linuxbrew"].include? user
|
2019-02-25 22:56:29 +01:00
|
|
|
repo = repo.sub(HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX, "")
|
2015-12-06 21:36:26 +08:00
|
|
|
|
2019-02-19 13:11:32 +00:00
|
|
|
return CoreTap.instance if ["Homebrew", "Linuxbrew"].include?(user) && ["core", "homebrew"].include?(repo)
|
2015-12-06 21:36:26 +08:00
|
|
|
|
2015-09-27 16:52:14 +08:00
|
|
|
cache_key = "#{user}/#{repo}".downcase
|
2020-06-12 11:42:40 +01:00
|
|
|
cache.fetch(cache_key) { |key| cache[key] = Tap.new(user, repo) }
|
2015-09-27 16:52:14 +08:00
|
|
|
end
|
|
|
|
|
2017-03-18 16:56:59 +02:00
|
|
|
def self.from_path(path)
|
2018-03-29 22:05:02 +02:00
|
|
|
match = File.expand_path(path).match(HOMEBREW_TAP_PATH_REGEX)
|
2020-09-30 16:33:43 +01:00
|
|
|
return if match.blank? || match[:user].blank? || match[:repo].blank?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2017-07-29 16:27:54 +02:00
|
|
|
fetch(match[:user], match[:repo])
|
2017-03-18 16:56:59 +02:00
|
|
|
end
|
|
|
|
|
2020-12-05 03:25:50 +01:00
|
|
|
sig { returns(T.attached_class) }
|
2018-06-09 10:13:28 +02:00
|
|
|
def self.default_cask_tap
|
|
|
|
@default_cask_tap ||= fetch("Homebrew", "cask")
|
|
|
|
end
|
|
|
|
|
2021-01-20 12:13:22 -05:00
|
|
|
sig { params(force: T::Boolean).returns(T::Boolean) }
|
|
|
|
def self.install_default_cask_tap_if_necessary(force: false)
|
2020-12-05 03:25:50 +01:00
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2015-06-10 15:39:47 +08:00
|
|
|
extend Enumerable
|
|
|
|
|
2018-09-04 09:58:58 +01:00
|
|
|
# The user name of this {Tap}. Usually, it's the GitHub username of
|
2018-10-18 21:42:43 -04:00
|
|
|
# this {Tap}'s remote repository.
|
2015-06-10 15:39:47 +08:00
|
|
|
attr_reader :user
|
2015-06-13 01:53:55 +08:00
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# The repository name of this {Tap} without the 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
|
|
|
|
2017-06-12 17:30:02 -04:00
|
|
|
# The full name of this {Tap}, including the `homebrew-` prefix.
|
|
|
|
# It combines {#user} and 'homebrew-'-prefixed {#repo} with a slash.
|
|
|
|
# e.g. `user/homebrew-repo`
|
|
|
|
attr_reader :full_name
|
|
|
|
|
2015-06-13 01:53:55 +08:00
|
|
|
# The local path to this {Tap}.
|
|
|
|
# e.g. `/usr/local/Library/Taps/user/homebrew-repo`
|
2023-04-15 19:06:40 -07:00
|
|
|
sig { returns(Pathname) }
|
2015-06-10 15:39:47 +08:00
|
|
|
attr_reader :path
|
|
|
|
|
2023-04-15 19:06:40 -07:00
|
|
|
# The git repository of this {Tap}.
|
|
|
|
sig { returns(GitRepository) }
|
|
|
|
attr_reader :git_repo
|
2023-04-15 15:06:58 -07:00
|
|
|
|
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
|
2017-06-12 17:30:02 -04:00
|
|
|
@full_name = "#{@user}/homebrew-#{@repo}"
|
2023-04-15 19:06:40 -07:00
|
|
|
@path = TAP_DIRECTORY/@full_name.downcase
|
|
|
|
@git_repo = GitRepository.new(@path)
|
2018-04-07 20:28:56 +01:00
|
|
|
@alias_table = nil
|
|
|
|
@alias_reverse_table = nil
|
2015-06-13 01:57:00 +08:00
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Clear internal cache.
|
2016-02-06 03:50:06 +08:00
|
|
|
def clear_cache
|
|
|
|
@remote = nil
|
2018-05-25 22:47:31 +02:00
|
|
|
@repo_var = nil
|
2016-02-06 03:50:06 +08:00
|
|
|
@formula_dir = nil
|
2016-08-04 14:37:37 +04:00
|
|
|
@cask_dir = nil
|
2018-07-25 11:38:19 +02:00
|
|
|
@command_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
|
2020-11-03 13:06:33 -05:00
|
|
|
@audit_exceptions = nil
|
2020-11-27 01:23:07 -05:00
|
|
|
@style_exceptions = nil
|
2020-11-20 18:14:45 -05:00
|
|
|
@pypi_formula_mappings = nil
|
2016-04-12 11:00:23 +01:00
|
|
|
@config = nil
|
2021-06-19 00:14:33 +01:00
|
|
|
@spell_checker = nil
|
2016-04-12 11:00:23 +01:00
|
|
|
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
|
2023-02-16 12:25:28 +00:00
|
|
|
return default_remote unless installed?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2023-04-15 18:13:38 -07:00
|
|
|
@remote ||= git_repo.origin_url
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2021-03-21 12:35:45 -04:00
|
|
|
# The remote repository name of this {Tap}.
|
|
|
|
# e.g. `user/homebrew-repo`
|
|
|
|
def remote_repo
|
2021-03-22 08:54:38 -04:00
|
|
|
return unless remote
|
|
|
|
|
|
|
|
@remote_repo ||= remote.delete_prefix("https://github.com/")
|
2021-04-22 22:02:09 -04:00
|
|
|
.delete_prefix("git@github.com:")
|
2021-03-22 08:54:38 -04:00
|
|
|
.delete_suffix(".git")
|
2021-03-21 12:35:45 -04:00
|
|
|
end
|
|
|
|
|
2016-04-19 15:25:29 +08:00
|
|
|
# The default remote path to this {Tap}.
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(String) }
|
2016-04-19 15:25:29 +08:00
|
|
|
def default_remote
|
2017-06-12 17:30:02 -04:00
|
|
|
"https://github.com/#{full_name}"
|
2016-04-19 15:25:29 +08:00
|
|
|
end
|
|
|
|
|
2018-05-25 16:21:37 +02:00
|
|
|
def repo_var
|
|
|
|
@repo_var ||= path.to_s
|
2018-09-15 00:04:01 +02:00
|
|
|
.delete_prefix(TAP_DIRECTORY.to_s)
|
2018-05-25 16:21:37 +02:00
|
|
|
.tr("^A-Za-z0-9", "_")
|
|
|
|
.upcase
|
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# True if this {Tap} is a Git repository.
|
2015-07-30 16:51:00 +08:00
|
|
|
def git?
|
2023-04-15 19:06:40 -07:00
|
|
|
git_repo.git_repo?
|
2015-07-30 16:51:00 +08:00
|
|
|
end
|
|
|
|
|
2017-09-27 16:32:13 -04:00
|
|
|
# git branch for this {Tap}.
|
|
|
|
def git_branch
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2023-04-15 18:13:38 -07:00
|
|
|
git_repo.branch_name
|
2017-09-27 16:32:13 -04:00
|
|
|
end
|
|
|
|
|
2016-03-05 20:03:43 +08:00
|
|
|
# git HEAD for this {Tap}.
|
|
|
|
def git_head
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2023-04-15 18:13:38 -07:00
|
|
|
@git_head ||= git_repo.head_ref
|
2016-03-05 20:03:43 +08:00
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Time since last git commit for this {Tap}.
|
2016-03-05 20:03:43 +08:00
|
|
|
def git_last_commit
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2023-04-15 18:13:38 -07:00
|
|
|
git_repo.last_committed
|
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`
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(T.nilable(String)) }
|
2015-12-28 13:23:22 +01:00
|
|
|
def issues_url
|
2021-01-07 13:49:05 -08:00
|
|
|
return if !official? && custom_remote?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2017-06-12 17:30:02 -04:00
|
|
|
"#{default_remote}/issues"
|
2015-12-28 13:23:22 +01:00
|
|
|
end
|
|
|
|
|
2015-06-10 15:39:47 +08:00
|
|
|
def to_s
|
|
|
|
name
|
|
|
|
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)
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-04-12 18:51:43 +08:00
|
|
|
@private = read_or_set_private_config
|
2016-04-12 11:00:23 +01:00
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# {TapConfig} of this {Tap}.
|
2016-04-12 11:00:23 +01:00
|
|
|
def config
|
|
|
|
@config ||= begin
|
|
|
|
raise TapUnavailableError, name unless installed?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-04-12 11:00:23 +01:00
|
|
|
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
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(T::Boolean) }
|
2016-03-07 18:04:25 +08:00
|
|
|
def core_tap?
|
2015-12-07 14:12:57 +08:00
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Install this {Tap}.
|
2015-11-07 16:25:34 +08:00
|
|
|
#
|
2020-02-05 20:22:21 +00:00
|
|
|
# @param clone_target [String] If passed, it will be used as the clone remote.
|
|
|
|
# @param force_auto_update [Boolean, nil] If present, whether to override the
|
2018-04-12 16:14:02 -07:00
|
|
|
# logic that skips non-GitHub repositories during auto-updates.
|
2020-02-05 20:22:21 +00:00
|
|
|
# @param quiet [Boolean] If set, suppress all output.
|
2021-10-11 17:00:43 +08:00
|
|
|
# @param custom_remote [Boolean] If set, change the tap's remote if already installed.
|
2022-09-05 13:57:22 +01:00
|
|
|
# @param verify [Boolean] If set, verify all the formula, casks and aliases in the tap are valid.
|
2023-07-05 19:15:48 +01:00
|
|
|
# @param force [Boolean] If set, force core and cask taps to install even under API mode.
|
|
|
|
def install(quiet: false, clone_target: nil, force_auto_update: nil,
|
|
|
|
custom_remote: false, verify: false, force: false)
|
2015-12-19 19:10:22 +08:00
|
|
|
require "descriptions"
|
2020-08-18 00:23:23 +01:00
|
|
|
require "readall"
|
2015-11-07 16:25:34 +08:00
|
|
|
|
2017-04-22 16:53:48 +01:00
|
|
|
if official? && DEPRECATED_OFFICIAL_TAPS.include?(repo)
|
2020-08-13 04:24:37 +02:00
|
|
|
odie "#{name} was deprecated. This tap is now empty and all its contents were either deleted or migrated."
|
2020-09-28 20:54:16 +02:00
|
|
|
elsif user == "caskroom" || name == "phinze/cask"
|
2022-08-10 14:19:33 +01:00
|
|
|
new_repo = (repo == "cask") ? "cask" : "cask-#{repo}"
|
2020-08-13 04:24:37 +02:00
|
|
|
odie "#{name} was moved. Tap homebrew/#{new_repo} instead."
|
2017-04-22 16:53:48 +01:00
|
|
|
end
|
|
|
|
|
2021-10-11 17:00:43 +08:00
|
|
|
raise TapNoCustomRemoteError, name if custom_remote && clone_target.nil?
|
|
|
|
|
2020-02-05 20:22:21 +00:00
|
|
|
requested_remote = clone_target || default_remote
|
2020-02-02 16:36:01 +01:00
|
|
|
|
2021-10-11 17:00:43 +08:00
|
|
|
if installed? && !custom_remote
|
2020-02-05 20:22:21 +00:00
|
|
|
raise TapRemoteMismatchError.new(name, @remote, requested_remote) if clone_target && requested_remote != remote
|
2020-04-09 18:36:36 +10:00
|
|
|
raise TapAlreadyTappedError, name if force_auto_update.nil? && !shallow?
|
2016-04-04 03:18:55 -07:00
|
|
|
end
|
2016-02-25 21:09:50 +08:00
|
|
|
|
2015-11-07 16:25:34 +08:00
|
|
|
# ensure git is installed
|
2020-08-23 06:32:26 +02:00
|
|
|
Utils::Git.ensure_installed!
|
2016-04-04 03:18:55 -07:00
|
|
|
|
|
|
|
if installed?
|
2021-10-11 17:00:43 +08:00
|
|
|
if requested_remote != remote # we are sure that clone_target is not nil and custom_remote is true here
|
|
|
|
fix_remote_configuration(requested_remote: requested_remote, quiet: quiet)
|
|
|
|
end
|
|
|
|
|
2018-04-12 16:14:02 -07:00
|
|
|
unless force_auto_update.nil?
|
2022-05-06 11:57:43 +08:00
|
|
|
if force_auto_update
|
|
|
|
config["forceautoupdate"] = force_auto_update
|
|
|
|
elsif config["forceautoupdate"] == "true"
|
|
|
|
config.delete("forceautoupdate")
|
|
|
|
end
|
2021-05-06 09:56:30 -04:00
|
|
|
return
|
2018-04-12 16:14:02 -07:00
|
|
|
end
|
|
|
|
|
2021-05-06 09:56:30 -04:00
|
|
|
$stderr.ohai "Unshallowing #{name}" if shallow? && !quiet
|
|
|
|
args = %w[fetch]
|
|
|
|
# Git throws an error when attempting to unshallow a full clone
|
|
|
|
args << "--unshallow" if shallow?
|
2016-04-04 03:18:55 -07:00
|
|
|
args << "-q" if quiet
|
2018-01-15 07:30:56 +00:00
|
|
|
path.cd { safe_system "git", *args }
|
2016-04-04 03:18:55 -07:00
|
|
|
return
|
2023-07-05 19:15:48 +01:00
|
|
|
elsif (core_tap? || name == "homebrew/cask") && !Homebrew::EnvConfig.no_install_from_api? && !force
|
|
|
|
# odeprecated: move to odie in the next minor release. This may break some CI so we should give notice.
|
|
|
|
opoo "Tapping #{name} is no longer typically necessary.\n" \
|
|
|
|
"Add #{Formatter.option("--force")} if you are sure you need one."
|
2016-04-04 03:18:55 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
clear_cache
|
|
|
|
|
2020-09-17 04:18:13 +05:30
|
|
|
$stderr.ohai "Tapping #{name}" unless quiet
|
2016-06-20 13:03:27 +01:00
|
|
|
args = %W[clone #{requested_remote} #{path}]
|
2021-02-09 15:29:54 +00:00
|
|
|
|
|
|
|
# Override possible user configs like:
|
|
|
|
# git config --global clone.defaultRemoteName notorigin
|
|
|
|
args << "--origin=origin"
|
2016-02-25 21:09:50 +08:00
|
|
|
args << "-q" if quiet
|
2015-11-07 16:25:34 +08:00
|
|
|
|
2021-05-14 10:47:52 -04:00
|
|
|
# Override user-set default template
|
|
|
|
args << "--template="
|
2023-07-04 09:56:57 +00:00
|
|
|
# prevent fsmonitor from watching this repo
|
|
|
|
args << "--config" << "core.fsmonitor=false"
|
2021-05-14 10:47:52 -04:00
|
|
|
|
2015-11-07 16:25:34 +08:00
|
|
|
begin
|
2018-01-15 07:30:56 +00:00
|
|
|
safe_system "git", *args
|
2022-03-08 19:24:55 +00:00
|
|
|
|
2022-09-05 13:57:22 +01:00
|
|
|
if verify && !Readall.valid_tap?(self, aliases: true) && !Homebrew::EnvConfig.developer?
|
2020-09-01 14:05:52 +01:00
|
|
|
raise "Cannot tap #{name}: invalid syntax in tap!"
|
2016-07-09 13:51:19 +01:00
|
|
|
end
|
2018-09-02 23:30:07 +02:00
|
|
|
rescue Interrupt, 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
|
2023-04-15 18:13:38 -07:00
|
|
|
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
|
|
|
|
|
2018-04-12 16:14:02 -07:00
|
|
|
config["forceautoupdate"] = force_auto_update unless force_auto_update.nil?
|
|
|
|
|
2020-06-17 18:03:40 -04:00
|
|
|
Commands.rebuild_commands_completion_list
|
2016-09-30 18:22:53 -05:00
|
|
|
link_completions_and_manpages
|
2015-12-08 17:26:53 +00:00
|
|
|
|
2018-09-17 20:11:11 +02:00
|
|
|
formatted_contents = contents.presence&.to_sentence&.dup&.prepend(" ")
|
2020-09-17 04:18:13 +05:30
|
|
|
$stderr.puts "Tapped#{formatted_contents} (#{path.abv})." unless quiet
|
2018-10-13 08:22:51 -07:00
|
|
|
CacheStoreDatabase.use(:descriptions) do |db|
|
|
|
|
DescriptionCacheStore.new(db)
|
|
|
|
.update_from_formula_names!(formula_names)
|
|
|
|
end
|
2022-03-23 00:03:11 -04:00
|
|
|
CacheStoreDatabase.use(:cask_descriptions) do |db|
|
|
|
|
CaskDescriptionCacheStore.new(db)
|
|
|
|
.update_from_cask_tokens!(cask_tokens)
|
|
|
|
end
|
2015-11-07 16:25:34 +08:00
|
|
|
|
2021-01-19 17:55:03 -05:00
|
|
|
if official?
|
2021-01-20 12:02:24 -05:00
|
|
|
untapped = self.class.untapped_official_taps
|
2021-01-19 17:55:03 -05:00
|
|
|
untapped -= [name]
|
|
|
|
|
|
|
|
if untapped.empty?
|
|
|
|
Homebrew::Settings.delete :untapped
|
|
|
|
else
|
|
|
|
Homebrew::Settings.write :untapped, untapped.join(";")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-02-05 20:22:21 +00:00
|
|
|
return if clone_target
|
2016-09-23 22:02:23 +02:00
|
|
|
return unless private?
|
|
|
|
return if quiet
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2022-04-11 20:57:43 +08:00
|
|
|
path.cd do
|
|
|
|
return if Utils.popen_read("git", "config", "--get", "credential.helper").present?
|
|
|
|
end
|
|
|
|
|
2020-09-17 04:18:13 +05:30
|
|
|
$stderr.puts <<~EOS
|
2016-09-23 22:02:23 +02:00
|
|
|
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}
|
2017-06-12 17:30:02 -04:00
|
|
|
git remote set-url origin git@github.com:#{full_name}.git
|
2016-09-23 22:02:23 +02:00
|
|
|
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)
|
2021-01-11 12:24:48 -05:00
|
|
|
|
2021-01-13 11:16:09 -05:00
|
|
|
Homebrew::Completions.show_completions_message_if_needed
|
|
|
|
if official? || Homebrew::Completions.link_completions?
|
2021-01-11 12:24:48 -05:00
|
|
|
Utils::Link.link_completions(path, command)
|
|
|
|
else
|
|
|
|
Utils::Link.unlink_completions(path)
|
|
|
|
end
|
2015-12-08 17:26:53 +00:00
|
|
|
end
|
|
|
|
|
2021-10-11 17:00:43 +08:00
|
|
|
def fix_remote_configuration(requested_remote: nil, quiet: false)
|
2021-10-12 20:21:58 +08:00
|
|
|
if requested_remote.present?
|
2021-10-11 17:00:43 +08:00
|
|
|
path.cd do
|
|
|
|
safe_system "git", "remote", "set-url", "origin", requested_remote
|
|
|
|
safe_system "git", "config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"
|
|
|
|
end
|
|
|
|
$stderr.ohai "#{name}: changed remote from #{remote} to #{requested_remote}" unless quiet
|
|
|
|
end
|
2021-01-25 23:57:01 -05:00
|
|
|
|
2023-04-15 19:35:00 -07:00
|
|
|
current_upstream_head = T.must(git_repo.origin_branch_name)
|
2023-04-15 18:13:38 -07:00
|
|
|
return if requested_remote.blank? && git_repo.origin_has_branch?(current_upstream_head)
|
2021-01-25 23:57:01 -05:00
|
|
|
|
2021-10-11 17:00:43 +08:00
|
|
|
args = %w[fetch]
|
2021-10-12 20:21:58 +08:00
|
|
|
args << "--quiet" if quiet
|
2021-10-11 17:00:43 +08:00
|
|
|
args << "origin"
|
|
|
|
safe_system "git", "-C", path, *args
|
2023-04-15 18:13:38 -07:00
|
|
|
git_repo.set_head_origin_auto
|
2021-01-25 23:57:01 -05:00
|
|
|
|
2023-04-15 19:35:00 -07:00
|
|
|
new_upstream_head = T.must(git_repo.origin_branch_name)
|
2021-10-12 21:58:40 +08:00
|
|
|
return if new_upstream_head == current_upstream_head
|
2021-10-11 17:00:43 +08:00
|
|
|
|
2023-04-15 18:13:38 -07:00
|
|
|
git_repo.rename_branch old: current_upstream_head, new: new_upstream_head
|
|
|
|
git_repo.set_upstream_branch local: new_upstream_head, origin: new_upstream_head
|
2021-01-25 23:57:01 -05:00
|
|
|
|
2021-10-12 20:38:09 +08:00
|
|
|
return if quiet
|
|
|
|
|
|
|
|
$stderr.ohai "#{name}: changed default branch name from #{current_upstream_head} to #{new_upstream_head}!"
|
2021-01-25 23:57:01 -05:00
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Uninstall this {Tap}.
|
2021-01-21 08:42:22 -05:00
|
|
|
def uninstall(manual: false)
|
2015-12-19 19:10:22 +08:00
|
|
|
require "descriptions"
|
2015-11-07 16:25:34 +08:00
|
|
|
raise TapUnavailableError, name unless installed?
|
|
|
|
|
2020-09-17 04:18:13 +05:30
|
|
|
$stderr.puts "Untapping #{name}..."
|
2018-06-19 17:59:25 +02:00
|
|
|
|
|
|
|
abv = path.abv
|
2018-09-17 20:11:11 +02:00
|
|
|
formatted_contents = contents.presence&.to_sentence&.dup&.prepend(" ")
|
2018-06-19 17:59:25 +02:00
|
|
|
|
2018-10-13 08:22:51 -07:00
|
|
|
CacheStoreDatabase.use(:descriptions) do |db|
|
|
|
|
DescriptionCacheStore.new(db)
|
|
|
|
.delete_from_formula_names!(formula_names)
|
|
|
|
end
|
2022-03-23 00:03:11 -04:00
|
|
|
CacheStoreDatabase.use(:cask_descriptions) do |db|
|
|
|
|
CaskDescriptionCacheStore.new(db)
|
|
|
|
.delete_from_cask_tokens!(cask_tokens)
|
|
|
|
end
|
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
|
2020-09-17 04:18:13 +05:30
|
|
|
$stderr.puts "Untapped#{formatted_contents} (#{abv})."
|
2020-06-17 18:03:40 -04:00
|
|
|
|
|
|
|
Commands.rebuild_commands_completion_list
|
2016-02-06 03:50:06 +08:00
|
|
|
clear_cache
|
2021-01-19 17:55:03 -05:00
|
|
|
|
2021-01-21 08:42:22 -05:00
|
|
|
return if !manual || !official?
|
2021-01-19 17:55:03 -05:00
|
|
|
|
2021-01-20 12:02:24 -05:00
|
|
|
untapped = self.class.untapped_official_taps
|
2021-01-19 17:55:03 -05:00
|
|
|
return if untapped.include? name
|
|
|
|
|
|
|
|
untapped << name
|
|
|
|
Homebrew::Settings.write :untapped, untapped.join(";")
|
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
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2016-09-17 15:17:27 +01:00
|
|
|
remote.casecmp(default_remote).nonzero?
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Path to the directory of all {Formula} files for this {Tap}.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Pathname) }
|
2015-12-06 20:59:31 +08:00
|
|
|
def formula_dir
|
2023-02-24 10:57:41 +00:00
|
|
|
# Official formulae taps always use this directory, saves time to hardcode.
|
|
|
|
@formula_dir ||= if official?
|
|
|
|
path/"Formula"
|
|
|
|
else
|
|
|
|
potential_formula_dirs.find(&:directory?) || (path/"Formula")
|
|
|
|
end
|
2016-12-13 01:53:05 +00:00
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[Pathname]) }
|
2016-12-13 01:53:05 +00:00
|
|
|
def potential_formula_dirs
|
2023-04-15 18:13:38 -07:00
|
|
|
@potential_formula_dirs ||= [path/"Formula", path/"HomebrewFormula", path].freeze
|
2015-12-06 20:59:31 +08:00
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Path to the directory of all {Cask} files for this {Tap}.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Pathname) }
|
2016-08-04 14:37:37 +04:00
|
|
|
def cask_dir
|
2017-03-12 19:18:41 +01:00
|
|
|
@cask_dir ||= path/"Casks"
|
2016-08-04 14:37:37 +04:00
|
|
|
end
|
|
|
|
|
2018-06-20 20:35:24 +02:00
|
|
|
def contents
|
|
|
|
contents = []
|
|
|
|
|
|
|
|
if (command_count = command_files.count).positive?
|
2023-03-20 07:23:17 -04:00
|
|
|
contents << Utils.pluralize("command", command_count, include_count: true)
|
2018-06-20 20:35:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
if (cask_count = cask_files.count).positive?
|
2023-03-20 07:23:17 -04:00
|
|
|
contents << Utils.pluralize("cask", cask_count, include_count: true)
|
2018-06-20 20:35:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
if (formula_count = formula_files.count).positive?
|
2023-03-20 07:23:17 -04:00
|
|
|
contents << Utils.pluralize("formula", formula_count, plural: "e", include_count: true)
|
2018-06-20 20:35:24 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
contents
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# An array of all {Formula} files of this {Tap}.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[Pathname]) }
|
2015-06-10 15:39:47 +08:00
|
|
|
def formula_files
|
2017-03-12 19:18:41 +01:00
|
|
|
@formula_files ||= if formula_dir.directory?
|
2023-07-06 16:47:09 +01:00
|
|
|
formula_dir.find.select(&method(:formula_file?))
|
2016-08-25 05:19:14 +02:00
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
# A cached hash of {Formula} basenames to {Formula} file pathnames for a {Tap}
|
|
|
|
sig { params(tap: Tap).returns(T::Hash[String, Pathname]) }
|
|
|
|
def self.formula_files_by_name(tap)
|
|
|
|
cache_key = "formula_files_by_name_#{tap}"
|
|
|
|
cache.fetch(cache_key) do |key|
|
2023-06-28 15:27:20 +01:00
|
|
|
cache[key] = tap.formula_files_by_name
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
|
|
|
sig { returns(T::Hash[String, Pathname]) }
|
|
|
|
def formula_files_by_name
|
|
|
|
formula_files.each_with_object({}) do |file, hash|
|
|
|
|
# If there's more than one file with the same basename: intentionally
|
|
|
|
# ignore the later ones here.
|
|
|
|
hash[file.basename.to_s] ||= file
|
2023-02-24 10:57:41 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# An array of all {Cask} files of this {Tap}.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[Pathname]) }
|
2016-08-25 05:19:14 +02:00
|
|
|
def cask_files
|
2017-03-12 19:18:41 +01:00
|
|
|
@cask_files ||= if cask_dir.directory?
|
2023-07-06 16:47:09 +01:00
|
|
|
cask_dir.find.select(&method(:ruby_file?))
|
2015-09-27 16:52:14 +08:00
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
# A cached hash of {Cask} basenames to {Cask} file pathnames for a {Tap}
|
|
|
|
sig { params(tap: Tap).returns(T::Hash[String, Pathname]) }
|
|
|
|
def self.cask_files_by_name(tap)
|
|
|
|
cache_key = "cask_files_by_name_#{tap}"
|
|
|
|
|
|
|
|
cache.fetch(cache_key) do |key|
|
|
|
|
cache[key] = tap.cask_files.each_with_object({}) do |file, hash|
|
|
|
|
# If there's more than one file with the same basename: intentionally
|
|
|
|
# ignore the later ones here.
|
|
|
|
hash[file.basename.to_s] ||= file
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-10 17:16:18 +02:00
|
|
|
# returns true if the file has a Ruby extension
|
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(file: Pathname).returns(T::Boolean) }
|
2018-10-10 17:16:18 +02:00
|
|
|
def ruby_file?(file)
|
|
|
|
file.extname == ".rb"
|
|
|
|
end
|
|
|
|
|
2023-04-06 04:59:55 +08:00
|
|
|
# returns true if given path would present a {Formula} file in this {Tap}.
|
2016-02-15 22:31:47 +08:00
|
|
|
# accepts both absolute path and relative path (relative to this {Tap}'s path)
|
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(file: T.any(String, Pathname)).returns(T::Boolean) }
|
2016-02-15 22:31:47 +08:00
|
|
|
def formula_file?(file)
|
|
|
|
file = Pathname.new(file) unless file.is_a? Pathname
|
2023-04-15 18:13:38 -07:00
|
|
|
file = file.expand_path(path)
|
2023-02-24 10:57:41 +00:00
|
|
|
return false unless ruby_file?(file)
|
2023-04-06 05:03:56 +08:00
|
|
|
return false if cask_file?(file)
|
2023-02-24 10:57:41 +00:00
|
|
|
|
|
|
|
file.to_s.start_with?("#{formula_dir}/")
|
2016-02-15 22:31:47 +08:00
|
|
|
end
|
|
|
|
|
2023-04-06 04:59:55 +08:00
|
|
|
# returns 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
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(file: T.any(String, Pathname)).returns(T::Boolean) }
|
2016-08-04 14:37:37 +04:00
|
|
|
def cask_file?(file)
|
|
|
|
file = Pathname.new(file) unless file.is_a? Pathname
|
2023-04-15 18:13:38 -07:00
|
|
|
file = file.expand_path(path)
|
2023-02-24 10:57:41 +00:00
|
|
|
return false unless ruby_file?(file)
|
|
|
|
|
|
|
|
file.to_s.start_with?("#{cask_dir}/")
|
2016-08-04 14:37:37 +04:00
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# An array of all {Formula} names of this {Tap}.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[String]) }
|
2015-06-10 15:39:47 +08:00
|
|
|
def formula_names
|
2020-02-21 09:56:52 -05:00
|
|
|
@formula_names ||= formula_files.map(&method(:formula_file_to_name))
|
|
|
|
end
|
|
|
|
|
|
|
|
# An array of all {Cask} tokens of this {Tap}.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[String]) }
|
2020-02-21 09:56:52 -05:00
|
|
|
def cask_tokens
|
|
|
|
@cask_tokens ||= cask_files.map(&method(:formula_file_to_name))
|
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
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Pathname) }
|
2015-12-06 21:15:43 +08:00
|
|
|
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
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[Pathname]) }
|
2015-09-12 18:21:22 +08:00
|
|
|
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
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[String]) }
|
2015-09-12 18:21:22 +08:00
|
|
|
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
|
2018-09-17 02:45:00 +02:00
|
|
|
|
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
|
2018-09-17 02:45:00 +02:00
|
|
|
|
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
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Pathname) }
|
2018-07-25 11:38:19 +02:00
|
|
|
def command_dir
|
|
|
|
@command_dir ||= path/"cmd"
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# An array of all commands files of this {Tap}.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[Pathname]) }
|
2015-06-10 15:39:47 +08:00
|
|
|
def command_files
|
2018-07-25 11:38:19 +02:00
|
|
|
@command_files ||= if command_dir.directory?
|
2020-02-21 09:59:07 -05:00
|
|
|
Commands.find_commands(command_dir)
|
2018-07-25 11:38:19 +02:00
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Hash) }
|
2015-06-10 15:39:47 +08:00
|
|
|
def to_hash
|
2015-08-13 14:56:14 +08:00
|
|
|
hash = {
|
2018-11-02 17:18:07 +00:00
|
|
|
"name" => name,
|
|
|
|
"user" => user,
|
|
|
|
"repo" => repo,
|
|
|
|
"path" => path.to_s,
|
|
|
|
"installed" => installed?,
|
|
|
|
"official" => official?,
|
2015-06-10 15:39:47 +08:00
|
|
|
"formula_names" => formula_names,
|
|
|
|
"formula_files" => formula_files.map(&:to_s),
|
2020-02-21 09:56:52 -05:00
|
|
|
"cask_tokens" => cask_tokens,
|
2020-02-16 15:57:40 -08:00
|
|
|
"cask_files" => cask_files.map(&:to_s),
|
2020-02-21 09:59:07 -05:00
|
|
|
"command_files" => command_files.map(&:to_s),
|
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
|
|
|
|
|
2023-04-08 14:10:58 +02:00
|
|
|
# Hash with tap cask renames.
|
|
|
|
sig { returns(T::Hash[String, String]) }
|
|
|
|
def cask_renames
|
|
|
|
@cask_renames ||= if name == "homebrew/cask" && !Homebrew::EnvConfig.no_install_from_api?
|
|
|
|
Homebrew::API::Cask.all_renames
|
|
|
|
elsif (rename_file = path/HOMEBREW_TAP_CASK_RENAMES_FILE).file?
|
|
|
|
JSON.parse(rename_file.read)
|
|
|
|
else
|
|
|
|
{}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Hash with tap formula renames.
|
2023-04-08 14:10:58 +02:00
|
|
|
sig { returns(T::Hash[String, String]) }
|
2015-08-09 14:39:46 +03:00
|
|
|
def formula_renames
|
2020-11-09 08:39:56 -05:00
|
|
|
@formula_renames ||= if (rename_file = path/HOMEBREW_TAP_FORMULA_RENAMES_FILE).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
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Hash with tap migrations.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Hash) }
|
2016-02-25 14:14:33 +08:00
|
|
|
def tap_migrations
|
2023-07-05 17:18:12 +01:00
|
|
|
@tap_migrations ||= if name == "homebrew/cask" && !Homebrew::EnvConfig.no_install_from_api?
|
|
|
|
migrations, = Homebrew::API.fetch_json_api_file "cask_tap_migrations.jws.json",
|
|
|
|
stale_seconds: TAP_MIGRATIONS_STALE_SECONDS
|
|
|
|
migrations
|
|
|
|
elsif (migration_file = path/HOMEBREW_TAP_MIGRATIONS_FILE).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
|
|
|
|
|
2020-11-03 13:06:33 -05:00
|
|
|
# Hash with audit exceptions
|
2020-11-20 18:14:45 -05:00
|
|
|
sig { returns(Hash) }
|
2020-11-03 13:06:33 -05:00
|
|
|
def audit_exceptions
|
2020-11-20 18:14:45 -05:00
|
|
|
@audit_exceptions = read_formula_list_directory "#{HOMEBREW_TAP_AUDIT_EXCEPTIONS_DIR}/*"
|
2020-11-20 00:55:21 -05:00
|
|
|
end
|
2020-11-04 23:43:22 -05:00
|
|
|
|
2020-11-27 01:23:07 -05:00
|
|
|
# Hash with style exceptions
|
|
|
|
sig { returns(Hash) }
|
|
|
|
def style_exceptions
|
|
|
|
@style_exceptions = read_formula_list_directory "#{HOMEBREW_TAP_STYLE_EXCEPTIONS_DIR}/*"
|
|
|
|
end
|
|
|
|
|
2020-11-20 18:14:45 -05:00
|
|
|
# Hash with pypi formula mappings
|
|
|
|
def pypi_formula_mappings
|
|
|
|
@pypi_formula_mappings = read_formula_list path/HOMEBREW_TAP_PYPI_FORMULA_MAPPINGS
|
2020-11-03 13:06:33 -05:00
|
|
|
end
|
|
|
|
|
2023-02-15 03:24:15 +00:00
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Boolean) }
|
2023-02-15 03:24:15 +00:00
|
|
|
def should_report_analytics?
|
2023-02-24 13:25:18 +00:00
|
|
|
return !Homebrew::EnvConfig.no_install_from_api? && official? unless installed?
|
2023-02-15 03:24:15 +00:00
|
|
|
|
|
|
|
!private?
|
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(other: T.nilable(T.any(String, Tap))).returns(T::Boolean) }
|
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
|
|
|
|
|
2020-09-19 02:03:49 +02:00
|
|
|
def self.each(&block)
|
2015-06-10 15:39:47 +08:00
|
|
|
return unless TAP_DIRECTORY.directory?
|
|
|
|
|
2020-11-16 22:18:56 +01:00
|
|
|
return to_enum unless block
|
2017-08-03 17:18:13 -07:00
|
|
|
|
2015-06-10 15:39:47 +08:00
|
|
|
TAP_DIRECTORY.subdirs.each do |user|
|
|
|
|
user.subdirs.each do |repo|
|
2021-11-02 11:16:10 +00:00
|
|
|
yield fetch(user.basename.to_s, repo.basename.to_s)
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# An array of all installed {Tap} names.
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[String]) }
|
2015-06-10 15:39:47 +08:00
|
|
|
def self.names
|
2017-10-14 06:13:40 +01:00
|
|
|
map(&:name).sort
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
2015-12-06 21:30:23 +08:00
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# An array of all tap cmd directory {Pathname}s.
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(T::Array[Pathname]) }
|
2017-11-05 15:37:57 +00:00
|
|
|
def self.cmd_directories
|
|
|
|
Pathname.glob TAP_DIRECTORY/"*/*/cmd"
|
|
|
|
end
|
|
|
|
|
2021-01-20 12:02:24 -05:00
|
|
|
# An array of official taps that have been manually untapped
|
|
|
|
sig { returns(T::Array[String]) }
|
|
|
|
def self.untapped_official_taps
|
|
|
|
Homebrew::Settings.read(:untapped)&.split(";") || []
|
|
|
|
end
|
|
|
|
|
2016-01-15 20:06:15 +08:00
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(file: Pathname).returns(String) }
|
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
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(file: Pathname).returns(String) }
|
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
|
|
|
|
2021-10-04 21:45:20 -04:00
|
|
|
def audit_exception(list, formula_or_cask, value = nil)
|
|
|
|
return false if audit_exceptions.blank?
|
|
|
|
return false unless audit_exceptions.key? list
|
|
|
|
|
|
|
|
list = audit_exceptions[list]
|
|
|
|
|
|
|
|
case list
|
|
|
|
when Array
|
|
|
|
list.include? formula_or_cask
|
|
|
|
when Hash
|
|
|
|
return false unless list.include? formula_or_cask
|
|
|
|
return list[formula_or_cask] if value.blank?
|
|
|
|
|
|
|
|
list[formula_or_cask] == value
|
|
|
|
end
|
|
|
|
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
|
2017-06-12 17:30:02 -04:00
|
|
|
GitHub.private_repo?(full_name)
|
2016-04-12 18:51:43 +08:00
|
|
|
end
|
2021-02-15 21:48:21 +05:30
|
|
|
rescue GitHub::API::HTTPNotFoundError
|
2016-04-12 18:51:43 +08:00
|
|
|
true
|
2021-02-15 21:48:21 +05:30
|
|
|
rescue GitHub::API::Error
|
2016-04-12 18:51:43 +08:00
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-11-20 00:55:21 -05:00
|
|
|
|
2020-11-27 17:45:42 +11:00
|
|
|
sig { params(file: Pathname).returns(T.any(T::Array[String], Hash)) }
|
2020-11-20 18:14:45 -05:00
|
|
|
def read_formula_list(file)
|
|
|
|
JSON.parse file.read
|
|
|
|
rescue JSON::ParserError
|
|
|
|
opoo "#{file} contains invalid JSON"
|
|
|
|
{}
|
|
|
|
rescue Errno::ENOENT
|
|
|
|
{}
|
|
|
|
end
|
|
|
|
|
|
|
|
sig { params(directory: String).returns(Hash) }
|
2020-11-20 00:55:21 -05:00
|
|
|
def read_formula_list_directory(directory)
|
|
|
|
list = {}
|
|
|
|
|
2020-11-20 18:14:45 -05:00
|
|
|
Pathname.glob(path/directory).each do |exception_file|
|
2020-11-20 00:55:21 -05:00
|
|
|
list_name = exception_file.basename.to_s.chomp(".json").to_sym
|
2020-11-20 18:14:45 -05:00
|
|
|
list_contents = read_formula_list exception_file
|
2020-11-20 00:55:21 -05:00
|
|
|
|
2020-11-20 18:14:45 -05:00
|
|
|
next if list_contents.blank?
|
2020-11-20 00:55:21 -05:00
|
|
|
|
|
|
|
list[list_name] = list_contents
|
|
|
|
end
|
|
|
|
|
|
|
|
list
|
|
|
|
end
|
2015-06-10 15:39:47 +08:00
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# A specialized {Tap} class for the core formulae.
|
2016-03-07 18:04:25 +08:00
|
|
|
class CoreTap < Tap
|
|
|
|
# @private
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { void }
|
2016-03-07 18:04:25 +08:00
|
|
|
def initialize
|
2016-03-09 17:55:35 +08:00
|
|
|
super "Homebrew", "core"
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(CoreTap) }
|
2016-03-07 18:04:25 +08:00
|
|
|
def self.instance
|
2016-10-03 16:12:19 +02:00
|
|
|
@instance ||= new
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { void }
|
2017-09-07 12:09:52 +01:00
|
|
|
def self.ensure_installed!
|
2016-03-09 17:55:35 +08:00
|
|
|
return if instance.installed?
|
2023-02-24 13:25:18 +00:00
|
|
|
return unless Homebrew::EnvConfig.no_install_from_api?
|
2023-02-14 09:54:24 +00:00
|
|
|
return if Homebrew::EnvConfig.automatically_set_no_install_from_api?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2022-11-09 14:45:43 +00:00
|
|
|
# Tests override homebrew-core locations and we don't want to auto-tap in them.
|
|
|
|
return if ENV["HOMEBREW_TESTS"]
|
|
|
|
|
2023-02-03 00:32:18 +00:00
|
|
|
instance.install
|
2016-03-09 17:55:35 +08:00
|
|
|
end
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(String) }
|
2021-12-07 00:13:56 +00:00
|
|
|
def remote
|
2023-04-05 09:19:20 +01:00
|
|
|
super if Homebrew::EnvConfig.no_install_from_api?
|
2021-12-07 00:13:56 +00:00
|
|
|
|
|
|
|
Homebrew::EnvConfig.core_git_remote
|
|
|
|
end
|
|
|
|
|
2020-10-09 09:32:38 +01:00
|
|
|
# CoreTap never allows shallow clones (on request from GitHub).
|
2023-07-05 19:15:48 +01:00
|
|
|
def install(quiet: false, clone_target: nil, force_auto_update: nil,
|
|
|
|
custom_remote: false, verify: false, force: false)
|
2021-10-13 19:52:44 +08:00
|
|
|
remote = Homebrew::EnvConfig.core_git_remote # set by HOMEBREW_CORE_GIT_REMOTE
|
|
|
|
requested_remote = clone_target || remote
|
2021-10-11 17:00:43 +08:00
|
|
|
|
2023-02-10 23:15:40 -05:00
|
|
|
# The remote will changed again on `brew update` since remotes for homebrew/core are mismatched
|
2021-10-11 17:00:43 +08:00
|
|
|
raise TapCoreRemoteMismatchError.new(name, remote, requested_remote) if requested_remote != remote
|
|
|
|
|
2020-09-17 04:18:13 +05:30
|
|
|
if remote != default_remote
|
2023-02-10 23:15:40 -05:00
|
|
|
$stderr.puts "HOMEBREW_CORE_GIT_REMOTE set: using #{remote} as the Homebrew/homebrew-core Git remote."
|
2020-09-17 04:18:13 +05:30
|
|
|
end
|
2021-10-11 17:00:43 +08:00
|
|
|
|
2023-07-05 19:15:48 +01:00
|
|
|
super(quiet: quiet, clone_target: remote, force_auto_update: force_auto_update,
|
|
|
|
custom_remote: custom_remote, force: force)
|
2020-01-13 09:43:02 +00:00
|
|
|
end
|
|
|
|
|
2016-03-07 18:04:25 +08:00
|
|
|
# @private
|
2021-06-12 18:02:23 -04:00
|
|
|
sig { params(manual: T::Boolean).void }
|
|
|
|
def uninstall(manual: false)
|
2023-02-24 13:25:18 +00:00
|
|
|
raise "Tap#uninstall is not available for CoreTap" if Homebrew::EnvConfig.no_install_from_api?
|
2021-06-12 18:02:23 -04:00
|
|
|
|
|
|
|
super
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(T::Boolean) }
|
2016-03-07 18:04:25 +08:00
|
|
|
def core_tap?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2021-10-20 11:01:38 +01:00
|
|
|
# @private
|
|
|
|
sig { returns(T::Boolean) }
|
|
|
|
def linuxbrew_core?
|
2021-12-11 22:27:46 +00:00
|
|
|
remote_repo.to_s.end_with?("/linuxbrew-core") || remote_repo == "Linuxbrew/homebrew-core"
|
2021-10-20 11:01:38 +01:00
|
|
|
end
|
|
|
|
|
2016-03-07 18:04:25 +08:00
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Pathname) }
|
2016-03-07 18:04:25 +08:00
|
|
|
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
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Pathname) }
|
2016-03-07 18:04:25 +08:00
|
|
|
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
|
2023-04-08 14:10:58 +02:00
|
|
|
sig { returns(T::Hash[String, String]) }
|
2016-03-07 18:04:25 +08:00
|
|
|
def formula_renames
|
2023-04-27 04:09:41 +01:00
|
|
|
@formula_renames ||= if Homebrew::EnvConfig.no_install_from_api?
|
2016-03-09 17:55:35 +08:00
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
2023-04-27 04:09:41 +01:00
|
|
|
else
|
|
|
|
Homebrew::API::Formula.all_renames
|
2016-03-09 17:55:35 +08:00
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Hash) }
|
2016-03-07 18:04:25 +08:00
|
|
|
def tap_migrations
|
2023-07-05 17:18:12 +01:00
|
|
|
@tap_migrations ||= if Homebrew::EnvConfig.no_install_from_api?
|
2016-03-09 17:55:35 +08:00
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
2023-07-05 17:18:12 +01:00
|
|
|
else
|
|
|
|
migrations, = Homebrew::API.fetch_json_api_file "formula_tap_migrations.jws.json",
|
|
|
|
stale_seconds: TAP_MIGRATIONS_STALE_SECONDS
|
|
|
|
migrations
|
2016-03-09 17:55:35 +08:00
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
|
|
|
|
2020-11-03 13:06:33 -05:00
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Hash) }
|
2020-11-03 13:06:33 -05:00
|
|
|
def audit_exceptions
|
|
|
|
@audit_exceptions ||= begin
|
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-27 01:23:07 -05:00
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Hash) }
|
2020-11-27 01:23:07 -05:00
|
|
|
def style_exceptions
|
|
|
|
@style_exceptions ||= begin
|
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(Hash) }
|
2020-11-20 18:14:45 -05:00
|
|
|
def pypi_formula_mappings
|
|
|
|
@pypi_formula_mappings ||= begin
|
2020-11-20 00:55:21 -05:00
|
|
|
self.class.ensure_installed!
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-03-07 18:04:25 +08:00
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(file: Pathname).returns(String) }
|
2016-03-07 18:04:25 +08:00
|
|
|
def formula_file_to_name(file)
|
|
|
|
file.basename(".rb").to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(file: Pathname).returns(String) }
|
2016-03-07 18:04:25 +08:00
|
|
|
def alias_file_to_name(file)
|
|
|
|
file.basename.to_s
|
|
|
|
end
|
2023-01-23 13:32:54 -05:00
|
|
|
|
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[String]) }
|
2023-01-23 13:32:54 -05:00
|
|
|
def aliases
|
2023-04-05 09:19:20 +01:00
|
|
|
return super if Homebrew::EnvConfig.no_install_from_api?
|
2023-01-23 13:32:54 -05:00
|
|
|
|
|
|
|
Homebrew::API::Formula.all_aliases.keys
|
|
|
|
end
|
|
|
|
|
2023-06-28 15:27:20 +01:00
|
|
|
# @private
|
|
|
|
sig { returns(T::Array[Pathname]) }
|
|
|
|
def formula_files
|
|
|
|
return super if Homebrew::EnvConfig.no_install_from_api? || installed?
|
|
|
|
|
|
|
|
raise TapUnavailableError, name
|
|
|
|
end
|
|
|
|
|
2023-01-23 13:32:54 -05:00
|
|
|
# @private
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { returns(T::Array[String]) }
|
2023-01-23 13:32:54 -05:00
|
|
|
def formula_names
|
2023-04-05 09:19:20 +01:00
|
|
|
return super if Homebrew::EnvConfig.no_install_from_api?
|
2023-01-23 13:32:54 -05:00
|
|
|
|
|
|
|
Homebrew::API::Formula.all_formulae.keys
|
|
|
|
end
|
2023-06-28 15:27:20 +01:00
|
|
|
|
|
|
|
# @private
|
|
|
|
sig { returns(T::Hash[String, Pathname]) }
|
|
|
|
def formula_files_by_name
|
|
|
|
return super if Homebrew::EnvConfig.no_install_from_api?
|
|
|
|
|
|
|
|
formula_names.each_with_object({}) do |name, hash|
|
|
|
|
# If there's more than one file with the same basename: intentionally
|
|
|
|
# ignore the later ones here.
|
|
|
|
hash[name] ||= sharded_formula_path(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
# @private
|
|
|
|
sig { params(name: String).returns(Pathname) }
|
|
|
|
def sharded_formula_path(name)
|
|
|
|
# TODO: add sharding logic.
|
|
|
|
formula_dir/"#{name}.rb"
|
|
|
|
end
|
2016-03-07 18:04:25 +08:00
|
|
|
end
|
2016-04-12 11:00:23 +01:00
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Permanent configuration per {Tap} using `git-config(1)`.
|
2016-04-12 11:00:23 +01:00
|
|
|
class TapConfig
|
|
|
|
attr_reader :tap
|
|
|
|
|
2023-02-24 10:57:41 +00:00
|
|
|
sig { params(tap: Tap).void }
|
2016-04-12 11:00:23 +01:00
|
|
|
def initialize(tap)
|
|
|
|
@tap = tap
|
|
|
|
end
|
|
|
|
|
|
|
|
def [](key)
|
|
|
|
return unless tap.git?
|
2020-08-23 06:32:26 +02:00
|
|
|
return unless Utils::Git.available?
|
2016-04-12 11:00:23 +01:00
|
|
|
|
2021-01-13 11:16:09 -05:00
|
|
|
Homebrew::Settings.read key, repo: tap.path
|
2016-04-12 11:00:23 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def []=(key, value)
|
|
|
|
return unless tap.git?
|
2020-08-23 06:32:26 +02:00
|
|
|
return unless Utils::Git.available?
|
2016-04-12 11:00:23 +01:00
|
|
|
|
2021-01-13 11:16:09 -05:00
|
|
|
Homebrew::Settings.write key, value.to_s, repo: tap.path
|
2016-04-12 11:00:23 +01:00
|
|
|
end
|
2022-05-06 11:57:43 +08:00
|
|
|
|
|
|
|
def delete(key)
|
|
|
|
return unless tap.git?
|
|
|
|
return unless Utils::Git.available?
|
|
|
|
|
|
|
|
Homebrew::Settings.delete key, repo: tap.path
|
|
|
|
end
|
2016-04-12 11:00:23 +01:00
|
|
|
end
|
2018-10-02 15:43:16 -07:00
|
|
|
|
|
|
|
require "extend/os/tap"
|