mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00

- Previously I thought that comments were fine to discourage people from wasting their time trying to bump things that used `undef` that Sorbet didn't support. But RuboCop is better at this since it'll complain if the comments are unnecessary. - Suggested in https://github.com/Homebrew/brew/pull/18018#issuecomment-2283369501. - I've gone for a mixture of `rubocop:disable` for the files that can't be `typed: strict` (use of undef, required before everything else, etc) and `rubocop:todo` for everything else that should be tried to make strictly typed. There's no functional difference between the two as `rubocop:todo` is `rubocop:disable` with a different name. - And I entirely disabled the cop for the docs/ directory since `typed: strict` isn't going to gain us anything for some Markdown linting config files. - This means that now it's easier to track what needs to be done rather than relying on checklists of files in our big Sorbet issue: ```shell $ git grep 'typed: true # rubocop:todo Sorbet/StrictSigil' | wc -l 268 ``` - And this is confirmed working for new files: ```shell $ git status On branch use-rubocop-for-sorbet-strict-sigils Untracked files: (use "git add <file>..." to include in what will be committed) Library/Homebrew/bad.rb Library/Homebrew/good.rb nothing added to commit but untracked files present (use "git add" to track) $ brew style Offenses: bad.rb:1:1: C: Sorbet/StrictSigil: Sorbet sigil should be at least strict got true. ^^^^^^^^^^^^^ 1340 files inspected, 1 offense detected ```
186 lines
4.8 KiB
Ruby
186 lines
4.8 KiB
Ruby
# typed: true # rubocop:disable Sorbet/StrictSigil
|
|
# frozen_string_literal: true
|
|
|
|
require "ostruct"
|
|
|
|
module Homebrew
|
|
module CLI
|
|
class Args < OpenStruct
|
|
attr_reader :options_only, :flags_only
|
|
|
|
# undefine tap to allow --tap argument
|
|
undef tap
|
|
|
|
sig { void }
|
|
def initialize
|
|
require "cli/named_args"
|
|
|
|
super
|
|
|
|
@processed_options = []
|
|
@options_only = []
|
|
@flags_only = []
|
|
@cask_options = false
|
|
|
|
# Can set these because they will be overwritten by freeze_named_args!
|
|
# (whereas other values below will only be overwritten if passed).
|
|
self[:named] = NamedArgs.new(parent: self)
|
|
self[:remaining] = []
|
|
end
|
|
|
|
def freeze_remaining_args!(remaining_args)
|
|
self[:remaining] = remaining_args.freeze
|
|
end
|
|
|
|
def freeze_named_args!(named_args, cask_options:, without_api:)
|
|
options = {}
|
|
options[:force_bottle] = true if self[:force_bottle?]
|
|
options[:override_spec] = :head if self[:HEAD?]
|
|
options[:flags] = flags_only unless flags_only.empty?
|
|
self[:named] = NamedArgs.new(
|
|
*named_args.freeze,
|
|
parent: self,
|
|
cask_options:,
|
|
without_api:,
|
|
**options,
|
|
)
|
|
end
|
|
|
|
def freeze_processed_options!(processed_options)
|
|
# Reset cache values reliant on processed_options
|
|
@cli_args = nil
|
|
|
|
@processed_options += processed_options
|
|
@processed_options.freeze
|
|
|
|
@options_only = cli_args.select { |a| a.start_with?("-") }.freeze
|
|
@flags_only = cli_args.select { |a| a.start_with?("--") }.freeze
|
|
end
|
|
|
|
sig { returns(NamedArgs) }
|
|
def named
|
|
require "formula"
|
|
self[:named]
|
|
end
|
|
|
|
def no_named?
|
|
named.blank?
|
|
end
|
|
|
|
def build_from_source_formulae
|
|
if build_from_source? || self[:HEAD?] || self[:build_bottle?]
|
|
named.to_formulae.map(&:full_name)
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def include_test_formulae
|
|
if include_test?
|
|
named.to_formulae.map(&:full_name)
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def value(name)
|
|
arg_prefix = "--#{name}="
|
|
flag_with_value = flags_only.find { |arg| arg.start_with?(arg_prefix) }
|
|
return unless flag_with_value
|
|
|
|
flag_with_value.delete_prefix(arg_prefix)
|
|
end
|
|
|
|
sig { returns(Context::ContextStruct) }
|
|
def context
|
|
Context::ContextStruct.new(debug: debug?, quiet: quiet?, verbose: verbose?)
|
|
end
|
|
|
|
def only_formula_or_cask
|
|
if formula? && !cask?
|
|
:formula
|
|
elsif cask? && !formula?
|
|
:cask
|
|
end
|
|
end
|
|
|
|
sig { returns(T::Array[[Symbol, Symbol]]) }
|
|
def os_arch_combinations
|
|
skip_invalid_combinations = false
|
|
|
|
oses = case (os_sym = os&.to_sym)
|
|
when nil
|
|
[SimulateSystem.current_os]
|
|
when :all
|
|
skip_invalid_combinations = true
|
|
|
|
OnSystem::ALL_OS_OPTIONS
|
|
else
|
|
[os_sym]
|
|
end
|
|
|
|
arches = case (arch_sym = arch&.to_sym)
|
|
when nil
|
|
[SimulateSystem.current_arch]
|
|
when :all
|
|
skip_invalid_combinations = true
|
|
OnSystem::ARCH_OPTIONS
|
|
else
|
|
[arch_sym]
|
|
end
|
|
|
|
oses.product(arches).select do |os, arch|
|
|
if skip_invalid_combinations
|
|
bottle_tag = Utils::Bottles::Tag.new(system: os, arch:)
|
|
bottle_tag.valid_combination?
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def option_to_name(option)
|
|
option.sub(/\A--?/, "")
|
|
.tr("-", "_")
|
|
end
|
|
|
|
def cli_args
|
|
return @cli_args if @cli_args
|
|
|
|
@cli_args = []
|
|
@processed_options.each do |short, long|
|
|
option = long || short
|
|
switch = :"#{option_to_name(option)}?"
|
|
flag = option_to_name(option).to_sym
|
|
if @table[switch] == true || @table[flag] == true
|
|
@cli_args << option
|
|
elsif @table[flag].instance_of? String
|
|
@cli_args << "#{option}=#{@table[flag]}"
|
|
elsif @table[flag].instance_of? Array
|
|
@cli_args << "#{option}=#{@table[flag].join(",")}"
|
|
end
|
|
end
|
|
@cli_args.freeze
|
|
end
|
|
|
|
def respond_to_missing?(method_name, *)
|
|
@table.key?(method_name)
|
|
end
|
|
|
|
def method_missing(method_name, *args)
|
|
return_value = super
|
|
|
|
# Once we are frozen, verify any arg method calls are already defined in the table.
|
|
# The default OpenStruct behaviour is to return nil for anything unknown.
|
|
if frozen? && args.empty? && !@table.key?(method_name)
|
|
raise NoMethodError, "CLI arg for `#{method_name}` is not declared for this command"
|
|
end
|
|
|
|
return_value
|
|
end
|
|
end
|
|
end
|
|
end
|