diff --git a/Library/Homebrew/abstract_command.rb b/Library/Homebrew/abstract_command.rb index 7bdc7b3907..23be6cb3dd 100644 --- a/Library/Homebrew/abstract_command.rb +++ b/Library/Homebrew/abstract_command.rb @@ -61,12 +61,5 @@ module Homebrew sig { abstract.void } def run; end - - sig { void } - def raise_sh_command_error! - raise StandardError, - "This command is just here for completions generation. " \ - "It's actually defined in `cmd/#{self.class.command_name}.sh` instead." - end end end diff --git a/Library/Homebrew/cmd/--repository.rb b/Library/Homebrew/cmd/--repository.rb index a42d16fc52..c5ac522a13 100644 --- a/Library/Homebrew/cmd/--repository.rb +++ b/Library/Homebrew/cmd/--repository.rb @@ -2,10 +2,13 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class Repository < AbstractCommand + include ShellCommand + sig { override.returns(String) } def self.command_name = "--repository" @@ -18,9 +21,6 @@ module Homebrew named_args :tap end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/--version.rb b/Library/Homebrew/cmd/--version.rb index 7cd4770874..bcaca65d2c 100644 --- a/Library/Homebrew/cmd/--version.rb +++ b/Library/Homebrew/cmd/--version.rb @@ -2,10 +2,13 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class Version < AbstractCommand + include ShellCommand + sig { override.returns(String) } def self.command_name = "--version" @@ -15,9 +18,6 @@ module Homebrew Homebrew/homebrew-cask (if tapped) to standard output. EOS end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/casks.rb b/Library/Homebrew/cmd/casks.rb index ec9ebafe41..4bc6ace0e8 100644 --- a/Library/Homebrew/cmd/casks.rb +++ b/Library/Homebrew/cmd/casks.rb @@ -2,18 +2,18 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" # This Ruby command exists to allow generation of completions for the Bash # version. It is not meant to be run. module Homebrew module Cmd class Casks < AbstractCommand + include ShellCommand + cmd_args do description "List all locally installable casks including short names." end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/formulae.rb b/Library/Homebrew/cmd/formulae.rb index 52b3909d5c..58c41d034b 100644 --- a/Library/Homebrew/cmd/formulae.rb +++ b/Library/Homebrew/cmd/formulae.rb @@ -2,16 +2,16 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class Formulae < AbstractCommand + include ShellCommand + cmd_args do description "List all locally installable formulae including short names." end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/setup-ruby.rb b/Library/Homebrew/cmd/setup-ruby.rb index 0053439dc0..161d3e22ff 100644 --- a/Library/Homebrew/cmd/setup-ruby.rb +++ b/Library/Homebrew/cmd/setup-ruby.rb @@ -2,10 +2,13 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class SetupRuby < AbstractCommand + include ShellCommand + cmd_args do description <<~EOS Installs and configures Homebrew's Ruby. If `command` is passed, it will only run Bundler if necessary for that command. @@ -13,9 +16,6 @@ module Homebrew named_args :command end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/shellenv.rb b/Library/Homebrew/cmd/shellenv.rb index c3b9c00e32..61c7e354cc 100644 --- a/Library/Homebrew/cmd/shellenv.rb +++ b/Library/Homebrew/cmd/shellenv.rb @@ -2,10 +2,13 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class Shellenv < AbstractCommand + include ShellCommand + cmd_args do description <<~EOS Valid shells: bash|csh|fish|pwsh|sh|tcsh|zsh @@ -21,9 +24,6 @@ module Homebrew EOS named_args :shell end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/update-reset.rb b/Library/Homebrew/cmd/update-reset.rb index a433d8fa0d..ca304d5625 100644 --- a/Library/Homebrew/cmd/update-reset.rb +++ b/Library/Homebrew/cmd/update-reset.rb @@ -2,10 +2,13 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class UpdateReset < AbstractCommand + include ShellCommand + cmd_args do description <<~EOS Fetch and reset Homebrew and all tap repositories (or any specified ) using `git`(1) to their latest `origin/HEAD`. @@ -15,9 +18,6 @@ module Homebrew named_args :repository end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/update.rb b/Library/Homebrew/cmd/update.rb index fb486425e4..b36f7776c9 100644 --- a/Library/Homebrew/cmd/update.rb +++ b/Library/Homebrew/cmd/update.rb @@ -2,10 +2,13 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class Update < AbstractCommand + include ShellCommand + cmd_args do description <<~EOS Fetch the newest version of Homebrew and all formulae from GitHub using `git`(1) and perform any necessary migrations. @@ -23,9 +26,6 @@ module Homebrew switch "-d", "--debug", description: "Display a trace of all shell commands as they are executed." end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/cmd/vendor-install.rb b/Library/Homebrew/cmd/vendor-install.rb index cf22afb18f..5995dd7682 100644 --- a/Library/Homebrew/cmd/vendor-install.rb +++ b/Library/Homebrew/cmd/vendor-install.rb @@ -2,10 +2,13 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew module Cmd class VendorInstall < AbstractCommand + include ShellCommand + cmd_args do description <<~EOS Install Homebrew's portable Ruby. @@ -15,9 +18,6 @@ module Homebrew hide_from_man_page! end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/dev-cmd/rubocop.rb b/Library/Homebrew/dev-cmd/rubocop.rb index 1919994708..aa33151bb9 100644 --- a/Library/Homebrew/dev-cmd/rubocop.rb +++ b/Library/Homebrew/dev-cmd/rubocop.rb @@ -2,18 +2,18 @@ # frozen_string_literal: true require "abstract_command" +require "shell_command" module Homebrew - module Cmd + module DevCmd class Rubocop < AbstractCommand + include ShellCommand + cmd_args do description <<~EOS Installs, configures and runs Homebrew's `rubocop`. EOS end - - sig { override.void } - def run = raise_sh_command_error! end end end diff --git a/Library/Homebrew/rubocops/all.rb b/Library/Homebrew/rubocops/all.rb index b42f50665e..284d135bd5 100644 --- a/Library/Homebrew/rubocops/all.rb +++ b/Library/Homebrew/rubocops/all.rb @@ -11,6 +11,7 @@ require_relative "negate_include" require_relative "presence" require_relative "present" require_relative "safe_navigation_with_blank" +require_relative "shell_command_stub" require_relative "shell_commands" require_relative "install_bundler_gems" diff --git a/Library/Homebrew/rubocops/shell_command_stub.rb b/Library/Homebrew/rubocops/shell_command_stub.rb new file mode 100644 index 0000000000..34d1999905 --- /dev/null +++ b/Library/Homebrew/rubocops/shell_command_stub.rb @@ -0,0 +1,24 @@ +# typed: strict +# frozen_string_literal: true + +module RuboCop + module Cop + module Homebrew + class ShellCommandStub < Base + MSG = "Shell command stubs must have a `.sh` counterpart." + RESTRICT_ON_SEND = [:include].freeze + + sig { params(node: AST::SendNode).void } + def on_send(node) + return if node.first_argument&.const_name != "ShellCommand" + + stub_path = Pathname.new(processed_source.file_path) + sh_cmd_path = Pathname.new("#{stub_path.dirname}/#{stub_path.basename(".rb")}.sh") + return if sh_cmd_path.exist? + + add_offense(node) + end + end + end + end +end diff --git a/Library/Homebrew/shell_command.rb b/Library/Homebrew/shell_command.rb new file mode 100644 index 0000000000..905cd50b23 --- /dev/null +++ b/Library/Homebrew/shell_command.rb @@ -0,0 +1,20 @@ +# typed: strict +# frozen_string_literal: true + +module Homebrew + module ShellCommand + extend T::Helpers + + requires_ancestor { AbstractCommand } + + sig { void } + def run + T.bind(self, AbstractCommand) + + sh_cmd_path = "#{self.class.dev_cmd? ? "dev-cmd" : "cmd"}/#{self.class.command_name}.sh" + raise StandardError, + "This command is just here for completions generation. " \ + "It's actually defined in `#{sh_cmd_path}` instead." + end + end +end