Merge pull request #12221 from XuehaiPan/brew-retap
tap: allow to change tap remote with `brew tap --custom-remote`
This commit is contained in:
commit
cc2c19be98
@ -293,6 +293,9 @@ module Homebrew
|
|||||||
|
|
||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def write_only?; end
|
def write_only?; end
|
||||||
|
|
||||||
|
sig { returns(T::Boolean) }
|
||||||
|
def custom_remote?; end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -35,6 +35,8 @@ module Homebrew
|
|||||||
switch "--force-auto-update",
|
switch "--force-auto-update",
|
||||||
description: "Auto-update tap even if it is not hosted on GitHub. By default, only taps "\
|
description: "Auto-update tap even if it is not hosted on GitHub. By default, only taps "\
|
||||||
"hosted on GitHub are auto-updated (for performance reasons)."
|
"hosted on GitHub are auto-updated (for performance reasons)."
|
||||||
|
switch "--custom-remote",
|
||||||
|
description: "Install or change a tap with a custom remote. Useful for mirrors."
|
||||||
switch "--repair",
|
switch "--repair",
|
||||||
description: "Migrate tapped formulae from symlink-based to directory-based structure."
|
description: "Migrate tapped formulae from symlink-based to directory-based structure."
|
||||||
switch "--list-pinned",
|
switch "--list-pinned",
|
||||||
@ -64,8 +66,9 @@ module Homebrew
|
|||||||
begin
|
begin
|
||||||
tap.install clone_target: args.named.second,
|
tap.install clone_target: args.named.second,
|
||||||
force_auto_update: force_auto_update?(args: args),
|
force_auto_update: force_auto_update?(args: args),
|
||||||
|
custom_remote: args.custom_remote?,
|
||||||
quiet: args.quiet?
|
quiet: args.quiet?
|
||||||
rescue TapRemoteMismatchError => e
|
rescue TapRemoteMismatchError, TapNoCustomRemoteError => e
|
||||||
odie e
|
odie e
|
||||||
rescue TapAlreadyTappedError
|
rescue TapAlreadyTappedError
|
||||||
nil
|
nil
|
||||||
|
@ -324,13 +324,28 @@ class TapRemoteMismatchError < RuntimeError
|
|||||||
@expected_remote = expected_remote
|
@expected_remote = expected_remote
|
||||||
@actual_remote = actual_remote
|
@actual_remote = actual_remote
|
||||||
|
|
||||||
super <<~EOS
|
super message
|
||||||
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
<<~EOS
|
||||||
Tap #{name} remote mismatch.
|
Tap #{name} remote mismatch.
|
||||||
#{expected_remote} != #{actual_remote}
|
#{expected_remote} != #{actual_remote}
|
||||||
EOS
|
EOS
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Raised when the remote of Homebrew/core does not match HOMEBREW_CORE_GIT_REMOTE.
|
||||||
|
class TapCoreRemoteMismatchError < TapRemoteMismatchError
|
||||||
|
def message
|
||||||
|
<<~EOS
|
||||||
|
Tap #{name} remote does mot match HOMEBREW_CORE_GIT_REMOTE.
|
||||||
|
#{expected_remote} != #{actual_remote}
|
||||||
|
Please set HOMEBREW_CORE_GIT_REMOTE="#{actual_remote}" and run `brew update` instead.
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Raised when a tap is already installed.
|
# Raised when a tap is already installed.
|
||||||
class TapAlreadyTappedError < RuntimeError
|
class TapAlreadyTappedError < RuntimeError
|
||||||
attr_reader :name
|
attr_reader :name
|
||||||
@ -344,6 +359,19 @@ class TapAlreadyTappedError < RuntimeError
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Raised when run `brew tap --custom-remote` without a remote URL.
|
||||||
|
class TapNoCustomRemoteError < RuntimeError
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
def initialize(name)
|
||||||
|
@name = name
|
||||||
|
|
||||||
|
super <<~EOS
|
||||||
|
Tap #{name} with option `--custom-remote` but without a remote URL.
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Raised when another Homebrew operation is already in progress.
|
# Raised when another Homebrew operation is already in progress.
|
||||||
class OperationInProgressError < RuntimeError
|
class OperationInProgressError < RuntimeError
|
||||||
def initialize(name)
|
def initialize(name)
|
||||||
|
@ -246,7 +246,8 @@ class Tap
|
|||||||
# @param force_auto_update [Boolean, nil] If present, whether to override the
|
# @param force_auto_update [Boolean, nil] If present, whether to override the
|
||||||
# logic that skips non-GitHub repositories during auto-updates.
|
# logic that skips non-GitHub repositories during auto-updates.
|
||||||
# @param quiet [Boolean] If set, suppress all output.
|
# @param quiet [Boolean] If set, suppress all output.
|
||||||
def install(quiet: false, clone_target: nil, force_auto_update: nil)
|
# @param custom_remote [Boolean] If set, change the tap's remote if already installed.
|
||||||
|
def install(quiet: false, clone_target: nil, force_auto_update: nil, custom_remote: false)
|
||||||
require "descriptions"
|
require "descriptions"
|
||||||
require "readall"
|
require "readall"
|
||||||
|
|
||||||
@ -257,9 +258,11 @@ class Tap
|
|||||||
odie "#{name} was moved. Tap homebrew/#{new_repo} instead."
|
odie "#{name} was moved. Tap homebrew/#{new_repo} instead."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
raise TapNoCustomRemoteError, name if custom_remote && clone_target.nil?
|
||||||
|
|
||||||
requested_remote = clone_target || default_remote
|
requested_remote = clone_target || default_remote
|
||||||
|
|
||||||
if installed?
|
if installed? && !custom_remote
|
||||||
raise TapRemoteMismatchError.new(name, @remote, requested_remote) if clone_target && requested_remote != remote
|
raise TapRemoteMismatchError.new(name, @remote, requested_remote) if clone_target && requested_remote != remote
|
||||||
raise TapAlreadyTappedError, name if force_auto_update.nil? && !shallow?
|
raise TapAlreadyTappedError, name if force_auto_update.nil? && !shallow?
|
||||||
end
|
end
|
||||||
@ -268,6 +271,10 @@ class Tap
|
|||||||
Utils::Git.ensure_installed!
|
Utils::Git.ensure_installed!
|
||||||
|
|
||||||
if installed?
|
if installed?
|
||||||
|
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
|
||||||
|
|
||||||
unless force_auto_update.nil?
|
unless force_auto_update.nil?
|
||||||
config["forceautoupdate"] = force_auto_update
|
config["forceautoupdate"] = force_auto_update
|
||||||
return
|
return
|
||||||
@ -358,20 +365,33 @@ class Tap
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_remote_configuration
|
def fix_remote_configuration(requested_remote: nil, quiet: false)
|
||||||
return unless remote.include? "github.com"
|
if requested_remote.present?
|
||||||
|
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
|
||||||
|
|
||||||
current_upstream_head = path.git_origin_branch
|
current_upstream_head = path.git_origin_branch
|
||||||
return if path.git_origin_has_branch? current_upstream_head
|
return if requested_remote.blank? && path.git_origin_has_branch?(current_upstream_head)
|
||||||
|
|
||||||
safe_system "git", "-C", path, "fetch", "origin"
|
args = %w[fetch]
|
||||||
|
args << "--quiet" if quiet
|
||||||
|
args << "origin"
|
||||||
|
safe_system "git", "-C", path, *args
|
||||||
path.git_origin_set_head_auto
|
path.git_origin_set_head_auto
|
||||||
|
|
||||||
new_upstream_head = path.git_origin_branch
|
new_upstream_head = path.git_origin_branch
|
||||||
|
return if new_upstream_head == old_upstream_head
|
||||||
|
|
||||||
path.git_rename_branch old: current_upstream_head, new: new_upstream_head
|
path.git_rename_branch old: current_upstream_head, new: new_upstream_head
|
||||||
path.git_branch_set_upstream local: new_upstream_head, origin: new_upstream_head
|
path.git_branch_set_upstream local: new_upstream_head, origin: new_upstream_head
|
||||||
|
|
||||||
ohai "#{name}: changed default branch name from #{current_upstream_head} to #{new_upstream_head}!"
|
return if quiet
|
||||||
|
|
||||||
|
$stderr.ohai "#{name}: changed default branch name from #{current_upstream_head} to #{new_upstream_head}!"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Uninstall this {Tap}.
|
# Uninstall this {Tap}.
|
||||||
@ -741,12 +761,18 @@ class CoreTap < Tap
|
|||||||
end
|
end
|
||||||
|
|
||||||
# CoreTap never allows shallow clones (on request from GitHub).
|
# CoreTap never allows shallow clones (on request from GitHub).
|
||||||
def install(quiet: false, clone_target: nil, force_auto_update: nil)
|
def install(quiet: false, clone_target: nil, force_auto_update: nil, custom_remote: false)
|
||||||
remote = Homebrew::EnvConfig.core_git_remote
|
remote = Homebrew::EnvConfig.core_git_remote
|
||||||
|
requested_remote = clone_target || default_remote
|
||||||
|
|
||||||
|
# The remote will changed again on `brew update` since remotes for Homebrew/core are mismatched
|
||||||
|
raise TapCoreRemoteMismatchError.new(name, remote, requested_remote) if requested_remote != remote
|
||||||
|
|
||||||
if remote != default_remote
|
if remote != default_remote
|
||||||
$stderr.puts "HOMEBREW_CORE_GIT_REMOTE set: using #{remote} for Homebrew/core Git remote URL."
|
$stderr.puts "HOMEBREW_CORE_GIT_REMOTE set: using #{remote} for Homebrew/core Git remote."
|
||||||
end
|
end
|
||||||
super(quiet: quiet, clone_target: remote, force_auto_update: force_auto_update)
|
|
||||||
|
super(quiet: quiet, clone_target: remote, force_auto_update: force_auto_update, custom_remote: custom_remote)
|
||||||
end
|
end
|
||||||
|
|
||||||
# @private
|
# @private
|
||||||
|
@ -289,6 +289,33 @@ describe Tap do
|
|||||||
}.to raise_error(TapRemoteMismatchError)
|
}.to raise_error(TapRemoteMismatchError)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "raises an error when the remote for Homebrew/core doesn't match HOMEBREW_CORE_GIT_REMOTE" do
|
||||||
|
core_tap = described_class.fetch("Homebrew", "core")
|
||||||
|
wrong_remote = "#{Homebrew::EnvConfig.core_git_remote}-oops"
|
||||||
|
expect {
|
||||||
|
core_tap.install clone_target: wrong_remote
|
||||||
|
}.to raise_error(TapCoreRemoteMismatchError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when run `brew tap --custom-remote` without a custom remote (already installed)" do
|
||||||
|
setup_git_repo
|
||||||
|
already_tapped_tap = described_class.new("Homebrew", "foo")
|
||||||
|
expect(already_tapped_tap).to be_installed
|
||||||
|
|
||||||
|
expect {
|
||||||
|
already_tapped_tap.install clone_target: nil, custom_remote: true
|
||||||
|
}.to raise_error(TapNoCustomRemoteError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when run `brew tap --custom-remote` without a custom remote (not installed)" do
|
||||||
|
not_tapped_tap = described_class.new("Homebrew", "bar")
|
||||||
|
expect(not_tapped_tap).not_to be_installed
|
||||||
|
|
||||||
|
expect {
|
||||||
|
not_tapped_tap.install clone_target: nil, custom_remote: true
|
||||||
|
}.to raise_error(TapNoCustomRemoteError)
|
||||||
|
end
|
||||||
|
|
||||||
describe "force_auto_update" do
|
describe "force_auto_update" do
|
||||||
before do
|
before do
|
||||||
setup_git_repo
|
setup_git_repo
|
||||||
|
@ -1902,6 +1902,7 @@ _brew_tap() {
|
|||||||
case "${cur}" in
|
case "${cur}" in
|
||||||
-*)
|
-*)
|
||||||
__brewcomp "
|
__brewcomp "
|
||||||
|
--custom-remote
|
||||||
--debug
|
--debug
|
||||||
--force-auto-update
|
--force-auto-update
|
||||||
--full
|
--full
|
||||||
|
@ -1288,6 +1288,7 @@ __fish_brew_complete_arg 'style; and not __fish_seen_argument -l formula -l form
|
|||||||
|
|
||||||
|
|
||||||
__fish_brew_complete_cmd 'tap' 'Tap a formula repository'
|
__fish_brew_complete_cmd 'tap' 'Tap a formula repository'
|
||||||
|
__fish_brew_complete_arg 'tap' -l custom-remote -d 'Install or change a tap with a custom remote. Useful for mirrors'
|
||||||
__fish_brew_complete_arg 'tap' -l debug -d 'Display any debugging information'
|
__fish_brew_complete_arg 'tap' -l debug -d 'Display any debugging information'
|
||||||
__fish_brew_complete_arg 'tap' -l force-auto-update -d 'Auto-update tap even if it is not hosted on GitHub. By default, only taps hosted on GitHub are auto-updated (for performance reasons)'
|
__fish_brew_complete_arg 'tap' -l force-auto-update -d 'Auto-update tap even if it is not hosted on GitHub. By default, only taps hosted on GitHub are auto-updated (for performance reasons)'
|
||||||
__fish_brew_complete_arg 'tap' -l full -d 'Convert a shallow clone to a full clone without untapping. Taps are only cloned as shallow clones if `--shallow` was originally passed'
|
__fish_brew_complete_arg 'tap' -l full -d 'Convert a shallow clone to a full clone without untapping. Taps are only cloned as shallow clones if `--shallow` was originally passed'
|
||||||
|
@ -1579,6 +1579,7 @@ _brew_style() {
|
|||||||
# brew tap
|
# brew tap
|
||||||
_brew_tap() {
|
_brew_tap() {
|
||||||
_arguments \
|
_arguments \
|
||||||
|
'--custom-remote[Install or change a tap with a custom remote. Useful for mirrors]' \
|
||||||
'--debug[Display any debugging information]' \
|
'--debug[Display any debugging information]' \
|
||||||
'--force-auto-update[Auto-update tap even if it is not hosted on GitHub. By default, only taps hosted on GitHub are auto-updated (for performance reasons)]' \
|
'--force-auto-update[Auto-update tap even if it is not hosted on GitHub. By default, only taps hosted on GitHub are auto-updated (for performance reasons)]' \
|
||||||
'--full[Convert a shallow clone to a full clone without untapping. Taps are only cloned as shallow clones if `--shallow` was originally passed]' \
|
'--full[Convert a shallow clone to a full clone without untapping. Taps are only cloned as shallow clones if `--shallow` was originally passed]' \
|
||||||
|
@ -600,6 +600,8 @@ using protocols other than HTTPS, e.g. SSH, git, HTTP, FTP(S), rsync.
|
|||||||
Fetch tap as a shallow clone rather than a full clone. Useful for continuous integration.
|
Fetch tap as a shallow clone rather than a full clone. Useful for continuous integration.
|
||||||
* `--force-auto-update`:
|
* `--force-auto-update`:
|
||||||
Auto-update tap even if it is not hosted on GitHub. By default, only taps hosted on GitHub are auto-updated (for performance reasons).
|
Auto-update tap even if it is not hosted on GitHub. By default, only taps hosted on GitHub are auto-updated (for performance reasons).
|
||||||
|
* `--custom-remote`:
|
||||||
|
Install or change a tap with a custom remote. Useful for mirrors.
|
||||||
* `--repair`:
|
* `--repair`:
|
||||||
Migrate tapped formulae from symlink-based to directory-based structure.
|
Migrate tapped formulae from symlink-based to directory-based structure.
|
||||||
* `--list-pinned`:
|
* `--list-pinned`:
|
||||||
|
@ -831,6 +831,10 @@ Fetch tap as a shallow clone rather than a full clone\. Useful for continuous in
|
|||||||
Auto\-update tap even if it is not hosted on GitHub\. By default, only taps hosted on GitHub are auto\-updated (for performance reasons)\.
|
Auto\-update tap even if it is not hosted on GitHub\. By default, only taps hosted on GitHub are auto\-updated (for performance reasons)\.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-\-custom\-remote\fR
|
||||||
|
Install or change a tap with a custom remote\. Useful for mirrors\.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
\fB\-\-repair\fR
|
\fB\-\-repair\fR
|
||||||
Migrate tapped formulae from symlink\-based to directory\-based structure\.
|
Migrate tapped formulae from symlink\-based to directory\-based structure\.
|
||||||
.
|
.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user