diff --git a/Library/Homebrew/abstract_command.rb b/Library/Homebrew/abstract_command.rb index e7a5713755..95b086f022 100644 --- a/Library/Homebrew/abstract_command.rb +++ b/Library/Homebrew/abstract_command.rb @@ -17,7 +17,7 @@ module Homebrew class << self sig { returns(String) } - def command_name = Utils.underscore(T.must(name).split("::").fetch(-1)).tr("_", "-") + def command_name = Utils.underscore(T.must(name).split("::").fetch(-1)).tr("_", "-").delete_suffix("-cmd") # @return the AbstractCommand subclass associated with the brew CLI command name. sig { params(name: String).returns(T.nilable(T.class_of(AbstractCommand))) } diff --git a/Library/Homebrew/dev-cmd/contributions.rb b/Library/Homebrew/dev-cmd/contributions.rb index 22fd4fdad6..1e0359a24e 100644 --- a/Library/Homebrew/dev-cmd/contributions.rb +++ b/Library/Homebrew/dev-cmd/contributions.rb @@ -1,6 +1,7 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "csv" diff --git a/Library/Homebrew/dev-cmd/create.rb b/Library/Homebrew/dev-cmd/create.rb index f6c2eea06f..75b8a79ac1 100644 --- a/Library/Homebrew/dev-cmd/create.rb +++ b/Library/Homebrew/dev-cmd/create.rb @@ -1,4 +1,4 @@ -# typed: true +# typed: strict # frozen_string_literal: true require "formula" @@ -9,232 +9,231 @@ require "utils/pypi" require "cask/cask_loader" module Homebrew - module_function - - sig { returns(CLI::Parser) } - def create_args - Homebrew::CLI::Parser.new do - description <<~EOS - Generate a formula or, with `--cask`, a cask for the downloadable file at - and open it in the editor. Homebrew will attempt to automatically derive the - formula name and version, but if it fails, you'll have to make your own template. - The `wget` formula serves as a simple example. For the complete API, see: - - EOS - switch "--autotools", - description: "Create a basic template for an Autotools-style build." - switch "--cask", - description: "Create a basic template for a cask." - switch "--cmake", - description: "Create a basic template for a CMake-style build." - switch "--crystal", - description: "Create a basic template for a Crystal build." - switch "--go", - description: "Create a basic template for a Go build." - switch "--meson", - description: "Create a basic template for a Meson-style build." - switch "--node", - description: "Create a basic template for a Node build." - switch "--perl", - description: "Create a basic template for a Perl build." - switch "--python", - description: "Create a basic template for a Python build." - switch "--ruby", - description: "Create a basic template for a Ruby build." - switch "--rust", - description: "Create a basic template for a Rust build." - switch "--no-fetch", - description: "Homebrew will not download to the cache and will thus not add its SHA-256 " \ - "to the formula for you, nor will it check the GitHub API for GitHub projects " \ - "(to fill out its description and homepage)." - switch "--HEAD", - description: "Indicate that points to the package's repository rather than a file." - flag "--set-name=", - description: "Explicitly set the of the new formula or cask." - flag "--set-version=", - description: "Explicitly set the of the new formula or cask." - flag "--set-license=", - description: "Explicitly set the of the new formula." - flag "--tap=", - description: "Generate the new formula within the given tap, specified as `/`." - switch "-f", "--force", - description: "Ignore errors for disallowed formula names and names that shadow aliases." - - conflicts "--autotools", "--cmake", "--crystal", "--go", "--meson", "--node", - "--perl", "--python", "--ruby", "--rust", "--cask" - conflicts "--cask", "--HEAD" - conflicts "--cask", "--set-license" - - named_args :url, number: 1 - end - end - - # Create a formula from a tarball URL. - sig { void } - def create - args = create_args.parse - - path = if args.cask? - create_cask(args:) - else - create_formula(args:) - end - - exec_editor path - end - - sig { params(args: CLI::Args).returns(Pathname) } - def create_cask(args:) - url = args.named.first - name = if args.set_name.blank? - stem = Pathname.new(url).stem.rpartition("=").last - print "Cask name [#{stem}]: " - __gets || stem - else - args.set_name - end - token = Cask::Utils.token_from(T.must(name)) - - cask_tap = Tap.fetch(args.tap || "homebrew/cask") - raise TapUnavailableError, cask_tap.name unless cask_tap.installed? - - cask_path = cask_tap.new_cask_path(token) - cask_path.dirname.mkpath unless cask_path.dirname.exist? - raise Cask::CaskAlreadyCreatedError, token if cask_path.exist? - - version = if args.set_version - Version.new(T.must(args.set_version)) - else - Version.detect(url.gsub(token, "").gsub(/x86(_64)?/, "")) - end - - interpolated_url, sha256 = if version.null? - [url, ""] - else - sha256 = if args.no_fetch? - "" - else - strategy = DownloadStrategyDetector.detect(url) - downloader = strategy.new(url, token, version.to_s, cache: Cask::Cache.path) - downloader.fetch - downloader.cached_location.sha256 - end - - [url.gsub(version.to_s, "\#{version}"), sha256] - end - - cask_path.atomic_write <<~RUBY - # Documentation: https://docs.brew.sh/Cask-Cookbook - # https://docs.brew.sh/Adding-Software-to-Homebrew#cask-stanzas - # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST! - cask "#{token}" do - version "#{version}" - sha256 "#{sha256}" - - url "#{interpolated_url}" - name "#{name}" - desc "" - homepage "" - - # Documentation: https://docs.brew.sh/Brew-Livecheck - livecheck do - url "" - strategy "" - end - - depends_on macos: "" - - app "" - - # Documentation: https://docs.brew.sh/Cask-Cookbook#stanza-zap - zap trash: "" - end - RUBY - - puts "Please run `brew audit --cask --new #{token}` before submitting, thanks." - cask_path - end - - sig { params(args: CLI::Args).returns(Pathname) } - def create_formula(args:) - mode = if args.autotools? - :autotools - elsif args.cmake? - :cmake - elsif args.crystal? - :crystal - elsif args.go? - :go - elsif args.meson? - :meson - elsif args.node? - :node - elsif args.perl? - :perl - elsif args.python? - :python - elsif args.ruby? - :ruby - elsif args.rust? - :rust - end - - fc = FormulaCreator.new( - args.set_name, - args.set_version, - tap: args.tap, - url: args.named.first, - mode:, - license: args.set_license, - fetch: !args.no_fetch?, - head: args.HEAD?, - ) - fc.parse_url - # ask for confirmation if name wasn't passed explicitly - if args.set_name.blank? - print "Formula name [#{fc.name}]: " - fc.name = __gets || fc.name - end - - fc.verify - - # Check for disallowed formula, or names that shadow aliases, - # unless --force is specified. - unless args.force? - if (reason = MissingFormula.disallowed_reason(fc.name)) - odie <<~EOS - The formula '#{fc.name}' is not allowed to be created. - #{reason} - If you really want to create this formula use `--force`. + module DevCmd + class Create < AbstractCommand + cmd_args do + description <<~EOS + Generate a formula or, with `--cask`, a cask for the downloadable file at + and open it in the editor. Homebrew will attempt to automatically derive the + formula name and version, but if it fails, you'll have to make your own template. + The `wget` formula serves as a simple example. For the complete API, see: + EOS + switch "--autotools", + description: "Create a basic template for an Autotools-style build." + switch "--cask", + description: "Create a basic template for a cask." + switch "--cmake", + description: "Create a basic template for a CMake-style build." + switch "--crystal", + description: "Create a basic template for a Crystal build." + switch "--go", + description: "Create a basic template for a Go build." + switch "--meson", + description: "Create a basic template for a Meson-style build." + switch "--node", + description: "Create a basic template for a Node build." + switch "--perl", + description: "Create a basic template for a Perl build." + switch "--python", + description: "Create a basic template for a Python build." + switch "--ruby", + description: "Create a basic template for a Ruby build." + switch "--rust", + description: "Create a basic template for a Rust build." + switch "--no-fetch", + description: "Homebrew will not download to the cache and will thus not add its SHA-256 " \ + "to the formula for you, nor will it check the GitHub API for GitHub projects " \ + "(to fill out its description and homepage)." + switch "--HEAD", + description: "Indicate that points to the package's repository rather than a file." + flag "--set-name=", + description: "Explicitly set the of the new formula or cask." + flag "--set-version=", + description: "Explicitly set the of the new formula or cask." + flag "--set-license=", + description: "Explicitly set the of the new formula." + flag "--tap=", + description: "Generate the new formula within the given tap, specified as `/`." + switch "-f", "--force", + description: "Ignore errors for disallowed formula names and names that shadow aliases." + + conflicts "--autotools", "--cmake", "--crystal", "--go", "--meson", "--node", + "--perl", "--python", "--ruby", "--rust", "--cask" + conflicts "--cask", "--HEAD" + conflicts "--cask", "--set-license" + + named_args :url, number: 1 end - Homebrew.with_no_api_env do - if Formula.aliases.include? fc.name - realname = Formulary.canonical_name(fc.name) - odie <<~EOS - The formula '#{realname}' is already aliased to '#{fc.name}'. - Please check that you are not creating a duplicate. - To force creation use `--force`. - EOS + # Create a formula from a tarball URL. + sig { override.void } + def run + path = if args.cask? + create_cask + else + create_formula end + + exec_editor path + end + + private + + sig { returns(Pathname) } + def create_cask + url = args.named.first + name = if args.set_name.blank? + stem = Pathname.new(url).stem.rpartition("=").last + print "Cask name [#{stem}]: " + __gets || stem + else + args.set_name + end + token = Cask::Utils.token_from(T.must(name)) + + cask_tap = Tap.fetch(args.tap || "homebrew/cask") + raise TapUnavailableError, cask_tap.name unless cask_tap.installed? + + cask_path = cask_tap.new_cask_path(token) + cask_path.dirname.mkpath unless cask_path.dirname.exist? + raise Cask::CaskAlreadyCreatedError, token if cask_path.exist? + + version = if args.set_version + Version.new(T.must(args.set_version)) + else + Version.detect(url.gsub(token, "").gsub(/x86(_64)?/, "")) + end + + interpolated_url, sha256 = if version.null? + [url, ""] + else + sha256 = if args.no_fetch? + "" + else + strategy = DownloadStrategyDetector.detect(url) + downloader = strategy.new(url, token, version.to_s, cache: Cask::Cache.path) + downloader.fetch + downloader.cached_location.sha256 + end + + [url.gsub(version.to_s, "\#{version}"), sha256] + end + + cask_path.atomic_write <<~RUBY + # Documentation: https://docs.brew.sh/Cask-Cookbook + # https://docs.brew.sh/Adding-Software-to-Homebrew#cask-stanzas + # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST! + cask "#{token}" do + version "#{version}" + sha256 "#{sha256}" + + url "#{interpolated_url}" + name "#{name}" + desc "" + homepage "" + + # Documentation: https://docs.brew.sh/Brew-Livecheck + livecheck do + url "" + strategy "" + end + + depends_on macos: "" + + app "" + + # Documentation: https://docs.brew.sh/Cask-Cookbook#stanza-zap + zap trash: "" + end + RUBY + + puts "Please run `brew audit --cask --new #{token}` before submitting, thanks." + cask_path + end + + sig { returns(Pathname) } + def create_formula + mode = if args.autotools? + :autotools + elsif args.cmake? + :cmake + elsif args.crystal? + :crystal + elsif args.go? + :go + elsif args.meson? + :meson + elsif args.node? + :node + elsif args.perl? + :perl + elsif args.python? + :python + elsif args.ruby? + :ruby + elsif args.rust? + :rust + end + + fc = FormulaCreator.new( + args.set_name, + args.set_version, + tap: args.tap, + url: args.named.first, + mode:, + license: args.set_license, + fetch: !args.no_fetch?, + head: args.HEAD?, + ) + fc.parse_url + # ask for confirmation if name wasn't passed explicitly + if args.set_name.blank? + print "Formula name [#{fc.name}]: " + fc.name = __gets || fc.name + end + + fc.verify + + # Check for disallowed formula, or names that shadow aliases, + # unless --force is specified. + unless args.force? + if (reason = MissingFormula.disallowed_reason(fc.name)) + odie <<~EOS + The formula '#{fc.name}' is not allowed to be created. + #{reason} + If you really want to create this formula use `--force`. + EOS + end + + Homebrew.with_no_api_env do + if Formula.aliases.include? fc.name + realname = Formulary.canonical_name(fc.name) + odie <<~EOS + The formula '#{realname}' is already aliased to '#{fc.name}'. + Please check that you are not creating a duplicate. + To force creation use `--force`. + EOS + end + end + end + + path = fc.write_formula! + + formula = Homebrew.with_no_api_env do + Formula[fc.name] + end + PyPI.update_python_resources! formula, ignore_non_pypi_packages: true if args.python? + + puts "Please run `HOMEBREW_NO_INSTALL_FROM_API=1 brew audit --new #{fc.name}` before submitting, thanks." + path + end + + sig { returns(T.nilable(String)) } + def __gets + gots = $stdin.gets.chomp + gots.empty? ? nil : gots end end - - path = fc.write_formula! - - formula = Homebrew.with_no_api_env do - Formula[fc.name] - end - PyPI.update_python_resources! formula, ignore_non_pypi_packages: true if args.python? - - puts "Please run `HOMEBREW_NO_INSTALL_FROM_API=1 brew audit --new #{fc.name}` before submitting, thanks." - path - end - - sig { returns(T.nilable(String)) } - def __gets - gots = $stdin.gets.chomp - gots.empty? ? nil : gots end end diff --git a/Library/Homebrew/dev-cmd/determine-test-runners.rb b/Library/Homebrew/dev-cmd/determine-test-runners.rb index 9a94f20ca9..72a63260a0 100644 --- a/Library/Homebrew/dev-cmd/determine-test-runners.rb +++ b/Library/Homebrew/dev-cmd/determine-test-runners.rb @@ -1,62 +1,62 @@ # typed: strict # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "test_runner_formula" require "github_runner_matrix" module Homebrew - sig { returns(Homebrew::CLI::Parser) } - def self.determine_test_runners_args - Homebrew::CLI::Parser.new do - usage_banner <<~EOS - `determine-test-runners` { []|--all-supported} + module DevCmd + class DetermineTestRunners < AbstractCommand + cmd_args do + usage_banner <<~EOS + `determine-test-runners` { []|--all-supported} - Determines the runners used to test formulae or their dependents. For internal use in Homebrew taps. - EOS - switch "--all-supported", - description: "Instead of selecting runners based on the chosen formula, return all supported runners." - switch "--eval-all", - description: "Evaluate all available formulae, whether installed or not, to determine testing " \ - "dependents.", - env: :eval_all - switch "--dependents", - description: "Determine runners for testing dependents. Requires `--eval-all` or `HOMEBREW_EVAL_ALL`.", - depends_on: "--eval-all" + Determines the runners used to test formulae or their dependents. For internal use in Homebrew taps. + EOS + switch "--all-supported", + description: "Instead of selecting runners based on the chosen formula, return all supported runners." + switch "--eval-all", + description: "Evaluate all available formulae, whether installed or not, to determine testing " \ + "dependents.", + env: :eval_all + switch "--dependents", + description: "Determine runners for testing dependents. Requires `--eval-all` or `HOMEBREW_EVAL_ALL`.", + depends_on: "--eval-all" - named_args max: 2 + named_args max: 2 - conflicts "--all-supported", "--dependents" + conflicts "--all-supported", "--dependents" - hide_from_man_page! - end - end + hide_from_man_page! + end - sig { void } - def self.determine_test_runners - args = determine_test_runners_args.parse + sig { override.void } + def run + if args.no_named? && !args.all_supported? + raise Homebrew::CLI::MinNamedArgumentsError, 1 + elsif args.all_supported? && !args.no_named? + raise UsageError, "`--all-supported` is mutually exclusive to other arguments." + end - if args.no_named? && !args.all_supported? - raise Homebrew::CLI::MinNamedArgumentsError, 1 - elsif args.all_supported? && !args.no_named? - raise UsageError, "`--all-supported` is mutually exclusive to other arguments." - end + testing_formulae = args.named.first&.split(",").to_a + testing_formulae.map! { |name| TestRunnerFormula.new(Formulary.factory(name), eval_all: args.eval_all?) } + .freeze + deleted_formulae = args.named.second&.split(",").to_a.freeze + runner_matrix = GitHubRunnerMatrix.new(testing_formulae, deleted_formulae, + all_supported: args.all_supported?, + dependent_matrix: args.dependents?) + runners = runner_matrix.active_runner_specs_hash - testing_formulae = args.named.first&.split(",").to_a - testing_formulae.map! { |name| TestRunnerFormula.new(Formulary.factory(name), eval_all: args.eval_all?) } - .freeze - deleted_formulae = args.named.second&.split(",").to_a.freeze - runner_matrix = GitHubRunnerMatrix.new(testing_formulae, deleted_formulae, - all_supported: args.all_supported?, - dependent_matrix: args.dependents?) - runners = runner_matrix.active_runner_specs_hash + ohai "Runners", JSON.pretty_generate(runners) - ohai "Runners", JSON.pretty_generate(runners) - - github_output = ENV.fetch("GITHUB_OUTPUT") - File.open(github_output, "a") do |f| - f.puts("runners=#{runners.to_json}") - f.puts("runners_present=#{runners.present?}") + github_output = ENV.fetch("GITHUB_OUTPUT") + File.open(github_output, "a") do |f| + f.puts("runners=#{runners.to_json}") + f.puts("runners_present=#{runners.present?}") + end + end end end end diff --git a/Library/Homebrew/dev-cmd/dispatch-build-bottle.rb b/Library/Homebrew/dev-cmd/dispatch-build-bottle.rb index 3b44ced9c2..29fd01138b 100644 --- a/Library/Homebrew/dev-cmd/dispatch-build-bottle.rb +++ b/Library/Homebrew/dev-cmd/dispatch-build-bottle.rb @@ -1,94 +1,93 @@ -# typed: true +# typed: strict # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "utils/github" module Homebrew - module_function + module DevCmd + class DispatchBuildBottle < AbstractCommand + cmd_args do + description <<~EOS + Build bottles for these formulae with GitHub Actions. + EOS + flag "--tap=", + description: "Target tap repository (default: `homebrew/core`)." + flag "--timeout=", + description: "Build timeout (in minutes, default: 60)." + flag "--issue=", + description: "If specified, post a comment to this issue number if the job fails." + comma_array "--macos", + description: "macOS version (or comma-separated list of versions) the bottle should be built for." + flag "--workflow=", + description: "Dispatch specified workflow (default: `dispatch-build-bottle.yml`)." + switch "--upload", + description: "Upload built bottles." + switch "--linux", + description: "Dispatch bottle for Linux (using GitHub runners)." + switch "--linux-self-hosted", + description: "Dispatch bottle for Linux (using self-hosted runner)." + switch "--linux-wheezy", + description: "Use Debian Wheezy container for building the bottle on Linux." - sig { returns(CLI::Parser) } - def dispatch_build_bottle_args - Homebrew::CLI::Parser.new do - description <<~EOS - Build bottles for these formulae with GitHub Actions. - EOS - flag "--tap=", - description: "Target tap repository (default: `homebrew/core`)." - flag "--timeout=", - description: "Build timeout (in minutes, default: 60)." - flag "--issue=", - description: "If specified, post a comment to this issue number if the job fails." - comma_array "--macos", - description: "macOS version (or comma-separated list of versions) the bottle should be built for." - flag "--workflow=", - description: "Dispatch specified workflow (default: `dispatch-build-bottle.yml`)." - switch "--upload", - description: "Upload built bottles." - switch "--linux", - description: "Dispatch bottle for Linux (using GitHub runners)." - switch "--linux-self-hosted", - description: "Dispatch bottle for Linux (using self-hosted runner)." - switch "--linux-wheezy", - description: "Use Debian Wheezy container for building the bottle on Linux." + conflicts "--linux", "--linux-self-hosted" + named_args :formula, min: 1 + end - conflicts "--linux", "--linux-self-hosted" - named_args :formula, min: 1 - end - end + sig { override.void } + def run + tap = Tap.fetch(args.tap || CoreTap.instance.name) + user, repo = tap.full_name.split("/") + ref = "master" + workflow = args.workflow || "dispatch-build-bottle.yml" - def dispatch_build_bottle - args = dispatch_build_bottle_args.parse + runners = [] - tap = Tap.fetch(args.tap || CoreTap.instance.name) - user, repo = tap.full_name.split("/") - ref = "master" - workflow = args.workflow || "dispatch-build-bottle.yml" + if (macos = args.macos&.compact_blank) && macos.present? + runners += macos.map do |element| + # We accept runner name syntax (11-arm64) or bottle syntax (arm64_big_sur) + os, arch = element.then do |s| + tag = Utils::Bottles::Tag.from_symbol(s.to_sym) + [tag.to_macos_version, tag.arch] + rescue ArgumentError, MacOSVersion::Error + os, arch = s.split("-", 2) + [MacOSVersion.new(os), arch&.to_sym] + end - runners = [] - - if (macos = args.macos&.compact_blank) && macos.present? - runners += macos.map do |element| - # We accept runner name syntax (11-arm64) or bottle syntax (arm64_big_sur) - os, arch = element.then do |s| - tag = Utils::Bottles::Tag.from_symbol(s.to_sym) - [tag.to_macos_version, tag.arch] - rescue ArgumentError, MacOSVersion::Error - os, arch = s.split("-", 2) - [MacOSVersion.new(os), arch&.to_sym] + if arch.present? && arch != :x86_64 + "#{os}-#{arch}" + else + os.to_s + end + end end - if arch.present? && arch != :x86_64 - "#{os}-#{arch}" - else - os.to_s + if args.linux? + runners << "ubuntu-22.04" + elsif args.linux_self_hosted? + runners << "linux-self-hosted-1" + end + + raise UsageError, "Must specify `--macos`, `--linux` or `--linux-self-hosted` option." if runners.empty? + + args.named.to_resolved_formulae.each do |formula| + # Required inputs + inputs = { + runner: runners.join(","), + formula: formula.name, + } + + # Optional inputs + # These cannot be passed as nil to GitHub API + inputs[:timeout] = args.timeout if args.timeout + inputs[:issue] = args.issue if args.issue + inputs[:upload] = args.upload? + + ohai "Dispatching #{tap} bottling request of formula \"#{formula.name}\" for #{runners.join(", ")}" + GitHub.workflow_dispatch_event(user, repo, workflow, ref, **inputs) end end end - - if args.linux? - runners << "ubuntu-22.04" - elsif args.linux_self_hosted? - runners << "linux-self-hosted-1" - end - - raise UsageError, "Must specify `--macos`, `--linux` or `--linux-self-hosted` option." if runners.empty? - - args.named.to_resolved_formulae.each do |formula| - # Required inputs - inputs = { - runner: runners.join(","), - formula: formula.name, - } - - # Optional inputs - # These cannot be passed as nil to GitHub API - inputs[:timeout] = args.timeout if args.timeout - inputs[:issue] = args.issue if args.issue - inputs[:upload] = args.upload? - - ohai "Dispatching #{tap} bottling request of formula \"#{formula.name}\" for #{runners.join(", ")}" - GitHub.workflow_dispatch_event(user, repo, workflow, ref, **inputs) - end end end diff --git a/Library/Homebrew/dev-cmd/edit.rb b/Library/Homebrew/dev-cmd/edit.rb index b8c5f4bf6a..1892d67502 100644 --- a/Library/Homebrew/dev-cmd/edit.rb +++ b/Library/Homebrew/dev-cmd/edit.rb @@ -1,133 +1,133 @@ # typed: strict # frozen_string_literal: true +require "abstract_command" require "formula" require "cli/parser" module Homebrew - module_function + module DevCmd + class Edit < AbstractCommand + cmd_args do + description <<~EOS + Open a , or in the editor set by `EDITOR` or `HOMEBREW_EDITOR`, + or open the Homebrew repository for editing if no argument is provided. + EOS - sig { returns(CLI::Parser) } - def edit_args - Homebrew::CLI::Parser.new do - description <<~EOS - Open a , or in the editor set by `EDITOR` or `HOMEBREW_EDITOR`, - or open the Homebrew repository for editing if no argument is provided. - EOS + switch "--formula", "--formulae", + description: "Treat all named arguments as formulae." + switch "--cask", "--casks", + description: "Treat all named arguments as casks." + switch "--print-path", + description: "Print the file path to be edited, without opening an editor." - switch "--formula", "--formulae", - description: "Treat all named arguments as formulae." - switch "--cask", "--casks", - description: "Treat all named arguments as casks." - switch "--print-path", - description: "Print the file path to be edited, without opening an editor." + conflicts "--formula", "--cask" - conflicts "--formula", "--cask" - - named_args [:formula, :cask, :tap], without_api: true - end - end - - sig { params(path: Pathname).returns(T::Boolean) } - def core_formula_path?(path) - path.fnmatch?("**/homebrew-core/Formula/**.rb", File::FNM_DOTMATCH) - end - - sig { params(path: Pathname).returns(T::Boolean) } - def core_cask_path?(path) - path.fnmatch?("**/homebrew-cask/Casks/**.rb", File::FNM_DOTMATCH) - end - - sig { params(path: Pathname).returns(T::Boolean) } - def core_formula_tap?(path) - path == CoreTap.instance.path - end - - sig { params(path: Pathname).returns(T::Boolean) } - def core_cask_tap?(path) - path == CoreCaskTap.instance.path - end - - sig { params(path: Pathname, cask: T::Boolean).returns(T.noreturn) } - def raise_with_message!(path, cask) - name = path.basename(".rb").to_s - - if (tap_match = Regexp.new("#{HOMEBREW_TAP_DIR_REGEX.source}$").match(path.to_s)) - raise TapUnavailableError, CoreTap.instance.name if core_formula_tap?(path) - raise TapUnavailableError, CoreCaskTap.instance.name if core_cask_tap?(path) - - raise TapUnavailableError, "#{tap_match[:user]}/#{tap_match[:repo]}" - elsif cask || core_cask_path?(path) - if !CoreCaskTap.instance.installed? && Homebrew::API::Cask.all_casks.key?(name) - command = "brew tap --force #{CoreCaskTap.instance.name}" - action = "tap #{CoreCaskTap.instance.name}" - else - command = "brew create --cask --set-name #{name} $URL" - action = "create a new cask" - end - elsif core_formula_path?(path) && - !CoreTap.instance.installed? && - Homebrew::API::Formula.all_formulae.key?(name) - command = "brew tap --force #{CoreTap.instance.name}" - action = "tap #{CoreTap.instance.name}" - else - command = "brew create --set-name #{name} $URL" - action = "create a new formula" - end - - raise UsageError, <<~EOS - #{name} doesn't exist on disk. - Run #{Formatter.identifier(command)} to #{action}! - EOS - end - - sig { void } - def edit - args = edit_args.parse - - ENV["COLORTERM"] = ENV.fetch("HOMEBREW_COLORTERM", nil) - - unless (HOMEBREW_REPOSITORY/".git").directory? - odie <<~EOS - Changes will be lost! - The first time you `brew update`, all local changes will be lost; you should - thus `brew update` before you `brew edit`! - EOS - end - - paths = if args.named.empty? - # Sublime requires opting into the project editing path, - # as opposed to VS Code which will infer from the .vscode path - if which_editor(silent: true) == "subl" - ["--project", "#{HOMEBREW_REPOSITORY}/.sublime/homebrew.sublime-project"] - else - # If no formulae are listed, open the project root in an editor. - [HOMEBREW_REPOSITORY] - end - else - expanded_paths = args.named.to_paths - expanded_paths.each do |path| - raise_with_message!(path, args.cask?) unless path.exist? + named_args [:formula, :cask, :tap], without_api: true end - if expanded_paths.any? do |path| - (core_formula_path?(path) || core_cask_path?(path) || core_formula_tap?(path) || core_cask_tap?(path)) && - !Homebrew::EnvConfig.no_install_from_api? && - !Homebrew::EnvConfig.no_env_hints? - end - opoo <<~EOS - `brew install` ignores locally edited casks and formulae if - HOMEBREW_NO_INSTALL_FROM_API is not set. + sig { override.void } + def run + ENV["COLORTERM"] = ENV.fetch("HOMEBREW_COLORTERM", nil) + + unless (HOMEBREW_REPOSITORY/".git").directory? + odie <<~EOS + Changes will be lost! + The first time you `brew update`, all local changes will be lost; you should + thus `brew update` before you `brew edit`! + EOS + end + + paths = if args.named.empty? + # Sublime requires opting into the project editing path, + # as opposed to VS Code which will infer from the .vscode path + if which_editor(silent: true) == "subl" + ["--project", "#{HOMEBREW_REPOSITORY}/.sublime/homebrew.sublime-project"] + else + # If no formulae are listed, open the project root in an editor. + [HOMEBREW_REPOSITORY] + end + else + expanded_paths = args.named.to_paths + expanded_paths.each do |path| + raise_with_message!(path, args.cask?) unless path.exist? + end + + if expanded_paths.any? do |path| + !Homebrew::EnvConfig.no_install_from_api? && + !Homebrew::EnvConfig.no_env_hints? && + (core_formula_path?(path) || core_cask_path?(path) || core_formula_tap?(path) || core_cask_tap?(path)) + end + opoo <<~EOS + `brew install` ignores locally edited casks and formulae if + HOMEBREW_NO_INSTALL_FROM_API is not set. + EOS + end + expanded_paths + end + + if args.print_path? + paths.each { puts _1 } + return + end + + exec_editor(*paths) + end + + private + + sig { params(path: Pathname).returns(T::Boolean) } + def core_formula_path?(path) + path.fnmatch?("**/homebrew-core/Formula/**.rb", File::FNM_DOTMATCH) + end + + sig { params(path: Pathname).returns(T::Boolean) } + def core_cask_path?(path) + path.fnmatch?("**/homebrew-cask/Casks/**.rb", File::FNM_DOTMATCH) + end + + sig { params(path: Pathname).returns(T::Boolean) } + def core_formula_tap?(path) + path == CoreTap.instance.path + end + + sig { params(path: Pathname).returns(T::Boolean) } + def core_cask_tap?(path) + path == CoreCaskTap.instance.path + end + + sig { params(path: Pathname, cask: T::Boolean).returns(T.noreturn) } + def raise_with_message!(path, cask) + name = path.basename(".rb").to_s + + if (tap_match = Regexp.new("#{HOMEBREW_TAP_DIR_REGEX.source}$").match(path.to_s)) + raise TapUnavailableError, CoreTap.instance.name if core_formula_tap?(path) + raise TapUnavailableError, CoreCaskTap.instance.name if core_cask_tap?(path) + + raise TapUnavailableError, "#{tap_match[:user]}/#{tap_match[:repo]}" + elsif cask || core_cask_path?(path) + if !CoreCaskTap.instance.installed? && Homebrew::API::Cask.all_casks.key?(name) + command = "brew tap --force #{CoreCaskTap.instance.name}" + action = "tap #{CoreCaskTap.instance.name}" + else + command = "brew create --cask --set-name #{name} $URL" + action = "create a new cask" + end + elsif core_formula_path?(path) && + !CoreTap.instance.installed? && + Homebrew::API::Formula.all_formulae.key?(name) + command = "brew tap --force #{CoreTap.instance.name}" + action = "tap #{CoreTap.instance.name}" + else + command = "brew create --set-name #{name} $URL" + action = "create a new formula" + end + + raise UsageError, <<~EOS + #{name} doesn't exist on disk. + Run #{Formatter.identifier(command)} to #{action}! EOS end - expanded_paths end - - if args.print_path? - paths.each(&method(:puts)) - return - end - - exec_editor(*paths) end end diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 1f8ed18374..f77505a323 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -1,6 +1,7 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "utils/git" require "formulary" @@ -8,216 +9,220 @@ require "software_spec" require "tap" module Homebrew - BOTTLE_BLOCK_REGEX = / bottle (?:do.+?end|:[a-z]+)\n\n/m + module DevCmd + class Extract < AbstractCommand + BOTTLE_BLOCK_REGEX = / bottle (?:do.+?end|:[a-z]+)\n\n/m - sig { returns(CLI::Parser) } - def self.extract_args - Homebrew::CLI::Parser.new do - usage_banner "`extract` [`--version=`] [`--force`] " - description <<~EOS - Look through repository history to find the most recent version of and - create a copy in . Specifically, the command will create the new - formula file at `/Formula/``@``.rb`. If the tap is not - installed yet, attempt to install/clone the tap before continuing. To extract - a formula from a tap that is not `homebrew/core` use its fully-qualified form of - `/``/`. - EOS - flag "--version=", - description: "Extract the specified of instead of the most recent." - switch "-f", "--force", - description: "Overwrite the destination formula if it already exists." + cmd_args do + usage_banner "`extract` [`--version=`] [`--force`] " + description <<~EOS + Look through repository history to find the most recent version of and + create a copy in . Specifically, the command will create the new + formula file at `/Formula/``@``.rb`. If the tap is not + installed yet, attempt to install/clone the tap before continuing. To extract + a formula from a tap that is not `homebrew/core` use its fully-qualified form of + `/``/`. + EOS + flag "--version=", + description: "Extract the specified of instead of the most recent." + switch "-f", "--force", + description: "Overwrite the destination formula if it already exists." - named_args [:formula, :tap], number: 2, without_api: true - end - end + named_args [:formula, :tap], number: 2, without_api: true + end - def self.extract - args = extract_args.parse + sig { override.void } + def run + if (tap_with_name = args.named.first&.then { Tap.with_formula_name(_1) }) + source_tap, name = tap_with_name + else + name = args.named.first.downcase + source_tap = CoreTap.instance + end + raise TapFormulaUnavailableError.new(source_tap, name) unless source_tap.installed? - if (tap_with_name = args.named.first&.then { Tap.with_formula_name(_1) }) - source_tap, name = tap_with_name - else - name = args.named.first.downcase - source_tap = CoreTap.instance - end - raise TapFormulaUnavailableError.new(source_tap, name) unless source_tap.installed? + destination_tap = Tap.fetch(args.named.second) + unless Homebrew::EnvConfig.developer? + odie "Cannot extract formula to homebrew/core!" if destination_tap.core_tap? + odie "Cannot extract formula to homebrew/cask!" if destination_tap.core_cask_tap? + odie "Cannot extract formula to the same tap!" if destination_tap == source_tap + end + destination_tap.install unless destination_tap.installed? - destination_tap = Tap.fetch(args.named.second) - unless Homebrew::EnvConfig.developer? - odie "Cannot extract formula to homebrew/core!" if destination_tap.core_tap? - odie "Cannot extract formula to homebrew/cask!" if destination_tap.core_cask_tap? - odie "Cannot extract formula to the same tap!" if destination_tap == source_tap - end - destination_tap.install unless destination_tap.installed? - - repo = source_tap.path - pattern = if source_tap.core_tap? - [source_tap.new_formula_path(name), repo/"Formula/#{name}.rb"].uniq - else - # A formula can technically live in the root directory of a tap or in any of its subdirectories - [repo/"#{name}.rb", repo/"**/#{name}.rb"] - end - - if args.version - ohai "Searching repository history" - version = args.version - version_segments = Gem::Version.new(version).segments if Gem::Version.correct?(version) - rev = T.let(nil, T.nilable(String)) - test_formula = T.let(nil, T.nilable(Formula)) - result = "" - loop do - rev = rev.nil? ? "HEAD" : "#{rev}~1" - rev, (path,) = Utils::Git.last_revision_commit_of_files(repo, pattern, before_commit: rev) - if rev.nil? && source_tap.shallow? - odie <<~EOS - Could not find #{name} but #{source_tap} is a shallow clone! - Try again after running: - git -C "#{source_tap.path}" fetch --unshallow - EOS - elsif rev.nil? - odie "Could not find #{name}! The formula or version may not have existed." + repo = source_tap.path + pattern = if source_tap.core_tap? + [source_tap.new_formula_path(name), repo/"Formula/#{name}.rb"].uniq + else + # A formula can technically live in the root directory of a tap or in any of its subdirectories + [repo/"#{name}.rb", repo/"**/#{name}.rb"] end - file = repo/path - result = Utils::Git.last_revision_of_file(repo, file, before_commit: rev) - if result.empty? - odebug "Skipping revision #{rev} - file is empty at this revision" - next - end + if args.version + ohai "Searching repository history" + version = args.version + version_segments = Gem::Version.new(version).segments if Gem::Version.correct?(version) + rev = T.let(nil, T.nilable(String)) + test_formula = T.let(nil, T.nilable(Formula)) + result = "" + loop do + rev = rev.nil? ? "HEAD" : "#{rev}~1" + rev, (path,) = Utils::Git.last_revision_commit_of_files(repo, pattern, before_commit: rev) + if rev.nil? && source_tap.shallow? + odie <<~EOS + Could not find #{name} but #{source_tap} is a shallow clone! + Try again after running: + git -C "#{source_tap.path}" fetch --unshallow + EOS + elsif rev.nil? + odie "Could not find #{name}! The formula or version may not have existed." + end - test_formula = formula_at_revision(repo, name, file, rev) - break if test_formula.nil? || test_formula.version == version + file = repo/path + result = Utils::Git.last_revision_of_file(repo, file, before_commit: rev) + if result.empty? + odebug "Skipping revision #{rev} - file is empty at this revision" + next + end - if version_segments && Gem::Version.correct?(test_formula.version) - test_formula_version_segments = Gem::Version.new(test_formula.version).segments - if version_segments.length < test_formula_version_segments.length - odebug "Apply semantic versioning with #{test_formula_version_segments}" - break if version_segments == test_formula_version_segments.first(version_segments.length) + test_formula = formula_at_revision(repo, name, file, rev) + break if test_formula.nil? || test_formula.version == version + + if version_segments && Gem::Version.correct?(test_formula.version) + test_formula_version_segments = Gem::Version.new(test_formula.version).segments + if version_segments.length < test_formula_version_segments.length + odebug "Apply semantic versioning with #{test_formula_version_segments}" + break if version_segments == test_formula_version_segments.first(version_segments.length) + end + end + + odebug "Trying #{test_formula.version} from revision #{rev} against desired #{version}" + end + odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil? + else + # Search in the root directory of as well as recursively in all of its subdirectories + files = Dir[repo/"{,**/}"].filter_map do |dir| + Pathname.glob("#{dir}/#{name}.rb").find(&:file?) + end + + if files.empty? + ohai "Searching repository history" + rev, (path,) = Utils::Git.last_revision_commit_of_files(repo, pattern) + odie "Could not find #{name}! The formula or version may not have existed." if rev.nil? + file = repo/path + version = T.must(formula_at_revision(repo, name, file, rev)).version + result = Utils::Git.last_revision_of_file(repo, file) + else + file = files.fetch(0).realpath + rev = T.let("HEAD", T.nilable(String)) + version = Formulary.factory(file).version + result = File.read(file) end end - odebug "Trying #{test_formula.version} from revision #{rev} against desired #{version}" - end - odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil? - else - # Search in the root directory of as well as recursively in all of its subdirectories - files = Dir[repo/"{,**/}"].filter_map do |dir| - Pathname.glob("#{dir}/#{name}.rb").find(&:file?) + # The class name has to be renamed to match the new filename, + # e.g. Foo version 1.2.3 becomes FooAT123 and resides in Foo@1.2.3.rb. + class_name = Formulary.class_s(name) + + # Remove any existing version suffixes, as a new one will be added later + name.sub!(/\b@(.*)\z\b/i, "") + versioned_name = Formulary.class_s("#{name}@#{version}") + result.sub!("class #{class_name} < Formula", "class #{versioned_name} < Formula") + + # Remove bottle blocks, they won't work. + result.sub!(BOTTLE_BLOCK_REGEX, "") + + path = destination_tap.path/"Formula/#{name}@#{version.to_s.downcase}.rb" + if path.exist? + unless args.force? + odie <<~EOS + Destination formula already exists: #{path} + To overwrite it and continue anyways, run: + brew extract --force --version=#{version} #{name} #{destination_tap.name} + EOS + end + odebug "Overwriting existing formula at #{path}" + path.delete + end + ohai "Writing formula for #{name} from revision #{rev} to:", path + path.dirname.mkpath + path.write result end - if files.empty? - ohai "Searching repository history" - rev, (path,) = Utils::Git.last_revision_commit_of_files(repo, pattern) - odie "Could not find #{name}! The formula or version may not have existed." if rev.nil? - file = repo/path - version = T.must(formula_at_revision(repo, name, file, rev)).version - result = Utils::Git.last_revision_of_file(repo, file) - else - file = files.fetch(0).realpath - rev = T.let("HEAD", T.nilable(String)) - version = Formulary.factory(file).version - result = File.read(file) + private + + sig { params(repo: Pathname, name: String, file: Pathname, rev: String).returns(T.nilable(Formula)) } + def formula_at_revision(repo, name, file, rev) + return if rev.empty? + + contents = Utils::Git.last_revision_of_file(repo, file, before_commit: rev) + contents.gsub!("@url=", "url ") + contents.gsub!("require 'brewkit'", "require 'formula'") + contents.sub!(BOTTLE_BLOCK_REGEX, "") + with_monkey_patch { Formulary.from_contents(name, file, contents, ignore_errors: true) } end - end - # The class name has to be renamed to match the new filename, - # e.g. Foo version 1.2.3 becomes FooAT123 and resides in Foo@1.2.3.rb. - class_name = Formulary.class_s(name) + def with_monkey_patch + # Since `method_defined?` is not a supported type guard, the use of `alias_method` below is not typesafe: + BottleSpecification.class_eval do + T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing) + define_method(:method_missing) do |*| + # do nothing + end + end - # Remove any existing version suffixes, as a new one will be added later - name.sub!(/\b@(.*)\z\b/i, "") - versioned_name = Formulary.class_s("#{name}@#{version}") - result.sub!("class #{class_name} < Formula", "class #{versioned_name} < Formula") + Module.class_eval do + T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing) + define_method(:method_missing) do |*| + # do nothing + end + end - # Remove bottle blocks, they won't work. - result.sub!(BOTTLE_BLOCK_REGEX, "") + Resource.class_eval do + T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing) + define_method(:method_missing) do |*| + # do nothing + end + end - path = destination_tap.path/"Formula/#{name}@#{version.to_s.downcase}.rb" - if path.exist? - unless args.force? - odie <<~EOS - Destination formula already exists: #{path} - To overwrite it and continue anyways, run: - brew extract --force --version=#{version} #{name} #{destination_tap.name} - EOS - end - odebug "Overwriting existing formula at #{path}" - path.delete - end - ohai "Writing formula for #{name} from revision #{rev} to:", path - path.dirname.mkpath - path.write result - end + DependencyCollector.class_eval do + if method_defined?(:parse_symbol_spec) + T.unsafe(self).alias_method :old_parse_symbol_spec, + :parse_symbol_spec + end + define_method(:parse_symbol_spec) do |*| + # do nothing + end + end - # @private - sig { params(repo: Pathname, name: String, file: Pathname, rev: String).returns(T.nilable(Formula)) } - def self.formula_at_revision(repo, name, file, rev) - return if rev.empty? + yield + ensure + BottleSpecification.class_eval do + if method_defined?(:old_method_missing) + T.unsafe(self).alias_method :method_missing, :old_method_missing + undef :old_method_missing + end + end - contents = Utils::Git.last_revision_of_file(repo, file, before_commit: rev) - contents.gsub!("@url=", "url ") - contents.gsub!("require 'brewkit'", "require 'formula'") - contents.sub!(BOTTLE_BLOCK_REGEX, "") - with_monkey_patch { Formulary.from_contents(name, file, contents, ignore_errors: true) } - end + Module.class_eval do + if method_defined?(:old_method_missing) + T.unsafe(self).alias_method :method_missing, :old_method_missing + undef :old_method_missing + end + end - private_class_method def self.with_monkey_patch - # Since `method_defined?` is not a supported type guard, the use of `alias_method` below is not typesafe: - BottleSpecification.class_eval do - T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing) - define_method(:method_missing) do |*| - # do nothing - end - end + Resource.class_eval do + if method_defined?(:old_method_missing) + T.unsafe(self).alias_method :method_missing, :old_method_missing + undef :old_method_missing + end + end - Module.class_eval do - T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing) - define_method(:method_missing) do |*| - # do nothing - end - end - - Resource.class_eval do - T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing) - define_method(:method_missing) do |*| - # do nothing - end - end - - DependencyCollector.class_eval do - T.unsafe(self).alias_method :old_parse_symbol_spec, :parse_symbol_spec if method_defined?(:parse_symbol_spec) - define_method(:parse_symbol_spec) do |*| - # do nothing - end - end - - yield - ensure - BottleSpecification.class_eval do - if method_defined?(:old_method_missing) - T.unsafe(self).alias_method :method_missing, :old_method_missing - undef :old_method_missing - end - end - - Module.class_eval do - if method_defined?(:old_method_missing) - T.unsafe(self).alias_method :method_missing, :old_method_missing - undef :old_method_missing - end - end - - Resource.class_eval do - if method_defined?(:old_method_missing) - T.unsafe(self).alias_method :method_missing, :old_method_missing - undef :old_method_missing - end - end - - DependencyCollector.class_eval do - if method_defined?(:old_parse_symbol_spec) - T.unsafe(self).alias_method :parse_symbol_spec, :old_parse_symbol_spec - undef :old_parse_symbol_spec + DependencyCollector.class_eval do + if method_defined?(:old_parse_symbol_spec) + T.unsafe(self).alias_method :parse_symbol_spec, :old_parse_symbol_spec + undef :old_parse_symbol_spec + end + end end end end diff --git a/Library/Homebrew/dev-cmd/formula.rb b/Library/Homebrew/dev-cmd/formula.rb index 7f08d465ba..e0189b4b81 100644 --- a/Library/Homebrew/dev-cmd/formula.rb +++ b/Library/Homebrew/dev-cmd/formula.rb @@ -1,32 +1,31 @@ -# typed: true +# typed: strict # frozen_string_literal: true +require "abstract_command" require "formula" require "cli/parser" module Homebrew - module_function + module DevCmd + class FormulaCmd < AbstractCommand + cmd_args do + description <<~EOS + Display the path where is located. + EOS - sig { returns(CLI::Parser) } - def formula_args - Homebrew::CLI::Parser.new do - description <<~EOS - Display the path where is located. - EOS + named_args :formula, min: 1, without_api: true + end - named_args :formula, min: 1, without_api: true + sig { override.void } + def run + formula_paths = args.named.to_paths(only: :formula).select(&:exist?) + if formula_paths.blank? && args.named + .to_paths(only: :cask) + .any?(&:exist?) + odie "Found casks but did not find formulae!" + end + formula_paths.each { puts _1 } + end end end - - def formula - args = formula_args.parse - - formula_paths = args.named.to_paths(only: :formula).select(&:exist?) - if formula_paths.blank? && args.named - .to_paths(only: :cask) - .any?(&:exist?) - odie "Found casks but did not find formulae!" - end - formula_paths.each(&method(:puts)) - end end diff --git a/Library/Homebrew/dev-cmd/generate-cask-api.rb b/Library/Homebrew/dev-cmd/generate-cask-api.rb index 61f2ab8403..c6682521c6 100644 --- a/Library/Homebrew/dev-cmd/generate-cask-api.rb +++ b/Library/Homebrew/dev-cmd/generate-cask-api.rb @@ -1,82 +1,83 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "cask/cask" require "formula" module Homebrew - module_function - - sig { returns(CLI::Parser) } - def generate_cask_api_args - Homebrew::CLI::Parser.new do - description <<~EOS - Generate `homebrew/cask` API data files for <#{HOMEBREW_API_WWW}>. - The generated files are written to the current directory. + module DevCmd + class GenerateCaskApi < AbstractCommand + CASK_JSON_TEMPLATE = <<~EOS + --- + layout: cask_json + --- + {{ content }} EOS - switch "-n", "--dry-run", description: "Generate API data without writing it to files." + cmd_args do + description <<~EOS + Generate `homebrew/cask` API data files for <#{HOMEBREW_API_WWW}>. + The generated files are written to the current directory. + EOS - named_args :none - end - end + switch "-n", "--dry-run", description: "Generate API data without writing it to files." - CASK_JSON_TEMPLATE = <<~EOS - --- - layout: cask_json - --- - {{ content }} - EOS - - def html_template(title) - <<~EOS - --- - title: #{title} - layout: cask - --- - {{ content }} - EOS - end - - def generate_cask_api - args = generate_cask_api_args.parse - - tap = CoreCaskTap.instance - raise TapUnavailableError, tap.name unless tap.installed? - - unless args.dry_run? - directories = ["_data/cask", "api/cask", "api/cask-source", "cask", "api/internal/v3"].freeze - FileUtils.rm_rf directories - FileUtils.mkdir_p directories - end - - Homebrew.with_no_api_env do - tap_migrations_json = JSON.dump(tap.tap_migrations) - File.write("api/cask_tap_migrations.json", tap_migrations_json) unless args.dry_run? - - Cask::Cask.generating_hash! - - tap.cask_files.each do |path| - cask = Cask::CaskLoader.load(path) - name = cask.token - json = JSON.pretty_generate(cask.to_hash_with_variations) - cask_source = path.read - html_template_name = html_template(name) - - unless args.dry_run? - File.write("_data/cask/#{name}.json", "#{json}\n") - File.write("api/cask/#{name}.json", CASK_JSON_TEMPLATE) - File.write("api/cask-source/#{name}.rb", cask_source) - File.write("cask/#{name}.html", html_template_name) - end - rescue - onoe "Error while generating data for cask '#{path.stem}'." - raise + named_args :none end - homebrew_cask_tap_json = JSON.generate(tap.to_internal_api_hash) - File.write("api/internal/v3/homebrew-cask.json", homebrew_cask_tap_json) unless args.dry_run? + sig { override.void } + def run + tap = CoreCaskTap.instance + raise TapUnavailableError, tap.name unless tap.installed? + + unless args.dry_run? + directories = ["_data/cask", "api/cask", "api/cask-source", "cask", "api/internal/v3"].freeze + FileUtils.rm_rf directories + FileUtils.mkdir_p directories + end + + Homebrew.with_no_api_env do + tap_migrations_json = JSON.dump(tap.tap_migrations) + File.write("api/cask_tap_migrations.json", tap_migrations_json) unless args.dry_run? + + Cask::Cask.generating_hash! + + tap.cask_files.each do |path| + cask = Cask::CaskLoader.load(path) + name = cask.token + json = JSON.pretty_generate(cask.to_hash_with_variations) + cask_source = path.read + html_template_name = html_template(name) + + unless args.dry_run? + File.write("_data/cask/#{name}.json", "#{json}\n") + File.write("api/cask/#{name}.json", CASK_JSON_TEMPLATE) + File.write("api/cask-source/#{name}.rb", cask_source) + File.write("cask/#{name}.html", html_template_name) + end + rescue + onoe "Error while generating data for cask '#{path.stem}'." + raise + end + + homebrew_cask_tap_json = JSON.generate(tap.to_internal_api_hash) + File.write("api/internal/v3/homebrew-cask.json", homebrew_cask_tap_json) unless args.dry_run? + end + end + + private + + def html_template(title) + <<~EOS + --- + title: #{title} + layout: cask + --- + {{ content }} + EOS + end end end end diff --git a/Library/Homebrew/dev-cmd/generate-formula-api.rb b/Library/Homebrew/dev-cmd/generate-formula-api.rb index 9c7f30548a..b06e99b95b 100644 --- a/Library/Homebrew/dev-cmd/generate-formula-api.rb +++ b/Library/Homebrew/dev-cmd/generate-formula-api.rb @@ -5,79 +5,79 @@ require "cli/parser" require "formula" module Homebrew - module_function - - sig { returns(CLI::Parser) } - def generate_formula_api_args - Homebrew::CLI::Parser.new do - description <<~EOS - Generate `homebrew/core` API data files for <#{HOMEBREW_API_WWW}>. - The generated files are written to the current directory. + module DevCmd + class GenerateFormulaApi < AbstractCommand + FORMULA_JSON_TEMPLATE = <<~EOS + --- + layout: formula_json + --- + {{ content }} EOS - switch "-n", "--dry-run", description: "Generate API data without writing it to files." + cmd_args do + description <<~EOS + Generate `homebrew/core` API data files for <#{HOMEBREW_API_WWW}>. + The generated files are written to the current directory. + EOS - named_args :none - end - end + switch "-n", "--dry-run", description: "Generate API data without writing it to files." - FORMULA_JSON_TEMPLATE = <<~EOS - --- - layout: formula_json - --- - {{ content }} - EOS - - def html_template(title) - <<~EOS - --- - title: #{title} - layout: formula - redirect_from: /formula-linux/#{title} - --- - {{ content }} - EOS - end - - def generate_formula_api - args = generate_formula_api_args.parse - - tap = CoreTap.instance - raise TapUnavailableError, tap.name unless tap.installed? - - unless args.dry_run? - directories = ["_data/formula", "api/formula", "formula", "api/internal/v3"] - FileUtils.rm_rf directories + ["_data/formula_canonical.json"] - FileUtils.mkdir_p directories - end - - Homebrew.with_no_api_env do - tap_migrations_json = JSON.dump(tap.tap_migrations) - File.write("api/formula_tap_migrations.json", tap_migrations_json) unless args.dry_run? - - Formulary.enable_factory_cache! - Formula.generating_hash! - - tap.formula_names.each do |name| - formula = Formulary.factory(name) - name = formula.name - json = JSON.pretty_generate(formula.to_hash_with_variations) - html_template_name = html_template(name) - - unless args.dry_run? - File.write("_data/formula/#{name.tr("+", "_")}.json", "#{json}\n") - File.write("api/formula/#{name}.json", FORMULA_JSON_TEMPLATE) - File.write("formula/#{name}.html", html_template_name) - end - rescue - onoe "Error while generating data for formula '#{name}'." - raise + named_args :none end - homebrew_core_tap_json = JSON.generate(tap.to_internal_api_hash) - File.write("api/internal/v3/homebrew-core.json", homebrew_core_tap_json) unless args.dry_run? - canonical_json = JSON.pretty_generate(tap.formula_renames.merge(tap.alias_table)) - File.write("_data/formula_canonical.json", "#{canonical_json}\n") unless args.dry_run? + sig { override.void } + def run + tap = CoreTap.instance + raise TapUnavailableError, tap.name unless tap.installed? + + unless args.dry_run? + directories = ["_data/formula", "api/formula", "formula", "api/internal/v3"] + FileUtils.rm_rf directories + ["_data/formula_canonical.json"] + FileUtils.mkdir_p directories + end + + Homebrew.with_no_api_env do + tap_migrations_json = JSON.dump(tap.tap_migrations) + File.write("api/formula_tap_migrations.json", tap_migrations_json) unless args.dry_run? + + Formulary.enable_factory_cache! + Formula.generating_hash! + + tap.formula_names.each do |name| + formula = Formulary.factory(name) + name = formula.name + json = JSON.pretty_generate(formula.to_hash_with_variations) + html_template_name = html_template(name) + + unless args.dry_run? + File.write("_data/formula/#{name.tr("+", "_")}.json", "#{json}\n") + File.write("api/formula/#{name}.json", FORMULA_JSON_TEMPLATE) + File.write("formula/#{name}.html", html_template_name) + end + rescue + onoe "Error while generating data for formula '#{name}'." + raise + end + + homebrew_core_tap_json = JSON.generate(tap.to_internal_api_hash) + File.write("api/internal/v3/homebrew-core.json", homebrew_core_tap_json) unless args.dry_run? + canonical_json = JSON.pretty_generate(tap.formula_renames.merge(tap.alias_table)) + File.write("_data/formula_canonical.json", "#{canonical_json}\n") unless args.dry_run? + end + end + + private + + def html_template(title) + <<~EOS + --- + title: #{title} + layout: formula + redirect_from: /formula-linux/#{title} + --- + {{ content }} + EOS + end end end end diff --git a/Library/Homebrew/dev-cmd/generate-man-completions.rb b/Library/Homebrew/dev-cmd/generate-man-completions.rb index eb67c90158..154041956b 100644 --- a/Library/Homebrew/dev-cmd/generate-man-completions.rb +++ b/Library/Homebrew/dev-cmd/generate-man-completions.rb @@ -1,38 +1,39 @@ -# typed: true +# typed: strict # frozen_string_literal: true +require "abstract_command" require "formula" require "completions" require "manpages" require "system_command" module Homebrew - extend SystemCommand::Mixin + module DevCmd + class GenerateManCompletions < AbstractCommand + include SystemCommand::Mixin - sig { returns(CLI::Parser) } - def self.generate_man_completions_args - Homebrew::CLI::Parser.new do - description <<~EOS - Generate Homebrew's manpages and shell completions. - EOS - named_args :none - end - end + cmd_args do + description <<~EOS + Generate Homebrew's manpages and shell completions. + EOS + named_args :none + end - def self.generate_man_completions - args = generate_man_completions_args.parse + sig { override.void } + def run + Commands.rebuild_internal_commands_completion_list + Manpages.regenerate_man_pages(quiet: args.quiet?) + Completions.update_shell_completions! - Commands.rebuild_internal_commands_completion_list - Manpages.regenerate_man_pages(quiet: args.quiet?) - Completions.update_shell_completions! - - diff = system_command "git", args: [ - "-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", "docs/Manpage.md", "manpages", "completions" - ] - if diff.status.success? - ofail "No changes to manpage or completions." - else - puts "Manpage and completions updated." + diff = system_command "git", args: [ + "-C", HOMEBREW_REPOSITORY, "diff", "--exit-code", "docs/Manpage.md", "manpages", "completions" + ] + if diff.status.success? + ofail "No changes to manpage or completions." + else + puts "Manpage and completions updated." + end + end end end end diff --git a/Library/Homebrew/dev-cmd/install-bundler-gems.rb b/Library/Homebrew/dev-cmd/install-bundler-gems.rb index 167bfa804f..6ac110609d 100644 --- a/Library/Homebrew/dev-cmd/install-bundler-gems.rb +++ b/Library/Homebrew/dev-cmd/install-bundler-gems.rb @@ -1,41 +1,40 @@ -# typed: true +# typed: strict # frozen_string_literal: true +require "abstract_command" require "cli/parser" module Homebrew - module_function + module DevCmd + class InstallBundlerGems < AbstractCommand + cmd_args do + description <<~EOS + Install Homebrew's Bundler gems. + EOS + comma_array "--groups", + description: "Installs the specified comma-separated list of gem groups (default: last used). " \ + "Replaces any previously installed groups." + comma_array "--add-groups", + description: "Installs the specified comma-separated list of gem groups, " \ + "in addition to those already installed." - sig { returns(CLI::Parser) } - def install_bundler_gems_args - Homebrew::CLI::Parser.new do - description <<~EOS - Install Homebrew's Bundler gems. - EOS - comma_array "--groups", - description: "Installs the specified comma-separated list of gem groups (default: last used). " \ - "Replaces any previously installed groups." - comma_array "--add-groups", - description: "Installs the specified comma-separated list of gem groups, " \ - "in addition to those already installed." + conflicts "--groups", "--add-groups" - conflicts "--groups", "--add-groups" + named_args :none + end - named_args :none + sig { override.void } + def run + groups = args.groups || args.add_groups || [] + + if groups.delete("all") + groups |= Homebrew.valid_gem_groups + elsif args.groups # if we have been asked to replace + Homebrew.forget_user_gem_groups! + end + + Homebrew.install_bundler_gems!(groups:) + end end end - - def install_bundler_gems - args = install_bundler_gems_args.parse - - groups = args.groups || args.add_groups || [] - - if groups.delete("all") - groups |= Homebrew.valid_gem_groups - elsif args.groups # if we have been asked to replace - Homebrew.forget_user_gem_groups! - end - - Homebrew.install_bundler_gems!(groups:) - end end diff --git a/Library/Homebrew/dev-cmd/irb.rb b/Library/Homebrew/dev-cmd/irb.rb index 59b52cb667..9da9a012fa 100644 --- a/Library/Homebrew/dev-cmd/irb.rb +++ b/Library/Homebrew/dev-cmd/irb.rb @@ -1,6 +1,7 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "formulary" require "cask/cask_loader" require "cli/parser" @@ -27,73 +28,76 @@ class Symbol end module Homebrew - module_function + module DevCmd + class Irb < AbstractCommand + cmd_args do + description <<~EOS + Enter the interactive Homebrew Ruby shell. + EOS + switch "--examples", + description: "Show several examples." + switch "--pry", + env: :pry, + description: "Use Pry instead of IRB. Implied if `HOMEBREW_PRY` is set." + end - sig { returns(CLI::Parser) } - def irb_args - Homebrew::CLI::Parser.new do - description <<~EOS - Enter the interactive Homebrew Ruby shell. - EOS - switch "--examples", - description: "Show several examples." - switch "--pry", - env: :pry, - description: "Use Pry instead of IRB. Implied if `HOMEBREW_PRY` is set." + # work around IRB modifying ARGV. + sig { params(argv: T.nilable(T::Array[String])).void } + def initialize(argv = nil) = super(argv || ARGV.dup.freeze) + + sig { override.void } + def run + clean_argv + + if args.examples? + puts <<~EOS + 'v8'.f # => instance of the v8 formula + :hub.f.latest_version_installed? + :lua.f.methods - 1.methods + :mpd.f.recursive_dependencies.reject(&:installed?) + + 'vlc'.c # => instance of the vlc cask + :tsh.c.livecheckable? + EOS + return + end + + if args.pry? + Homebrew.install_bundler_gems!(groups: ["pry"]) + require "pry" + else + require "irb" + end + + require "formula" + require "keg" + require "cask" + + ohai "Interactive Homebrew Shell", "Example commands available with: `brew irb --examples`" + if args.pry? + Pry.config.should_load_rc = false # skip loading .pryrc + Pry.config.history_file = "#{Dir.home}/.brew_pry_history" + Pry.config.memory_size = 100 # max lines to save to history file + Pry.config.prompt_name = "brew" + + Pry.start + else + ENV["IRBRC"] = (HOMEBREW_LIBRARY_PATH/"brew_irbrc").to_s + + IRB.start + end + end + + private + + # Remove the `--debug`, `--verbose` and `--quiet` options which cause problems + # for IRB and have already been parsed by the CLI::Parser. + def clean_argv + global_options = Homebrew::CLI::Parser + .global_options + .flat_map { |options| options[0..1] } + ARGV.reject! { |arg| global_options.include?(arg) } + end end end - - def irb - # work around IRB modifying ARGV. - args = irb_args.parse(ARGV.dup.freeze) - - clean_argv - - if args.examples? - puts <<~EOS - 'v8'.f # => instance of the v8 formula - :hub.f.latest_version_installed? - :lua.f.methods - 1.methods - :mpd.f.recursive_dependencies.reject(&:installed?) - - 'vlc'.c # => instance of the vlc cask - :tsh.c.livecheckable? - EOS - return - end - - if args.pry? - Homebrew.install_bundler_gems!(groups: ["pry"]) - require "pry" - else - require "irb" - end - - require "formula" - require "keg" - require "cask" - - ohai "Interactive Homebrew Shell", "Example commands available with: `brew irb --examples`" - if args.pry? - Pry.config.should_load_rc = false # skip loading .pryrc - Pry.config.history_file = "#{Dir.home}/.brew_pry_history" - Pry.config.memory_size = 100 # max lines to save to history file - Pry.config.prompt_name = "brew" - - Pry.start - else - ENV["IRBRC"] = (HOMEBREW_LIBRARY_PATH/"brew_irbrc").to_s - - IRB.start - end - end - - # Remove the `--debug`, `--verbose` and `--quiet` options which cause problems - # for IRB and have already been parsed by the CLI::Parser. - def clean_argv - global_options = Homebrew::CLI::Parser - .global_options - .flat_map { |options| options[0..1] } - ARGV.reject! { |arg| global_options.include?(arg) } - end end diff --git a/Library/Homebrew/dev-cmd/linkage.rb b/Library/Homebrew/dev-cmd/linkage.rb index 753106f67a..233b028ab8 100644 --- a/Library/Homebrew/dev-cmd/linkage.rb +++ b/Library/Homebrew/dev-cmd/linkage.rb @@ -1,58 +1,57 @@ -# typed: true +# typed: strict # frozen_string_literal: true +require "abstract_command" require "cache_store" require "linkage_checker" require "cli/parser" module Homebrew - module_function + module DevCmd + class Linkage < AbstractCommand + cmd_args do + description <<~EOS + Check the library links from the given kegs. If no are + provided, check all kegs. Raises an error if run on uninstalled formulae. + EOS + switch "--test", + description: "Show only missing libraries and exit with a non-zero status if any missing " \ + "libraries are found." + switch "--strict", + depends_on: "--test", + description: "Exit with a non-zero status if any undeclared dependencies with linkage are found." + switch "--reverse", + description: "For every library that a keg references, print its dylib path followed by the " \ + "binaries that link to it." + switch "--cached", + description: "Print the cached linkage values stored in `HOMEBREW_CACHE`, set by a previous " \ + "`brew linkage` run." - sig { returns(CLI::Parser) } - def linkage_args - Homebrew::CLI::Parser.new do - description <<~EOS - Check the library links from the given kegs. If no are - provided, check all kegs. Raises an error if run on uninstalled formulae. - EOS - switch "--test", - description: "Show only missing libraries and exit with a non-zero status if any missing " \ - "libraries are found." - switch "--strict", - depends_on: "--test", - description: "Exit with a non-zero status if any undeclared dependencies with linkage are found." - switch "--reverse", - description: "For every library that a keg references, print its dylib path followed by the " \ - "binaries that link to it." - switch "--cached", - description: "Print the cached linkage values stored in `HOMEBREW_CACHE`, set by a previous " \ - "`brew linkage` run." - - named_args :installed_formula - end - end - - def linkage - args = linkage_args.parse - - CacheStoreDatabase.use(:linkage) do |db| - kegs = if args.named.to_default_kegs.empty? - Formula.installed.filter_map(&:any_installed_keg) - else - args.named.to_default_kegs + named_args :installed_formula end - kegs.each do |keg| - ohai "Checking #{keg.name} linkage" if kegs.size > 1 - result = LinkageChecker.new(keg, cache_db: db) + sig { override.void } + def run + CacheStoreDatabase.use(:linkage) do |db| + kegs = if args.named.to_default_kegs.empty? + Formula.installed.filter_map(&:any_installed_keg) + else + args.named.to_default_kegs + end + kegs.each do |keg| + ohai "Checking #{keg.name} linkage" if kegs.size > 1 - if args.test? - result.display_test_output(strict: args.strict?) - Homebrew.failed = true if result.broken_library_linkage?(test: true, strict: args.strict?) - elsif args.reverse? - result.display_reverse_output - else - result.display_normal_output + result = LinkageChecker.new(keg, cache_db: db) + + if args.test? + result.display_test_output(strict: args.strict?) + Homebrew.failed = true if result.broken_library_linkage?(test: true, strict: args.strict?) + elsif args.reverse? + result.display_reverse_output + else + result.display_normal_output + end + end end end end diff --git a/Library/Homebrew/dev-cmd/livecheck.rb b/Library/Homebrew/dev-cmd/livecheck.rb index 351fcb8a18..c02deece81 100644 --- a/Library/Homebrew/dev-cmd/livecheck.rb +++ b/Library/Homebrew/dev-cmd/livecheck.rb @@ -1,139 +1,140 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "formula" require "livecheck/livecheck" require "livecheck/strategy" module Homebrew - module_function + module DevCmd + class LivecheckCmd < AbstractCommand + cmd_args do + description <<~EOS + Check for newer versions of formulae and/or casks from upstream. + If no formula or cask argument is passed, the list of formulae and + casks to check is taken from `HOMEBREW_LIVECHECK_WATCHLIST` or + `~/.homebrew/livecheck_watchlist.txt`. + EOS + switch "--full-name", + description: "Print formulae and casks with fully-qualified names." + flag "--tap=", + description: "Check formulae and casks within the given tap, specified as `/`." + switch "--eval-all", + description: "Evaluate all available formulae and casks, whether installed or not, to check them." + switch "--installed", + description: "Check formulae and casks that are currently installed." + switch "--newer-only", + description: "Show the latest version only if it's newer than the formula/cask." + switch "--json", + description: "Output information in JSON format." + switch "-r", "--resources", + description: "Also check resources for formulae." + switch "-q", "--quiet", + description: "Suppress warnings, don't print a progress bar for JSON output." + switch "--formula", "--formulae", + description: "Only check formulae." + switch "--cask", "--casks", + description: "Only check casks." + switch "--extract-plist", + description: "Include casks using the ExtractPlist livecheck strategy." - sig { returns(CLI::Parser) } - def livecheck_args - Homebrew::CLI::Parser.new do - description <<~EOS - Check for newer versions of formulae and/or casks from upstream. - If no formula or cask argument is passed, the list of formulae and - casks to check is taken from `HOMEBREW_LIVECHECK_WATCHLIST` or - `~/.homebrew/livecheck_watchlist.txt`. - EOS - switch "--full-name", - description: "Print formulae and casks with fully-qualified names." - flag "--tap=", - description: "Check formulae and casks within the given tap, specified as `/`." - switch "--eval-all", - description: "Evaluate all available formulae and casks, whether installed or not, to check them." - switch "--installed", - description: "Check formulae and casks that are currently installed." - switch "--newer-only", - description: "Show the latest version only if it's newer than the formula/cask." - switch "--json", - description: "Output information in JSON format." - switch "-r", "--resources", - description: "Also check resources for formulae." - switch "-q", "--quiet", - description: "Suppress warnings, don't print a progress bar for JSON output." - switch "--formula", "--formulae", - description: "Only check formulae." - switch "--cask", "--casks", - description: "Only check casks." - switch "--extract-plist", - description: "Include casks using the ExtractPlist livecheck strategy." + conflicts "--debug", "--json" + conflicts "--tap=", "--eval-all", "--installed" + conflicts "--cask", "--formula" + conflicts "--formula", "--extract-plist" - conflicts "--debug", "--json" - conflicts "--tap=", "--eval-all", "--installed" - conflicts "--cask", "--formula" - conflicts "--formula", "--extract-plist" - - named_args [:formula, :cask], without_api: true - end - end - - def watchlist_path - @watchlist_path ||= begin - watchlist = File.expand_path(Homebrew::EnvConfig.livecheck_watchlist) - - unless File.exist?(watchlist) - previous_default_watchlist = File.expand_path("~/.brew_livecheck_watchlist") - if File.exist?(previous_default_watchlist) - odisabled "~/.brew_livecheck_watchlist", "~/.homebrew/livecheck_watchlist.txt" - watchlist = previous_default_watchlist - end + named_args [:formula, :cask], without_api: true end - watchlist - end - end + sig { override.void } + def run + Homebrew.install_bundler_gems!(groups: ["livecheck"]) - def livecheck - args = livecheck_args.parse + all = args.eval_all? - Homebrew.install_bundler_gems!(groups: ["livecheck"]) - - all = args.eval_all? - - if args.debug? && args.verbose? - puts args - puts Homebrew::EnvConfig.livecheck_watchlist if Homebrew::EnvConfig.livecheck_watchlist.present? - end - - formulae_and_casks_to_check = Homebrew.with_no_api_env do - if args.tap - tap = Tap.fetch(args.tap) - formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) } - casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) } - formulae + casks - elsif args.installed? - formulae = args.cask? ? [] : Formula.installed - casks = args.formula? ? [] : Cask::Caskroom.casks - formulae + casks - elsif all - formulae = args.cask? ? [] : Formula.all(eval_all: args.eval_all?) - casks = args.formula? ? [] : Cask::Cask.all(eval_all: args.eval_all?) - formulae + casks - elsif args.named.present? - if args.formula? - args.named.to_formulae - elsif args.cask? - args.named.to_casks - else - args.named.to_formulae_and_casks + if args.debug? && args.verbose? + puts args + puts Homebrew::EnvConfig.livecheck_watchlist if Homebrew::EnvConfig.livecheck_watchlist.present? end - elsif File.exist?(watchlist_path) - begin - names = Pathname.new(watchlist_path).read.lines - .reject { |line| line.start_with?("#") || line.blank? } - .map(&:strip) - named_args = CLI::NamedArgs.new(*names, parent: args) - named_args.to_formulae_and_casks(ignore_unavailable: true) - rescue Errno::ENOENT => e - onoe e + formulae_and_casks_to_check = Homebrew.with_no_api_env do + if args.tap + tap = Tap.fetch(T.must(args.tap)) + formulae = args.cask? ? [] : tap.formula_files.map { |path| Formulary.factory(path) } + casks = args.formula? ? [] : tap.cask_files.map { |path| Cask::CaskLoader.load(path) } + formulae + casks + elsif args.installed? + formulae = args.cask? ? [] : Formula.installed + casks = args.formula? ? [] : Cask::Caskroom.casks + formulae + casks + elsif all + formulae = args.cask? ? [] : Formula.all(eval_all: args.eval_all?) + casks = args.formula? ? [] : Cask::Cask.all(eval_all: args.eval_all?) + formulae + casks + elsif args.named.present? + if args.formula? + args.named.to_formulae + elsif args.cask? + args.named.to_casks + else + args.named.to_formulae_and_casks + end + elsif File.exist?(watchlist_path) + begin + names = Pathname.new(watchlist_path).read.lines + .reject { |line| line.start_with?("#") || line.blank? } + .map(&:strip) + + named_args = CLI::NamedArgs.new(*names, parent: args) + named_args.to_formulae_and_casks(ignore_unavailable: true) + rescue Errno::ENOENT => e + onoe e + end + else + raise UsageError, "A watchlist file is required when no arguments are given." + end + end + + formulae_and_casks_to_check = formulae_and_casks_to_check.sort_by do |formula_or_cask| + formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name + end + + raise UsageError, "No formulae or casks to check." if formulae_and_casks_to_check.blank? + + options = { + json: args.json?, + full_name: args.full_name?, + handle_name_conflict: !args.formula? && !args.cask?, + check_resources: args.resources?, + newer_only: args.newer_only?, + extract_plist: args.extract_plist?, + quiet: args.quiet?, + debug: args.debug?, + verbose: args.verbose?, + }.compact + + Livecheck.run_checks(formulae_and_casks_to_check, **options) + end + + private + + def watchlist_path + @watchlist_path ||= begin + watchlist = File.expand_path(Homebrew::EnvConfig.livecheck_watchlist) + + unless File.exist?(watchlist) + previous_default_watchlist = File.expand_path("~/.brew_livecheck_watchlist") + if File.exist?(previous_default_watchlist) + odisabled "~/.brew_livecheck_watchlist", "~/.homebrew/livecheck_watchlist.txt" + watchlist = previous_default_watchlist + end + end + + watchlist end - else - raise UsageError, "A watchlist file is required when no arguments are given." end end - - formulae_and_casks_to_check = formulae_and_casks_to_check.sort_by do |formula_or_cask| - formula_or_cask.respond_to?(:token) ? formula_or_cask.token : formula_or_cask.name - end - - raise UsageError, "No formulae or casks to check." if formulae_and_casks_to_check.blank? - - options = { - json: args.json?, - full_name: args.full_name?, - handle_name_conflict: !args.formula? && !args.cask?, - check_resources: args.resources?, - newer_only: args.newer_only?, - extract_plist: args.extract_plist?, - quiet: args.quiet?, - debug: args.debug?, - verbose: args.verbose?, - }.compact - - Livecheck.run_checks(formulae_and_casks_to_check, **options) end end diff --git a/Library/Homebrew/dev-cmd/pr-automerge.rb b/Library/Homebrew/dev-cmd/pr-automerge.rb index 583708359f..6995de1332 100644 --- a/Library/Homebrew/dev-cmd/pr-automerge.rb +++ b/Library/Homebrew/dev-cmd/pr-automerge.rb @@ -1,87 +1,86 @@ -# typed: true +# typed: strict # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "utils/github" module Homebrew - module_function + module DevCmd + class PrAutomerge < AbstractCommand + cmd_args do + description <<~EOS + Find pull requests that can be automatically merged using `brew pr-publish`. + EOS + flag "--tap=", + description: "Target tap repository (default: `homebrew/core`)." + flag "--workflow=", + description: "Workflow file to use with `brew pr-publish`." + flag "--with-label=", + description: "Pull requests must have this label." + comma_array "--without-labels", + description: "Pull requests must not have these labels (default: " \ + "`do not merge`, `new formula`, `automerge-skip`, " \ + "`pre-release`, `CI-published-bottle-commits`)." + switch "--without-approval", + description: "Pull requests do not require approval to be merged." + switch "--publish", + description: "Run `brew pr-publish` on matching pull requests." + switch "--autosquash", + description: "Instruct `brew pr-publish` to automatically reformat and reword commits " \ + "in the pull request to the preferred format." + switch "--no-autosquash", + description: "Instruct `brew pr-publish` to skip automatically reformatting and rewording commits " \ + "in the pull request to the preferred format.", + disable: true, # odisabled: remove this switch with 4.3.0 + hidden: true + switch "--ignore-failures", + description: "Include pull requests that have failing status checks." - sig { returns(CLI::Parser) } - def pr_automerge_args - Homebrew::CLI::Parser.new do - description <<~EOS - Find pull requests that can be automatically merged using `brew pr-publish`. - EOS - flag "--tap=", - description: "Target tap repository (default: `homebrew/core`)." - flag "--workflow=", - description: "Workflow file to use with `brew pr-publish`." - flag "--with-label=", - description: "Pull requests must have this label." - comma_array "--without-labels", - description: "Pull requests must not have these labels (default: " \ - "`do not merge`, `new formula`, `automerge-skip`, " \ - "`pre-release`, `CI-published-bottle-commits`)." - switch "--without-approval", - description: "Pull requests do not require approval to be merged." - switch "--publish", - description: "Run `brew pr-publish` on matching pull requests." - switch "--autosquash", - description: "Instruct `brew pr-publish` to automatically reformat and reword commits " \ - "in the pull request to the preferred format." - switch "--no-autosquash", - description: "Instruct `brew pr-publish` to skip automatically reformatting and rewording commits " \ - "in the pull request to the preferred format.", - disable: true, # odisabled: remove this switch with 4.3.0 - hidden: true - switch "--ignore-failures", - description: "Include pull requests that have failing status checks." + named_args :none + end - named_args :none - end - end + sig { override.void } + def run + without_labels = args.without_labels || [ + "do not merge", + "new formula", + "automerge-skip", + "pre-release", + "CI-published-bottle-commits", + ] + tap = Tap.fetch(args.tap || CoreTap.instance.name) - def pr_automerge - args = pr_automerge_args.parse + query = "is:pr is:open repo:#{tap.full_name} draft:false" + query += args.ignore_failures? ? " -status:pending" : " status:success" + query += " review:approved" unless args.without_approval? + query += " label:\"#{args.with_label}\"" if args.with_label + without_labels.each { |label| query += " -label:\"#{label}\"" } + odebug "Searching: #{query}" - without_labels = args.without_labels || [ - "do not merge", - "new formula", - "automerge-skip", - "pre-release", - "CI-published-bottle-commits", - ] - tap = Tap.fetch(args.tap || CoreTap.instance.name) + prs = GitHub.search_issues query + if prs.blank? + ohai "No matching pull requests!" + return + end - query = "is:pr is:open repo:#{tap.full_name} draft:false" - query += args.ignore_failures? ? " -status:pending" : " status:success" - query += " review:approved" unless args.without_approval? - query += " label:\"#{args.with_label}\"" if args.with_label - without_labels&.each { |label| query += " -label:\"#{label}\"" } - odebug "Searching: #{query}" + ohai "#{prs.count} matching pull #{Utils.pluralize("request", prs.count)}:" + pr_urls = [] + prs.each do |pr| + puts "#{tap.full_name unless tap.core_tap?}##{pr["number"]}: #{pr["title"]}" + pr_urls << pr["html_url"] + end - prs = GitHub.search_issues query - if prs.blank? - ohai "No matching pull requests!" - return - end - - ohai "#{prs.count} matching pull #{Utils.pluralize("request", prs.count)}:" - pr_urls = [] - prs.each do |pr| - puts "#{tap.full_name unless tap.core_tap?}##{pr["number"]}: #{pr["title"]}" - pr_urls << pr["html_url"] - end - - publish_args = ["pr-publish"] - publish_args << "--tap=#{tap}" if tap - publish_args << "--workflow=#{args.workflow}" if args.workflow - publish_args << "--autosquash" if args.autosquash? - if args.publish? - safe_system HOMEBREW_BREW_FILE, *publish_args, *pr_urls - else - ohai "Now run:", " brew #{publish_args.join " "} \\\n #{pr_urls.join " \\\n "}" + publish_args = ["pr-publish"] + publish_args << "--tap=#{tap}" if tap + publish_args << "--workflow=#{args.workflow}" if args.workflow + publish_args << "--autosquash" if args.autosquash? + if args.publish? + safe_system HOMEBREW_BREW_FILE, *publish_args, *pr_urls + else + ohai "Now run:", " brew #{publish_args.join " "} \\\n #{pr_urls.join " \\\n "}" + end + end end end end diff --git a/Library/Homebrew/dev-cmd/pr-publish.rb b/Library/Homebrew/dev-cmd/pr-publish.rb index ea0415f71e..a6b83183c4 100644 --- a/Library/Homebrew/dev-cmd/pr-publish.rb +++ b/Library/Homebrew/dev-cmd/pr-publish.rb @@ -1,75 +1,74 @@ -# typed: true +# typed: strict # frozen_string_literal: true +require "abstract_command" require "cli/parser" require "utils/github" module Homebrew - module_function + module DevCmd + class PrPublish < AbstractCommand + cmd_args do + description <<~EOS + Publish bottles for a pull request with GitHub Actions. + Requires write access to the repository. + EOS + switch "--autosquash", + description: "If supported on the target tap, automatically reformat and reword commits " \ + "to our preferred format." + switch "--large-runner", + description: "Run the upload job on a large runner." + flag "--branch=", + description: "Branch to use the workflow from (default: `master`)." + flag "--message=", + depends_on: "--autosquash", + description: "Message to include when autosquashing revision bumps, deletions and rebuilds." + flag "--tap=", + description: "Target tap repository (default: `homebrew/core`)." + flag "--workflow=", + description: "Target workflow filename (default: `publish-commit-bottles.yml`)." - sig { returns(CLI::Parser) } - def pr_publish_args - Homebrew::CLI::Parser.new do - description <<~EOS - Publish bottles for a pull request with GitHub Actions. - Requires write access to the repository. - EOS - switch "--autosquash", - description: "If supported on the target tap, automatically reformat and reword commits " \ - "to our preferred format." - switch "--large-runner", - description: "Run the upload job on a large runner." - flag "--branch=", - description: "Branch to use the workflow from (default: `master`)." - flag "--message=", - depends_on: "--autosquash", - description: "Message to include when autosquashing revision bumps, deletions and rebuilds." - flag "--tap=", - description: "Target tap repository (default: `homebrew/core`)." - flag "--workflow=", - description: "Target workflow filename (default: `publish-commit-bottles.yml`)." - - named_args :pull_request, min: 1 - end - end - - def pr_publish - args = pr_publish_args.parse - - tap = Tap.fetch(args.tap || CoreTap.instance.name) - workflow = args.workflow || "publish-commit-bottles.yml" - ref = args.branch || "master" - - inputs = { - autosquash: args.autosquash?, - large_runner: args.large_runner?, - } - inputs[:message] = args.message if args.message.presence - - args.named.uniq.each do |arg| - arg = "#{tap.default_remote}/pull/#{arg}" if arg.to_i.positive? - url_match = arg.match HOMEBREW_PULL_OR_COMMIT_URL_REGEX - _, user, repo, issue = *url_match - odie "Not a GitHub pull request: #{arg}" unless issue - - inputs[:pull_request] = issue - - pr_labels = GitHub.pull_request_labels(user, repo, issue) - if pr_labels.include?("autosquash") - oh1 "Found `autosquash` label on ##{issue}. Requesting autosquash." - inputs[:autosquash] = true - end - if pr_labels.include?("large-bottle-upload") - oh1 "Found `large-bottle-upload` label on ##{issue}. Requesting upload on large runner." - inputs[:large_runner] = true + named_args :pull_request, min: 1 end - if args.tap.present? && !T.must("#{user}/#{repo}".casecmp(tap.full_name)).zero? - odie "Pull request URL is for #{user}/#{repo} but `--tap=#{tap.full_name}` was specified!" - end + sig { override.void } + def run + tap = Tap.fetch(args.tap || CoreTap.instance.name) + workflow = args.workflow || "publish-commit-bottles.yml" + ref = args.branch || "master" - ohai "Dispatching #{tap} pull request ##{issue}" - GitHub.workflow_dispatch_event(user, repo, workflow, ref, **inputs) + inputs = { + autosquash: args.autosquash?, + large_runner: args.large_runner?, + } + inputs[:message] = args.message if args.message.presence + + args.named.uniq.each do |arg| + arg = "#{tap.default_remote}/pull/#{arg}" if arg.to_i.positive? + url_match = arg.match HOMEBREW_PULL_OR_COMMIT_URL_REGEX + _, user, repo, issue = *url_match + odie "Not a GitHub pull request: #{arg}" unless issue + + inputs[:pull_request] = issue + + pr_labels = GitHub.pull_request_labels(user, repo, issue) + if pr_labels.include?("autosquash") + oh1 "Found `autosquash` label on ##{issue}. Requesting autosquash." + inputs[:autosquash] = true + end + if pr_labels.include?("large-bottle-upload") + oh1 "Found `large-bottle-upload` label on ##{issue}. Requesting upload on large runner." + inputs[:large_runner] = true + end + + if args.tap.present? && !T.must("#{user}/#{repo}".casecmp(tap.full_name)).zero? + odie "Pull request URL is for #{user}/#{repo} but `--tap=#{tap.full_name}` was specified!" + end + + ohai "Dispatching #{tap} pull request ##{issue}" + GitHub.workflow_dispatch_event(user, repo, workflow, ref, **inputs) + end + end end end end diff --git a/Library/Homebrew/test/abstract_command_spec.rb b/Library/Homebrew/test/abstract_command_spec.rb index 527a78e0b7..e3ae8add92 100644 --- a/Library/Homebrew/test/abstract_command_spec.rb +++ b/Library/Homebrew/test/abstract_command_spec.rb @@ -39,6 +39,11 @@ RSpec.describe Homebrew::AbstractCommand do expect(described_class.command("test-cat")).to be(TestCat) end + it "removes -cmd suffix from command name" do + require "dev-cmd/formula" + expect(Homebrew::DevCmd::FormulaCmd.command_name).to eq("formula") + end + describe "when command name is overridden" do before do tac = Class.new(described_class) do @@ -61,7 +66,7 @@ RSpec.describe Homebrew::AbstractCommand do ["cmd", "dev-cmd"].each do |dir| Dir[File.join(__dir__, "../#{dir}", "*.rb")].each { require(_1) } end - test_classes = ["Cat", "Tac"] + test_classes = ["TestCat", "Tac"] described_class.subclasses.each do |klass| next if test_classes.include?(klass.name) diff --git a/Library/Homebrew/test/cmd/shared_examples/args_parse.rb b/Library/Homebrew/test/cmd/shared_examples/args_parse.rb index b2a89271ae..865f480013 100644 --- a/Library/Homebrew/test/cmd/shared_examples/args_parse.rb +++ b/Library/Homebrew/test/cmd/shared_examples/args_parse.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples "parseable arguments" do |argv: []| +RSpec.shared_examples "parseable arguments" do |argv: nil| subject(:method_name) { "#{command_name.tr("-", "_")}_args" } let(:command_name) do |example| @@ -9,6 +9,8 @@ RSpec.shared_examples "parseable arguments" do |argv: []| it "can parse arguments" do if described_class + argv ||= described_class.parser.instance_variable_get(:@min_named_args)&.times&.map { "argument" } + argv ||= [] cmd = described_class.new(argv) expect(cmd.args).to be_a Homebrew::CLI::Args else diff --git a/Library/Homebrew/test/dev-cmd/bottle_spec.rb b/Library/Homebrew/test/dev-cmd/bottle_spec.rb index 58dbd58cc6..d646e6b1d5 100644 --- a/Library/Homebrew/test/dev-cmd/bottle_spec.rb +++ b/Library/Homebrew/test/dev-cmd/bottle_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Homebrew::DevCmd::Bottle do EOS end - it_behaves_like "parseable arguments", argv: ["foo"] + it_behaves_like "parseable arguments" it "builds a bottle for the given Formula", :integration_test do install_test_formula "testball", build_bottle: true diff --git a/Library/Homebrew/test/dev-cmd/bump-cask-pr_spec.rb b/Library/Homebrew/test/dev-cmd/bump-cask-pr_spec.rb index 8454650c9d..7c0a5afa97 100644 --- a/Library/Homebrew/test/dev-cmd/bump-cask-pr_spec.rb +++ b/Library/Homebrew/test/dev-cmd/bump-cask-pr_spec.rb @@ -4,5 +4,5 @@ require "cmd/shared_examples/args_parse" require "dev-cmd/bump-cask-pr" RSpec.describe Homebrew::DevCmd::BumpCaskPr do - it_behaves_like "parseable arguments", argv: ["foo"] + it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/bump-revision_spec.rb b/Library/Homebrew/test/dev-cmd/bump-revision_spec.rb index 10c2646451..e6d8d516cc 100644 --- a/Library/Homebrew/test/dev-cmd/bump-revision_spec.rb +++ b/Library/Homebrew/test/dev-cmd/bump-revision_spec.rb @@ -4,5 +4,5 @@ require "cmd/shared_examples/args_parse" require "dev-cmd/bump-revision" RSpec.describe Homebrew::DevCmd::BumpRevision do - it_behaves_like "parseable arguments", argv: ["foo"] + it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/bump-unversioned-casks_spec.rb b/Library/Homebrew/test/dev-cmd/bump-unversioned-casks_spec.rb index f6c6936307..48250d51d4 100644 --- a/Library/Homebrew/test/dev-cmd/bump-unversioned-casks_spec.rb +++ b/Library/Homebrew/test/dev-cmd/bump-unversioned-casks_spec.rb @@ -4,5 +4,5 @@ require "cmd/shared_examples/args_parse" require "dev-cmd/bump-unversioned-casks" RSpec.describe Homebrew::DevCmd::BumpUnversionedCasks do - it_behaves_like "parseable arguments", argv: ["foo"] + it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/cat_spec.rb b/Library/Homebrew/test/dev-cmd/cat_spec.rb index b00cd32f81..b3789b8856 100644 --- a/Library/Homebrew/test/dev-cmd/cat_spec.rb +++ b/Library/Homebrew/test/dev-cmd/cat_spec.rb @@ -4,7 +4,7 @@ require "cmd/shared_examples/args_parse" require "dev-cmd/cat" RSpec.describe Homebrew::DevCmd::Cat do - it_behaves_like "parseable arguments", argv: ["foo"] + it_behaves_like "parseable arguments" it "prints the content of a given Formula", :integration_test do formula_file = setup_test_formula "testball" diff --git a/Library/Homebrew/test/dev-cmd/command_spec.rb b/Library/Homebrew/test/dev-cmd/command_spec.rb index 1ea4ef9265..d5751e5dd4 100644 --- a/Library/Homebrew/test/dev-cmd/command_spec.rb +++ b/Library/Homebrew/test/dev-cmd/command_spec.rb @@ -4,7 +4,7 @@ require "cmd/shared_examples/args_parse" require "dev-cmd/command" RSpec.describe Homebrew::DevCmd::Command do - it_behaves_like "parseable arguments", argv: ["foo"] + it_behaves_like "parseable arguments" it "returns the file for a given command", :integration_test do expect { brew "command", "info" } diff --git a/Library/Homebrew/test/dev-cmd/create_spec.rb b/Library/Homebrew/test/dev-cmd/create_spec.rb index fefc85dab2..f1cd4b526a 100644 --- a/Library/Homebrew/test/dev-cmd/create_spec.rb +++ b/Library/Homebrew/test/dev-cmd/create_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/create" -RSpec.describe "brew create" do +RSpec.describe Homebrew::DevCmd::Create do let(:url) { "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz" } let(:formula_file) { CoreTap.instance.new_formula_path("testball") } diff --git a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb index 4241778350..ed6b374b37 100644 --- a/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb +++ b/Library/Homebrew/test/dev-cmd/determine-test-runners_spec.rb @@ -3,7 +3,7 @@ require "dev-cmd/determine-test-runners" require "cmd/shared_examples/args_parse" -RSpec.describe "brew determine-test-runners" do +RSpec.describe Homebrew::DevCmd::DetermineTestRunners do def get_runners(file) runner_line = File.open(file).first json_text = runner_line[/runners=(.*)/, 1] diff --git a/Library/Homebrew/test/dev-cmd/dispatch-build-bottle_spec.rb b/Library/Homebrew/test/dev-cmd/dispatch-build-bottle_spec.rb index e3fad4df2e..84c329bc3b 100644 --- a/Library/Homebrew/test/dev-cmd/dispatch-build-bottle_spec.rb +++ b/Library/Homebrew/test/dev-cmd/dispatch-build-bottle_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/dispatch-build-bottle" -RSpec.describe "brew dispatch-build-bottle" do +RSpec.describe Homebrew::DevCmd::DispatchBuildBottle do it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/edit_spec.rb b/Library/Homebrew/test/dev-cmd/edit_spec.rb index 09ccfbdc24..265eb73113 100644 --- a/Library/Homebrew/test/dev-cmd/edit_spec.rb +++ b/Library/Homebrew/test/dev-cmd/edit_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/edit" -RSpec.describe "brew edit" do +RSpec.describe Homebrew::DevCmd::Edit do it_behaves_like "parseable arguments" it "opens a given Formula in an editor", :integration_test do diff --git a/Library/Homebrew/test/dev-cmd/extract_spec.rb b/Library/Homebrew/test/dev-cmd/extract_spec.rb index 9f1ec85e49..f6e6104255 100644 --- a/Library/Homebrew/test/dev-cmd/extract_spec.rb +++ b/Library/Homebrew/test/dev-cmd/extract_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/extract" -RSpec.describe "brew extract" do +RSpec.describe Homebrew::DevCmd::Extract do it_behaves_like "parseable arguments" context "when extracting a formula" do diff --git a/Library/Homebrew/test/dev-cmd/formula_spec.rb b/Library/Homebrew/test/dev-cmd/formula_spec.rb index 73949595a3..59acf64bfa 100644 --- a/Library/Homebrew/test/dev-cmd/formula_spec.rb +++ b/Library/Homebrew/test/dev-cmd/formula_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/formula" -RSpec.describe "brew formula" do +RSpec.describe Homebrew::DevCmd::FormulaCmd do it_behaves_like "parseable arguments" it "prints a given Formula's path", :integration_test do diff --git a/Library/Homebrew/test/dev-cmd/generate-cask-api_spec.rb b/Library/Homebrew/test/dev-cmd/generate-cask-api_spec.rb index 08db0a6083..426c4b9cb1 100644 --- a/Library/Homebrew/test/dev-cmd/generate-cask-api_spec.rb +++ b/Library/Homebrew/test/dev-cmd/generate-cask-api_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/generate-cask-api" -RSpec.describe "brew generate-cask-api" do +RSpec.describe Homebrew::DevCmd::GenerateCaskApi do it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb b/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb index 26958bffa6..74136d6645 100644 --- a/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb +++ b/Library/Homebrew/test/dev-cmd/generate-formula-api_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/generate-formula-api" -RSpec.describe "brew generate-formula-api" do +RSpec.describe Homebrew::DevCmd::GenerateFormulaApi do it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/generate-man-completions_spec.rb b/Library/Homebrew/test/dev-cmd/generate-man-completions_spec.rb index 154790498a..6ac40d1af1 100644 --- a/Library/Homebrew/test/dev-cmd/generate-man-completions_spec.rb +++ b/Library/Homebrew/test/dev-cmd/generate-man-completions_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/generate-man-completions" -RSpec.describe "brew generate-man-completions" do +RSpec.describe Homebrew::DevCmd::GenerateManCompletions do it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/install-bundler-gems_spec.rb b/Library/Homebrew/test/dev-cmd/install-bundler-gems_spec.rb new file mode 100644 index 0000000000..d7d33e739e --- /dev/null +++ b/Library/Homebrew/test/dev-cmd/install-bundler-gems_spec.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "cmd/shared_examples/args_parse" +require "dev-cmd/install-bundler-gems" + +RSpec.describe Homebrew::DevCmd::InstallBundlerGems do + it_behaves_like "parseable arguments" +end diff --git a/Library/Homebrew/test/dev-cmd/irb_spec.rb b/Library/Homebrew/test/dev-cmd/irb_spec.rb index 5ede76a455..4ba95bc123 100644 --- a/Library/Homebrew/test/dev-cmd/irb_spec.rb +++ b/Library/Homebrew/test/dev-cmd/irb_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/irb" -RSpec.describe "brew irb" do +RSpec.describe Homebrew::DevCmd::Irb do it_behaves_like "parseable arguments" describe "integration test" do diff --git a/Library/Homebrew/test/dev-cmd/linkage_spec.rb b/Library/Homebrew/test/dev-cmd/linkage_spec.rb index 2318f53bff..bd859fd94f 100644 --- a/Library/Homebrew/test/dev-cmd/linkage_spec.rb +++ b/Library/Homebrew/test/dev-cmd/linkage_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/linkage" -RSpec.describe "brew linkage" do +RSpec.describe Homebrew::DevCmd::Linkage do it_behaves_like "parseable arguments" it "works when no arguments are provided", :integration_test do diff --git a/Library/Homebrew/test/dev-cmd/livecheck_spec.rb b/Library/Homebrew/test/dev-cmd/livecheck_spec.rb index 686eb3a8da..413e6287d2 100644 --- a/Library/Homebrew/test/dev-cmd/livecheck_spec.rb +++ b/Library/Homebrew/test/dev-cmd/livecheck_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/livecheck" -RSpec.describe "brew livecheck" do +RSpec.describe Homebrew::DevCmd::LivecheckCmd do it_behaves_like "parseable arguments" it "reports the latest version of a Formula", :integration_test, :needs_network do diff --git a/Library/Homebrew/test/dev-cmd/pr-automerge_spec.rb b/Library/Homebrew/test/dev-cmd/pr-automerge_spec.rb index 261df7db89..8fda0193f3 100644 --- a/Library/Homebrew/test/dev-cmd/pr-automerge_spec.rb +++ b/Library/Homebrew/test/dev-cmd/pr-automerge_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/pr-automerge" -RSpec.describe "brew pr-automerge" do +RSpec.describe Homebrew::DevCmd::PrAutomerge do it_behaves_like "parseable arguments" end diff --git a/Library/Homebrew/test/dev-cmd/pr-publish_spec.rb b/Library/Homebrew/test/dev-cmd/pr-publish_spec.rb index 91dbc30a9c..66384ab898 100644 --- a/Library/Homebrew/test/dev-cmd/pr-publish_spec.rb +++ b/Library/Homebrew/test/dev-cmd/pr-publish_spec.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true require "cmd/shared_examples/args_parse" +require "dev-cmd/pr-publish" -RSpec.describe "brew pr-publish" do +RSpec.describe Homebrew::DevCmd::PrPublish do it_behaves_like "parseable arguments" end