Enable types in dev-cmd

This commit is contained in:
Douglas Eichelberger 2023-03-13 18:31:26 -07:00
parent d993167fae
commit f4c9a96c70
12 changed files with 118 additions and 120 deletions

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module Cask

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "formula"
@ -22,10 +22,8 @@ require "tap_auditor"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def audit_args
def self.audit_args
Homebrew::CLI::Parser.new do
description <<~EOS
Check <formula> for Homebrew coding style violations. This should be run before
@ -103,7 +101,7 @@ module Homebrew
end
sig { void }
def audit
def self.audit
args = audit_args.parse
Homebrew.auditing = true
@ -117,7 +115,7 @@ module Homebrew
strict = new_formula || args.strict?
online = new_formula || args.online?
skip_style = args.skip_style? || args.no_named? || args.tap
no_named_args = false
no_named_args = T.let(false, T::Boolean)
ENV.activate_extensions!
ENV.setup_build_environment
@ -195,7 +193,7 @@ module Homebrew
# load licenses
spdx_license_data = SPDX.license_data
spdx_exception_data = SPDX.exception_data
new_formula_problem_lines = []
new_formula_problem_lines = T.let([], T::Array[String])
formula_results = audit_formulae.sort.to_h do |f|
only = only_cops ? ["style"] : args.only
options = {
@ -320,16 +318,16 @@ module Homebrew
end
end
def format_problem_lines(problems)
def self.format_problem_lines(problems)
problems.uniq
.map { |message:, location:| format_problem(message, location) }
end
def format_problem(message, location)
def self.format_problem(message, location)
"* #{location&.to_s&.dup&.concat(": ")}#{message.chomp.gsub("\n", "\n ")}"
end
def without_api(&block)
def self.without_api(&block)
return yield if Homebrew::EnvConfig.no_install_from_api?
with_env(HOMEBREW_NO_INSTALL_FROM_API: "1", HOMEBREW_AUTOMATICALLY_SET_NO_INSTALL_FROM_API: "1", &block)

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "formula"
@ -38,10 +38,8 @@ ALLOWABLE_HOMEBREW_REPOSITORY_LINKS = [
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def bottle_args
def self.bottle_args
Homebrew::CLI::Parser.new do
description <<~EOS
Generate a bottle (binary package) from a formula that was installed with
@ -94,7 +92,7 @@ module Homebrew
end
end
def bottle
def self.bottle
args = bottle_args.parse
if args.merge?
@ -107,7 +105,7 @@ module Homebrew
end
end
def keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil, args:)
def self.keg_contain?(string, keg, ignores, formula_and_runtime_deps_names = nil, args:)
@put_string_exists_header, @put_filenames = nil
print_filename = lambda do |str, filename|
@ -124,7 +122,7 @@ module Homebrew
@put_filenames << filename
end
result = false
result = T.let(false, T::Boolean)
keg.each_unique_file_matching(string) do |file|
next if Metafiles::EXTENSIONS.include?(file.extname) # Skip document files.
@ -157,7 +155,7 @@ module Homebrew
keg_contain_absolute_symlink_starting_with?(string, keg, args: args) || result
end
def keg_contain_absolute_symlink_starting_with?(string, keg, args:)
def self.keg_contain_absolute_symlink_starting_with?(string, keg, args:)
absolute_symlinks_start_with_string = []
keg.find do |pn|
next if !pn.symlink? || !(link = pn.readlink).absolute?
@ -175,7 +173,7 @@ module Homebrew
!absolute_symlinks_start_with_string.empty?
end
def cellar_parameter_needed?(cellar)
def self.cellar_parameter_needed?(cellar)
default_cellars = [
Homebrew::DEFAULT_MACOS_CELLAR,
Homebrew::DEFAULT_MACOS_ARM_CELLAR,
@ -184,7 +182,7 @@ module Homebrew
cellar.present? && default_cellars.exclude?(cellar)
end
def generate_sha256_line(tag, digest, cellar, tag_column, digest_column)
def self.generate_sha256_line(tag, digest, cellar, tag_column, digest_column)
line = "sha256 "
tag_column += line.length
digest_column += line.length
@ -199,7 +197,7 @@ module Homebrew
%Q(#{line}"#{digest}")
end
def bottle_output(bottle, root_url_using)
def self.bottle_output(bottle, root_url_using)
cellars = bottle.checksums.map do |checksum|
cellar = checksum["cellar"]
next unless cellar_parameter_needed? cellar
@ -227,13 +225,13 @@ module Homebrew
erb.result(erb_binding).gsub(/^\s*$\n/, "")
end
def sudo_purge
def self.sudo_purge
return unless ENV["HOMEBREW_BOTTLE_SUDO_PURGE"]
system "/usr/bin/sudo", "--non-interactive", "/usr/sbin/purge"
end
def setup_tar_and_args!(args, mtime)
def self.setup_tar_and_args!(args, mtime)
# Without --only-json-tab bottles are never reproducible
default_tar_args = ["tar", [].freeze].freeze
return default_tar_args unless args.only_json_tab?
@ -261,7 +259,7 @@ module Homebrew
["#{gnu_tar.opt_bin}/gtar", gnutar_args].freeze
end
def formula_ignores(formula)
def self.formula_ignores(formula)
ignores = []
cellar_regex = Regexp.escape(HOMEBREW_CELLAR)
prefix_regex = Regexp.escape(HOMEBREW_PREFIX)
@ -291,7 +289,7 @@ module Homebrew
ignores.compact
end
def bottle_formula(formula, args:)
def self.bottle_formula(formula, args:)
local_bottle_json = args.json? && formula.local_bottle_path.present?
unless local_bottle_json
@ -425,7 +423,7 @@ module Homebrew
sudo_purge
# Set filename as it affects the tarball checksum.
relocatable_tar_path = "#{formula}-bottle.tar"
mv tar_path, relocatable_tar_path
mv T.must(tar_path), relocatable_tar_path
# Use gzip, faster to compress than bzip2, faster to uncompress than bzip2
# or an uncompressed tarball (and more bandwidth friendly).
Utils::Gzip.compress_with_options(relocatable_tar_path,
@ -572,13 +570,13 @@ module Homebrew
json_path.write(JSON.pretty_generate(json))
end
def parse_json_files(filenames)
def self.parse_json_files(filenames)
filenames.map do |filename|
JSON.parse(File.read(filename))
end
end
def merge_json_files(json_files)
def self.merge_json_files(json_files)
json_files.reduce({}) do |hash, json_file|
json_file.each_value do |json_hash|
json_bottle = json_hash["bottle"]
@ -591,7 +589,7 @@ module Homebrew
end
end
def merge(args:)
def self.merge(args:)
bottles_hash = merge_json_files(parse_json_files(args.named))
any_cellars = ["any", "any_skip_relocation"]
@ -660,7 +658,7 @@ module Homebrew
end
end
all_bottle_hash = nil
all_bottle_hash = T.let(nil, T.nilable(Hash))
bottle_hash["bottle"]["tags"].each do |tag, tag_hash|
filename = Bottle::Filename.new(
formula_name,
@ -745,7 +743,7 @@ module Homebrew
end
end
def merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
def self.merge_bottle_spec(old_keys, old_bottle_spec, new_bottle_hash)
mismatches = []
checksums = []
@ -785,12 +783,12 @@ module Homebrew
[mismatches, checksums]
end
def old_checksums(formula, formula_ast, bottle_hash, args:)
def self.old_checksums(formula, formula_ast, bottle_hash, args:)
bottle_node = formula_ast.bottle_block
return if bottle_node.nil?
return [] unless args.keep_old?
old_keys = Utils::AST.body_children(bottle_node.body).map(&:method_name)
old_keys = T.cast(Utils::AST.body_children(bottle_node.body), T::Array[RuboCop::AST::SendNode]).map(&:method_name)
old_bottle_spec = formula.bottle_specification
mismatches, checksums = merge_bottle_spec(old_keys, old_bottle_spec, bottle_hash["bottle"])
if mismatches.present?

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cask"
@ -158,7 +158,7 @@ module Homebrew
m = /^ +url "(.+?)"\n/m.match(old_contents)
odie "Could not find old URL in cask!" if m.nil?
old_base_url = m.captures.first
old_base_url = m.captures.fetch(0)
replacement_pairs << [
/#{Regexp.escape(old_base_url)}/,

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cli/parser"
@ -6,10 +6,8 @@ require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def cat_args
def self.cat_args
Homebrew::CLI::Parser.new do
description <<~EOS
Display the source of a <formula> or <cask>.
@ -26,40 +24,41 @@ module Homebrew
end
end
def cat
def self.cat
args = cat_args.parse
cd HOMEBREW_REPOSITORY
pager = if Homebrew::EnvConfig.bat?
ENV["BAT_CONFIG_PATH"] = Homebrew::EnvConfig.bat_config_path
ENV["BAT_THEME"] = Homebrew::EnvConfig.bat_theme
ensure_formula_installed!(
"bat",
reason: "displaying <formula>/<cask> source",
# The user might want to capture the output of `brew cat ...`
# Redirect stdout to stderr
output_to_stderr: true,
).opt_bin/"bat"
else
"cat"
cd HOMEBREW_REPOSITORY do
pager = if Homebrew::EnvConfig.bat?
ENV["BAT_CONFIG_PATH"] = Homebrew::EnvConfig.bat_config_path
ENV["BAT_THEME"] = Homebrew::EnvConfig.bat_theme
ensure_formula_installed!(
"bat",
reason: "displaying <formula>/<cask> source",
# The user might want to capture the output of `brew cat ...`
# Redirect stdout to stderr
output_to_stderr: true,
).opt_bin/"bat"
else
"cat"
end
args.named.to_paths.each do |path|
next path if path.exist?
path = path.basename(".rb") if args.cask?
ofail "#{path}'s source doesn't exist on disk."
end
if Homebrew.failed?
$stderr.puts "The name may be wrong, or the tap hasn't been tapped. Instead try:"
treat_as = "--cask " if args.cask?
treat_as = "--formula " if args.formula?
$stderr.puts " brew info --github #{treat_as}#{args.named.join(" ")}"
return
end
safe_system pager, *args.named.to_paths
end
args.named.to_paths.each do |path|
next path if path.exist?
path = path.basename(".rb") if args.cask?
ofail "#{path}'s source doesn't exist on disk."
end
if Homebrew.failed?
$stderr.puts "The name may be wrong, or the tap hasn't been tapped. Instead try:"
treat_as = "--cask " if args.cask?
treat_as = "--formula " if args.formula?
$stderr.puts " brew info --github #{treat_as}#{args.named.join(" ")}"
return
end
safe_system pager, *args.named.to_paths
end
end

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cli/parser"
@ -8,29 +8,30 @@ require "software_spec"
require "tap"
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
alias_method :old_method_missing, :method_missing if method_defined?(:method_missing)
T.unsafe(self).alias_method :old_method_missing, :method_missing if method_defined?(:method_missing)
define_method(:method_missing) do |*|
# do nothing
end
end
Module.class_eval do
alias_method :old_method_missing, :method_missing if method_defined?(:method_missing)
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
alias_method :old_method_missing, :method_missing if method_defined?(:method_missing)
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
alias_method :old_parse_symbol_spec, :parse_symbol_spec if method_defined?(:parse_symbol_spec)
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
@ -40,28 +41,28 @@ def with_monkey_patch
ensure
BottleSpecification.class_eval do
if method_defined?(:old_method_missing)
alias_method :method_missing, :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)
alias_method :method_missing, :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)
alias_method :method_missing, :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)
alias_method :parse_symbol_spec, :old_parse_symbol_spec
T.unsafe(self).alias_method :parse_symbol_spec, :old_parse_symbol_spec
undef :old_parse_symbol_spec
end
end
@ -70,12 +71,10 @@ end
module Homebrew
extend T::Sig
module_function
BOTTLE_BLOCK_REGEX = / bottle (?:do.+?end|:[a-z]+)\n\n/m.freeze
sig { returns(CLI::Parser) }
def extract_args
def self.extract_args
Homebrew::CLI::Parser.new do
usage_banner "`extract` [`--version=`] [`--force`] <formula> <tap>"
description <<~EOS
@ -95,7 +94,7 @@ module Homebrew
end
end
def extract
def self.extract
args = extract_args.parse
if (match = args.named.first.match(HOMEBREW_TAP_FORMULA_REGEX))
@ -126,8 +125,8 @@ module Homebrew
ohai "Searching repository history"
version = args.version
version_segments = Gem::Version.new(version).segments if Gem::Version.correct?(version)
rev = nil
test_formula = nil
rev = T.let(nil, T.nilable(String))
test_formula = T.let(nil, T.nilable(Formula))
result = ""
loop do
rev = rev.nil? ? "HEAD" : "#{rev}~1"
@ -166,7 +165,7 @@ module Homebrew
else
# Search in the root directory of <repo> as well as recursively in all of its subdirectories
files = Dir[repo/"{,**/}"].map do |dir|
Pathname.glob(["#{dir}/#{name}.rb"]).find(&:file?)
Pathname.glob("#{dir}/#{name}.rb").find(&:file?)
end.compact
if files.empty?
@ -174,11 +173,11 @@ module Homebrew
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 = formula_at_revision(repo, name, file, rev).version
version = T.must(formula_at_revision(repo, name, file, rev)).version
result = Utils::Git.last_revision_of_file(repo, file)
else
file = files.first.realpath
rev = "HEAD"
file = files.fetch(0).realpath
rev = T.let("HEAD", T.nilable(String))
version = Formulary.factory(file).version
result = File.read(file)
end
@ -214,7 +213,8 @@ module Homebrew
end
# @private
def formula_at_revision(repo, name, file, rev)
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?
contents = Utils::Git.last_revision_of_file(repo, file, before_commit: rev)

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "cli/parser"
@ -51,7 +51,7 @@ module Homebrew
url_match = arg.match HOMEBREW_PULL_OR_COMMIT_URL_REGEX
_, user, repo, issue = *url_match
odie "Not a GitHub pull request: #{arg}" unless issue
if args.tap.present? && !"#{user}/#{repo}".casecmp(tap.full_name).zero?
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

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "download_strategy"
@ -10,10 +10,8 @@ require "formula"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def pr_pull_args
def self.pr_pull_args
Homebrew::CLI::Parser.new do
description <<~EOS
Download and publish bottles, and apply the bottle commit from a
@ -69,7 +67,7 @@ module Homebrew
end
# Separates a commit message into subject, body, and trailers.
def separate_commit_message(message)
def self.separate_commit_message(message)
subject = message.lines.first.strip
# Skip the subject and separate lines that look like trailers (e.g. "Co-authored-by")
@ -82,7 +80,7 @@ module Homebrew
[subject, body, trailers]
end
def signoff!(path, pr: nil, dry_run: false)
def self.signoff!(path, pr: nil, dry_run: false)
subject, body, trailers = separate_commit_message(path.git_commit_message)
if pr
@ -108,7 +106,7 @@ module Homebrew
end
end
def get_package(tap, subject_name, subject_path, content)
def self.get_package(tap, subject_name, subject_path, content)
if subject_path.dirname == tap.cask_dir
cask = begin
Cask::CaskLoader.load(content.dup)
@ -125,7 +123,7 @@ module Homebrew
end
end
def determine_bump_subject(old_contents, new_contents, subject_path, reason: nil)
def self.determine_bump_subject(old_contents, new_contents, subject_path, reason: nil)
subject_path = Pathname(subject_path)
tap = Tap.from_path(subject_path)
subject_name = subject_path.basename.to_s.chomp(".rb")
@ -153,7 +151,7 @@ module Homebrew
# Cherry picks a single commit that modifies a single file.
# Potentially rewords this commit using {determine_bump_subject}.
def reword_package_commit(commit, file, reason: "", verbose: false, resolve: false, path: ".")
def self.reword_package_commit(commit, file, reason: "", verbose: false, resolve: false, path: ".")
package_file = Pathname.new(path) / file
package_name = package_file.basename.to_s.chomp(".rb")
@ -178,7 +176,7 @@ module Homebrew
# Cherry picks multiple commits that each modify a single file.
# Words the commit according to {determine_bump_subject} with the body
# corresponding to all the original commit messages combined.
def squash_package_commits(commits, file, reason: "", verbose: false, resolve: false, path: ".")
def self.squash_package_commits(commits, file, reason: "", verbose: false, resolve: false, path: ".")
odebug "Squashing #{file}: #{commits.join " "}"
# Format commit messages into something similar to `git fmt-merge-message`.
@ -223,7 +221,7 @@ module Homebrew
ohai bump_subject
end
def autosquash!(original_commit, tap:, reason: "", verbose: false, resolve: false)
def self.autosquash!(original_commit, tap:, reason: "", verbose: false, resolve: false)
original_head = tap.path.git_head
commits = Utils.safe_popen_read("git", "-C", tap.path, "rev-list",
@ -257,7 +255,7 @@ module Homebrew
# Iterate over every commit in the pull request series, but if we have to squash
# multiple commits into one, ensure that we skip over commits we've already squashed.
processed_commits = []
processed_commits = T.let([], T::Array[String])
commits.each do |commit|
next if processed_commits.include? commit
@ -288,7 +286,7 @@ module Homebrew
raise
end
def cherry_pick_pr!(user, repo, pr, args:, path: ".")
def self.cherry_pick_pr!(user, repo, pr, args:, path: ".")
if args.dry_run?
puts <<~EOS
git fetch --force origin +refs/pull/#{pr}/head
@ -304,7 +302,7 @@ module Homebrew
Utils::Git.cherry_pick!(path, "--ff", "--allow-empty", *commits, verbose: args.verbose?, resolve: args.resolve?)
end
def formulae_need_bottles?(tap, original_commit, user, repo, pr, args:)
def self.formulae_need_bottles?(tap, original_commit, user, repo, pr, args:)
return if args.dry_run?
labels = GitHub.pull_request_labels(user, repo, pr)
@ -316,7 +314,7 @@ module Homebrew
end
end
def changed_packages(tap, original_commit)
def self.changed_packages(tap, original_commit)
formulae = Utils.popen_read("git", "-C", tap.path, "diff-tree",
"-r", "--name-only", "--diff-filter=AM",
original_commit, "HEAD", "--", tap.formula_dir)
@ -352,7 +350,7 @@ module Homebrew
formulae + casks
end
def download_artifact(url, dir, pr)
def self.download_artifact(url, dir, pr)
odie "Credentials must be set to access the Artifacts API" if GitHub::API.credentials_type == :none
token = GitHub::API.credentials
@ -368,7 +366,7 @@ module Homebrew
end
end
def pr_check_conflicts(repo, pr)
def self.pr_check_conflicts(repo, pr)
long_build_pr_files = GitHub.issues(
repo: repo, state: "open", labels: "no long build conflict",
).each_with_object({}) do |long_build_pr, hash|
@ -415,7 +413,7 @@ module Homebrew
EOS
end
def pr_pull
def self.pr_pull
args = pr_pull_args.parse
# Needed when extracting the CI artifact.

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "extend/ENV"
@ -38,7 +38,7 @@ module Homebrew
ENV.setup_build_environment
if superenv?(args.env)
# superenv stopped adding brew's bin but generally users will want it
ENV["PATH"] = PATH.new(ENV.fetch("PATH")).insert(1, HOMEBREW_PREFIX/"bin")
ENV["PATH"] = PATH.new(ENV.fetch("PATH")).insert(1, HOMEBREW_PREFIX/"bin").to_s
end
ENV["VERBOSE"] = "1" if args.verbose?

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "tap"
@ -7,10 +7,8 @@ require "cli/parser"
module Homebrew
extend T::Sig
module_function
sig { returns(CLI::Parser) }
def tap_new_args
def self.tap_new_args
Homebrew::CLI::Parser.new do
usage_banner "`tap-new` [<options>] <user>`/`<repo>"
description <<~EOS
@ -31,7 +29,7 @@ module Homebrew
end
end
def tap_new
def self.tap_new
args = tap_new_args.parse
label = args.pull_label || "pr-pull"
@ -176,7 +174,7 @@ module Homebrew
EOS
end
def write_path(tap, filename, content)
def self.write_path(tap, filename, content)
path = tap.path/filename
tap.path.mkpath
odie "#{path} already exists" if path.exist?

View File

@ -79,6 +79,7 @@ module Kernel
Homebrew.failed = true
end
sig { params(error: T.any(String, Exception)).returns(T.noreturn) }
def odie(error)
onoe error
exit 1

View File

@ -7,6 +7,8 @@ module Utils
# @see GitRepositoryExtension
# @api private
module Git
extend T::Sig
module_function
def available?
@ -76,6 +78,10 @@ module Utils
[rev, paths]
end
sig {
params(repo: T.any(Pathname, String), file: T.any(Pathname, String), before_commit: T.nilable(String))
.returns(String)
}
def last_revision_of_file(repo, file, before_commit: nil)
relative_file = Pathname(file).relative_path_from(repo)
commit_hash = last_revision_commit_of_file(repo, relative_file, before_commit: before_commit)