Skip to content

Commit

Permalink
Merge pull request #12221 from XuehaiPan/brew-retap
Browse files Browse the repository at this point in the history
tap: allow to change tap remote with `brew tap --custom-remote`
  • Loading branch information
MikeMcQuaid committed Oct 12, 2021
2 parents bddcc08 + 2be19d8 commit cc2c19b
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 12 deletions.
3 changes: 3 additions & 0 deletions Library/Homebrew/cli/args.rbi
Expand Up @@ -293,6 +293,9 @@ module Homebrew

sig { returns(T::Boolean) }
def write_only?; end

sig { returns(T::Boolean) }
def custom_remote?; end
end
end
end
5 changes: 4 additions & 1 deletion Library/Homebrew/cmd/tap.rb
Expand Up @@ -35,6 +35,8 @@ def tap_args
switch "--force-auto-update",
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)."
switch "--custom-remote",
description: "Install or change a tap with a custom remote. Useful for mirrors."
switch "--repair",
description: "Migrate tapped formulae from symlink-based to directory-based structure."
switch "--list-pinned",
Expand Down Expand Up @@ -64,8 +66,9 @@ def tap
begin
tap.install clone_target: args.named.second,
force_auto_update: force_auto_update?(args: args),
custom_remote: args.custom_remote?,
quiet: args.quiet?
rescue TapRemoteMismatchError => e
rescue TapRemoteMismatchError, TapNoCustomRemoteError => e
odie e
rescue TapAlreadyTappedError
nil
Expand Down
30 changes: 29 additions & 1 deletion Library/Homebrew/exceptions.rb
Expand Up @@ -324,13 +324,28 @@ def initialize(name, expected_remote, actual_remote)
@expected_remote = expected_remote
@actual_remote = actual_remote

super <<~EOS
super message
end

def message
<<~EOS
Tap #{name} remote mismatch.
#{expected_remote} != #{actual_remote}
EOS
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.
class TapAlreadyTappedError < RuntimeError
attr_reader :name
Expand All @@ -344,6 +359,19 @@ def initialize(name)
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.
class OperationInProgressError < RuntimeError
def initialize(name)
Expand Down
46 changes: 36 additions & 10 deletions Library/Homebrew/tap.rb
Expand Up @@ -246,7 +246,8 @@ def core_tap?
# @param force_auto_update [Boolean, nil] If present, whether to override the
# logic that skips non-GitHub repositories during auto-updates.
# @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 "readall"

Expand All @@ -257,9 +258,11 @@ def install(quiet: false, clone_target: nil, force_auto_update: nil)
odie "#{name} was moved. Tap homebrew/#{new_repo} instead."
end

raise TapNoCustomRemoteError, name if custom_remote && clone_target.nil?

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 TapAlreadyTappedError, name if force_auto_update.nil? && !shallow?
end
Expand All @@ -268,6 +271,10 @@ def install(quiet: false, clone_target: nil, force_auto_update: nil)
Utils::Git.ensure_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?
config["forceautoupdate"] = force_auto_update
return
Expand Down Expand Up @@ -358,20 +365,33 @@ def link_completions_and_manpages
end
end

def fix_remote_configuration
return unless remote.include? "github.com"
def fix_remote_configuration(requested_remote: nil, quiet: false)
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
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

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_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

# Uninstall this {Tap}.
Expand Down Expand Up @@ -741,12 +761,18 @@ def self.ensure_installed!
end

# 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
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
$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
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

# @private
Expand Down
27 changes: 27 additions & 0 deletions Library/Homebrew/test/tap_spec.rb
Expand Up @@ -289,6 +289,33 @@ def setup_completion(link:)
}.to raise_error(TapRemoteMismatchError)
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
before do
setup_git_repo
Expand Down
1 change: 1 addition & 0 deletions completions/bash/brew
Expand Up @@ -1902,6 +1902,7 @@ _brew_tap() {
case "${cur}" in
-*)
__brewcomp "
--custom-remote
--debug
--force-auto-update
--full
Expand Down
1 change: 1 addition & 0 deletions completions/fish/brew.fish
Expand Up @@ -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_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 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'
Expand Down
1 change: 1 addition & 0 deletions completions/zsh/_brew
Expand Up @@ -1579,6 +1579,7 @@ _brew_style() {
# brew tap
_brew_tap() {
_arguments \
'--custom-remote[Install or change a tap with a custom remote. Useful for mirrors]' \
'--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)]' \
'--full[Convert a shallow clone to a full clone without untapping. Taps are only cloned as shallow clones if `--shallow` was originally passed]' \
Expand Down
2 changes: 2 additions & 0 deletions docs/Manpage.md
Expand Up @@ -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.
* `--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).
* `--custom-remote`:
Install or change a tap with a custom remote. Useful for mirrors.
* `--repair`:
Migrate tapped formulae from symlink-based to directory-based structure.
* `--list-pinned`:
Expand Down
4 changes: 4 additions & 0 deletions manpages/brew.1
Expand Up @@ -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)\.
.
.TP
\fB\-\-custom\-remote\fR
Install or change a tap with a custom remote\. Useful for mirrors\.
.
.TP
\fB\-\-repair\fR
Migrate tapped formulae from symlink\-based to directory\-based structure\.
.
Expand Down

0 comments on commit cc2c19b

Please sign in to comment.