# typed: false # frozen_string_literal: true require "completions" # Helper functions for commands. # # @api private module Commands module_function HOMEBREW_CMD_PATH = (HOMEBREW_LIBRARY_PATH/"cmd").freeze HOMEBREW_DEV_CMD_PATH = (HOMEBREW_LIBRARY_PATH/"dev-cmd").freeze HOMEBREW_INTERNAL_COMMAND_ALIASES = { "ls" => "list", "homepage" => "home", "-S" => "search", "up" => "update", "ln" => "link", "instal" => "install", # gem does the same "uninstal" => "uninstall", "rm" => "uninstall", "remove" => "uninstall", "abv" => "info", "dr" => "doctor", "--repo" => "--repository", "environment" => "--env", "--config" => "config", "-v" => "--version", "lc" => "livecheck", "tc" => "typecheck", }.freeze INSTALL_FROM_API_FORBIDDEN_COMMANDS = %w[ audit bottle bump-cask-pr bump-formula-pr bump-revision bump-unversioned-casks cat create edit extract formula livecheck pr-pull pr-upload test update-python-resources ].freeze def valid_internal_cmd?(cmd) require?(HOMEBREW_CMD_PATH/cmd) end def valid_internal_dev_cmd?(cmd) require?(HOMEBREW_DEV_CMD_PATH/cmd) end def method_name(cmd) cmd.to_s .tr("-", "_") .downcase .to_sym end def args_method_name(cmd_path) cmd_path_basename = basename_without_extension(cmd_path) cmd_method_prefix = method_name(cmd_path_basename) "#{cmd_method_prefix}_args".to_sym end def internal_cmd_path(cmd) [ HOMEBREW_CMD_PATH/"#{cmd}.rb", HOMEBREW_CMD_PATH/"#{cmd}.sh", ].find(&:exist?) end def internal_dev_cmd_path(cmd) [ HOMEBREW_DEV_CMD_PATH/"#{cmd}.rb", HOMEBREW_DEV_CMD_PATH/"#{cmd}.sh", ].find(&:exist?) end # Ruby commands which can be `require`d without being run. def external_ruby_v2_cmd_path(cmd) path = which("#{cmd}.rb", Tap.cmd_directories) path if require?(path) end # Ruby commands which are run by being `require`d. def external_ruby_cmd_path(cmd) which("brew-#{cmd}.rb", PATH.new(ENV["PATH"]).append(Tap.cmd_directories)) end def external_cmd_path(cmd) which("brew-#{cmd}", PATH.new(ENV["PATH"]).append(Tap.cmd_directories)) end def path(cmd) internal_cmd = HOMEBREW_INTERNAL_COMMAND_ALIASES.fetch(cmd, cmd) path ||= internal_cmd_path(internal_cmd) path ||= internal_dev_cmd_path(internal_cmd) path ||= external_ruby_v2_cmd_path(cmd) path ||= external_ruby_cmd_path(cmd) path ||= external_cmd_path(cmd) path end def commands(external: true, aliases: false) cmds = internal_commands cmds += internal_developer_commands cmds += external_commands if external cmds += internal_commands_aliases if aliases cmds.sort end def internal_commands_paths find_commands HOMEBREW_CMD_PATH end def internal_developer_commands_paths find_commands HOMEBREW_DEV_CMD_PATH end def official_external_commands_paths(quiet:) OFFICIAL_CMD_TAPS.flat_map do |tap_name, cmds| tap = Tap.fetch(tap_name) tap.install(quiet: quiet) unless tap.installed? cmds.map(&method(:external_ruby_v2_cmd_path)).compact end end def internal_commands find_internal_commands(HOMEBREW_CMD_PATH).map(&:to_s) end def internal_developer_commands find_internal_commands(HOMEBREW_DEV_CMD_PATH).map(&:to_s) end def internal_commands_aliases HOMEBREW_INTERNAL_COMMAND_ALIASES.keys end def find_internal_commands(path) find_commands(path).map(&:basename) .map(&method(:basename_without_extension)) end def external_commands Tap.cmd_directories.flat_map do |path| find_commands(path).select(&:executable?) .map(&method(:basename_without_extension)) .map { |p| p.to_s.delete_prefix("brew-").strip } end.map(&:to_s) .sort end def basename_without_extension(path) path.basename(path.extname) end def find_commands(path) Pathname.glob("#{path}/*") .select(&:file?) .sort end def rebuild_internal_commands_completion_list cmds = internal_commands + internal_developer_commands + internal_commands_aliases cmds.reject! { |cmd| Homebrew::Completions::COMPLETIONS_EXCLUSION_LIST.include? cmd } file = HOMEBREW_REPOSITORY/"completions/internal_commands_list.txt" file.atomic_write("#{cmds.sort.join("\n")}\n") end def rebuild_commands_completion_list # Ensure that the cache exists so we can build the commands list HOMEBREW_CACHE.mkpath cmds = commands(aliases: true) - Homebrew::Completions::COMPLETIONS_EXCLUSION_LIST all_commands_file = HOMEBREW_CACHE/"all_commands_list.txt" external_commands_file = HOMEBREW_CACHE/"external_commands_list.txt" all_commands_file.atomic_write("#{cmds.sort.join("\n")}\n") external_commands_file.atomic_write("#{external_commands.sort.join("\n")}\n") end def command_options(command) path = self.path(command) return if path.blank? if (cmd_parser = Homebrew::CLI::Parser.from_cmd_path(path)) cmd_parser.processed_options.map do |short, long, _, desc, hidden| next if hidden [long || short, desc] end.compact else options = [] comment_lines = path.read.lines.grep(/^#:/) return options if comment_lines.empty? # skip the comment's initial usage summary lines comment_lines.slice(2..-1).each do |line| if / (?