mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Merge pull request #19258 from Homebrew/brew_alias_import
Import `brew alias` and `brew unalias` commands
This commit is contained in:
commit
5ae29d324d
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@ -113,7 +113,6 @@ jobs:
|
||||
|
||||
- name: Set up all Homebrew taps
|
||||
run: |
|
||||
brew tap homebrew/aliases
|
||||
brew tap homebrew/bundle
|
||||
brew tap homebrew/command-not-found
|
||||
brew tap homebrew/formula-analytics
|
||||
@ -129,8 +128,7 @@ jobs:
|
||||
homebrew/services \
|
||||
homebrew/test-bot
|
||||
|
||||
brew style homebrew/aliases \
|
||||
homebrew/command-not-found \
|
||||
brew style homebrew/command-not-found \
|
||||
homebrew/formula-analytics \
|
||||
homebrew/portable-ruby
|
||||
|
||||
|
113
Library/Homebrew/aliases/alias.rb
Normal file
113
Library/Homebrew/aliases/alias.rb
Normal file
@ -0,0 +1,113 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Homebrew
|
||||
module Aliases
|
||||
class Alias
|
||||
sig { returns(String) }
|
||||
attr_accessor :name
|
||||
|
||||
sig { returns(T.nilable(String)) }
|
||||
attr_accessor :command
|
||||
|
||||
sig { params(name: String, command: T.nilable(String)).void }
|
||||
def initialize(name, command = nil)
|
||||
@name = T.let(name.strip, String)
|
||||
@command = T.let(nil, T.nilable(String))
|
||||
@script = T.let(nil, T.nilable(Pathname))
|
||||
@symlink = T.let(nil, T.nilable(Pathname))
|
||||
|
||||
@command = if command&.start_with?("!", "%")
|
||||
command[1..]
|
||||
elsif command
|
||||
"brew #{command}"
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def reserved?
|
||||
RESERVED.include? name
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def cmd_exists?
|
||||
path = which("brew-#{name}.rb") || which("brew-#{name}")
|
||||
!path.nil? && path.realpath.parent != HOMEBREW_ALIASES
|
||||
end
|
||||
|
||||
sig { returns(Pathname) }
|
||||
def script
|
||||
@script ||= Pathname.new("#{HOMEBREW_ALIASES}/#{name.gsub(/\W/, "_")}")
|
||||
end
|
||||
|
||||
sig { returns(Pathname) }
|
||||
def symlink
|
||||
@symlink ||= Pathname.new("#{HOMEBREW_PREFIX}/bin/brew-#{name}")
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def valid_symlink?
|
||||
symlink.realpath.parent == HOMEBREW_ALIASES.realpath
|
||||
rescue NameError
|
||||
false
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def link
|
||||
FileUtils.rm symlink if File.symlink? symlink
|
||||
FileUtils.ln_s script, symlink
|
||||
end
|
||||
|
||||
sig { params(opts: T::Hash[Symbol, T::Boolean]).void }
|
||||
def write(opts = {})
|
||||
odie "'#{name}' is a reserved command. Sorry." if reserved?
|
||||
odie "'brew #{name}' already exists. Sorry." if cmd_exists?
|
||||
|
||||
return if !opts[:override] && script.exist?
|
||||
|
||||
content = if command
|
||||
<<~EOS
|
||||
#: * `#{name}` [args...]
|
||||
#: `brew #{name}` is an alias for `#{command}`
|
||||
#{command} $*
|
||||
EOS
|
||||
else
|
||||
<<~EOS
|
||||
#
|
||||
# This is a Homebrew alias script. It'll be called when the user
|
||||
# types `brew #{name}`. Any remaining arguments are passed to
|
||||
# this script. You can retrieve those with $*, or only the first
|
||||
# one with $1. Please keep your script on one line.
|
||||
|
||||
# TODO Replace the line below with your script
|
||||
echo "Hello I'm brew alias "#{name}" and my args are:" $1
|
||||
EOS
|
||||
end
|
||||
|
||||
script.open("w") do |f|
|
||||
f.write <<~EOS
|
||||
#! #{`which bash`.chomp}
|
||||
# alias: brew #{name}
|
||||
#{content}
|
||||
EOS
|
||||
end
|
||||
script.chmod 0744
|
||||
link
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def remove
|
||||
odie "'brew #{name}' is not aliased to anything." if !symlink.exist? || !valid_symlink?
|
||||
|
||||
script.unlink
|
||||
symlink.unlink
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def edit
|
||||
write(override: false)
|
||||
exec_editor script.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
77
Library/Homebrew/aliases/aliases.rb
Normal file
77
Library/Homebrew/aliases/aliases.rb
Normal file
@ -0,0 +1,77 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "aliases/alias"
|
||||
|
||||
module Homebrew
|
||||
module Aliases
|
||||
RESERVED = T.let((
|
||||
Commands.internal_commands +
|
||||
Commands.internal_developer_commands +
|
||||
Commands.internal_commands_aliases +
|
||||
%w[alias unalias]
|
||||
).freeze, T::Array[String])
|
||||
|
||||
sig { void }
|
||||
def self.init
|
||||
FileUtils.mkdir_p HOMEBREW_ALIASES
|
||||
end
|
||||
|
||||
sig { params(name: String, command: String).void }
|
||||
def self.add(name, command)
|
||||
new_alias = Alias.new(name, command)
|
||||
odie "alias 'brew #{name}' already exists!" if new_alias.script.exist?
|
||||
new_alias.write
|
||||
end
|
||||
|
||||
sig { params(name: String).void }
|
||||
def self.remove(name)
|
||||
Alias.new(name).remove
|
||||
end
|
||||
|
||||
sig { params(only: T::Array[String], block: T.proc.params(target: String, cmd: String).void).void }
|
||||
def self.each(only, &block)
|
||||
Dir["#{HOMEBREW_ALIASES}/*"].each do |path|
|
||||
next if path.end_with? "~" # skip Emacs-like backup files
|
||||
next if File.directory?(path)
|
||||
|
||||
_shebang, _meta, *lines = File.readlines(path)
|
||||
target = File.basename(path)
|
||||
next if !only.empty? && only.exclude?(target)
|
||||
|
||||
lines.reject! { |line| line.start_with?("#") || line =~ /^\s*$/ }
|
||||
first_line = T.must(lines.first)
|
||||
cmd = first_line.chomp
|
||||
cmd.sub!(/ \$\*$/, "")
|
||||
|
||||
if cmd.start_with? "brew "
|
||||
cmd.sub!(/^brew /, "")
|
||||
else
|
||||
cmd = "!#{cmd}"
|
||||
end
|
||||
|
||||
yield target, cmd if block.present?
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(aliases: String).void }
|
||||
def self.show(*aliases)
|
||||
each([*aliases]) do |target, cmd|
|
||||
puts "brew alias #{target}='#{cmd}'"
|
||||
existing_alias = Alias.new(target, cmd)
|
||||
existing_alias.link unless existing_alias.symlink.exist?
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(name: String, command: T.nilable(String)).void }
|
||||
def self.edit(name, command = nil)
|
||||
Alias.new(name, command).write unless command.nil?
|
||||
Alias.new(name, command).edit
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def self.edit_all
|
||||
exec_editor(*Dir[HOMEBREW_ALIASES])
|
||||
end
|
||||
end
|
||||
end
|
47
Library/Homebrew/cmd/alias.rb
Executable file
47
Library/Homebrew/cmd/alias.rb
Executable file
@ -0,0 +1,47 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "aliases/aliases"
|
||||
|
||||
module Homebrew
|
||||
module Cmd
|
||||
class Alias < AbstractCommand
|
||||
cmd_args do
|
||||
usage_banner "`alias` [<alias> ... | <alias>=<command>]"
|
||||
description <<~EOS
|
||||
Show existing aliases. If no aliases are given, print the whole list.
|
||||
EOS
|
||||
switch "--edit",
|
||||
description: "Edit aliases in a text editor. Either one or all aliases may be opened at once. " \
|
||||
"If the given alias doesn't exist it'll be pre-populated with a template."
|
||||
named_args max: 1
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
name = args.named.first
|
||||
name, command = name.split("=", 2) if name.present?
|
||||
|
||||
Aliases.init
|
||||
|
||||
if name.nil?
|
||||
if args.edit?
|
||||
Aliases.edit_all
|
||||
else
|
||||
Aliases.show
|
||||
end
|
||||
elsif command.nil?
|
||||
if args.edit?
|
||||
Aliases.edit name
|
||||
else
|
||||
Aliases.show name
|
||||
end
|
||||
else
|
||||
Aliases.add name, command
|
||||
Aliases.edit name if args.edit?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
24
Library/Homebrew/cmd/unalias.rb
Executable file
24
Library/Homebrew/cmd/unalias.rb
Executable file
@ -0,0 +1,24 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_command"
|
||||
require "aliases/aliases"
|
||||
|
||||
module Homebrew
|
||||
module Cmd
|
||||
class Unalias < AbstractCommand
|
||||
cmd_args do
|
||||
description <<~EOS
|
||||
Remove aliases.
|
||||
EOS
|
||||
named_args :alias, min: 1
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
Aliases.init
|
||||
args.named.each { |a| Aliases.remove a }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -6,7 +6,6 @@ OFFICIAL_CASK_TAPS = %w[
|
||||
].freeze
|
||||
|
||||
OFFICIAL_CMD_TAPS = T.let({
|
||||
"homebrew/aliases" => ["alias", "unalias"],
|
||||
"homebrew/bundle" => ["bundle"],
|
||||
"homebrew/command-not-found" => ["command-not-found-init", "which-formula", "which-update"],
|
||||
"homebrew/test-bot" => ["test-bot"],
|
||||
@ -14,6 +13,7 @@ OFFICIAL_CMD_TAPS = T.let({
|
||||
}.freeze, T::Hash[String, T::Array[String]])
|
||||
|
||||
DEPRECATED_OFFICIAL_TAPS = %w[
|
||||
aliases
|
||||
apache
|
||||
binary
|
||||
cask-drivers
|
||||
|
16
Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/alias.rbi
Normal file
16
Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/alias.rbi
Normal file
@ -0,0 +1,16 @@
|
||||
# typed: true
|
||||
|
||||
# DO NOT EDIT MANUALLY
|
||||
# This is an autogenerated file for dynamic methods in `Homebrew::Cmd::Alias`.
|
||||
# Please instead update this file by running `bin/tapioca dsl Homebrew::Cmd::Alias`.
|
||||
|
||||
|
||||
class Homebrew::Cmd::Alias
|
||||
sig { returns(Homebrew::Cmd::Alias::Args) }
|
||||
def args; end
|
||||
end
|
||||
|
||||
class Homebrew::Cmd::Alias::Args < Homebrew::CLI::Args
|
||||
sig { returns(T::Boolean) }
|
||||
def edit?; end
|
||||
end
|
13
Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/unalias.rbi
Normal file
13
Library/Homebrew/sorbet/rbi/dsl/homebrew/cmd/unalias.rbi
Normal file
@ -0,0 +1,13 @@
|
||||
# typed: true
|
||||
|
||||
# DO NOT EDIT MANUALLY
|
||||
# This is an autogenerated file for dynamic methods in `Homebrew::Cmd::Unalias`.
|
||||
# Please instead update this file by running `bin/tapioca dsl Homebrew::Cmd::Unalias`.
|
||||
|
||||
|
||||
class Homebrew::Cmd::Unalias
|
||||
sig { returns(Homebrew::Cmd::Unalias::Args) }
|
||||
def args; end
|
||||
end
|
||||
|
||||
class Homebrew::Cmd::Unalias::Args < Homebrew::CLI::Args; end
|
@ -65,3 +65,15 @@ HOMEBREW_RUBY_EXEC_ARGS = [
|
||||
ENV.fetch("HOMEBREW_RUBY_WARNINGS"),
|
||||
ENV.fetch("HOMEBREW_RUBY_DISABLE_OPTIONS"),
|
||||
].freeze
|
||||
|
||||
# Location for `brew alias` and `brew unalias` commands.
|
||||
#
|
||||
# Unix-Like systems store config in $HOME/.config whose location can be
|
||||
# overridden by the XDG_CONFIG_HOME environment variable. Unfortunately
|
||||
# Homebrew strictly filters environment variables in BuildEnvironment.
|
||||
HOMEBREW_ALIASES = if (path = Pathname.new("~/.config/brew-aliases").expand_path).exist? ||
|
||||
(path = Pathname.new("~/.brew-aliases").expand_path).exist?
|
||||
path.realpath
|
||||
else
|
||||
path
|
||||
end.freeze
|
||||
|
6
Library/Homebrew/test/.brew-aliases/foo
Executable file
6
Library/Homebrew/test/.brew-aliases/foo
Executable file
@ -0,0 +1,6 @@
|
||||
#! /bin/bash
|
||||
# alias: brew foo
|
||||
#: * `foo` [args...]
|
||||
#: `brew foo` is an alias for `brew bar`
|
||||
brew bar $*
|
||||
|
19
Library/Homebrew/test/cmd/alias_spec.rb
Normal file
19
Library/Homebrew/test/cmd/alias_spec.rb
Normal file
@ -0,0 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/alias"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe Homebrew::Cmd::Alias do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "sets an alias", :integration_test do
|
||||
expect { brew "alias", "foo=bar" }
|
||||
.to not_to_output.to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
expect { brew "alias" }
|
||||
.to output(/brew alias foo='bar'/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
end
|
27
Library/Homebrew/test/cmd/unalias_spec.rb
Normal file
27
Library/Homebrew/test/cmd/unalias_spec.rb
Normal file
@ -0,0 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "cmd/unalias"
|
||||
require "cmd/shared_examples/args_parse"
|
||||
|
||||
RSpec.describe Homebrew::Cmd::Unalias do
|
||||
it_behaves_like "parseable arguments"
|
||||
|
||||
it "unsets an alias", :integration_test do
|
||||
expect { brew "alias", "foo=bar" }
|
||||
.to not_to_output.to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
expect { brew "alias" }
|
||||
.to output(/brew alias foo='bar'/).to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
expect { brew "unalias", "foo" }
|
||||
.to not_to_output.to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
expect { brew "alias" }
|
||||
.to not_to_output.to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
end
|
@ -63,6 +63,7 @@ TEST_DIRECTORIES = [
|
||||
HOMEBREW_LOCKS,
|
||||
HOMEBREW_LOGS,
|
||||
HOMEBREW_TEMP,
|
||||
HOMEBREW_ALIASES,
|
||||
].freeze
|
||||
|
||||
# Make `instance_double` and `class_double`
|
||||
|
@ -24,6 +24,7 @@ HOMEBREW_DATA_PATH = (HOMEBREW_LIBRARY_PATH/"data").freeze
|
||||
|
||||
# Paths redirected to a temporary directory and wiped at the end of the test run
|
||||
HOMEBREW_PREFIX = (Pathname(TEST_TMPDIR)/"prefix").freeze
|
||||
HOMEBREW_ALIASES = (Pathname(TEST_TMPDIR)/"aliases").freeze
|
||||
HOMEBREW_REPOSITORY = HOMEBREW_PREFIX.dup.freeze
|
||||
HOMEBREW_LIBRARY = (HOMEBREW_REPOSITORY/"Library").freeze
|
||||
HOMEBREW_CACHE = (HOMEBREW_PREFIX.parent/"cache").freeze
|
||||
|
@ -50,7 +50,7 @@ Unlike formulae, casks must have globally unique names to avoid clashes. This ca
|
||||
|
||||
You can provide your tap users with custom `brew` commands by adding them in a `cmd` subdirectory. [Read more on external commands](External-Commands.md).
|
||||
|
||||
See [homebrew/aliases](https://github.com/Homebrew/homebrew-aliases) for an example of a tap with external commands.
|
||||
See [Homebrew/test-bot](https://github.com/Homebrew/homebrew-test-bot) for an example of a tap with external commands.
|
||||
|
||||
## Upstream taps
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user