Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add typing for Cask#url and fix detected issues #18139

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 59 additions & 43 deletions Library/Homebrew/cask/audit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
include ::Utils::Curl
extend Attrable

attr_reader :cask, :download
sig { returns(Cask) }
attr_reader :cask

sig { returns(T.nilable(Download)) }
attr_reader :download

attr_predicate :new_cask?, :strict?, :signing?, :online?, :token_conflicts?

Expand Down Expand Up @@ -293,45 +297,45 @@
def audit_hosting_with_livecheck(livecheck_result: audit_livecheck_version)
return if cask.deprecated? || cask.disabled?
return if cask.version&.latest?
return unless cask.url
return if (url = cask.url).nil?
return if block_url_offline?
return if cask.livecheckable?
return if livecheck_result == :auto_detected

add_livecheck = "please add a livecheck. See #{Formatter.url(LIVECHECK_REFERENCE_URL)}"

case cask.url.to_s
case url.to_s
when %r{sourceforge.net/(\S+)}
return unless online?

add_error "Download is hosted on SourceForge, #{add_livecheck}", location: cask.url.location
add_error "Download is hosted on SourceForge, #{add_livecheck}", location: url.location
when %r{dl.devmate.com/(\S+)}
add_error "Download is hosted on DevMate, #{add_livecheck}", location: cask.url.location
add_error "Download is hosted on DevMate, #{add_livecheck}", location: url.location
when %r{rink.hockeyapp.net/(\S+)}
add_error "Download is hosted on HockeyApp, #{add_livecheck}", location: cask.url.location
add_error "Download is hosted on HockeyApp, #{add_livecheck}", location: url.location
end
end

SOURCEFORGE_OSDN_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#sourceforgeosdn-urls"

sig { void }
def audit_download_url_format
return unless cask.url
return if (url = cask.url).nil?
return if block_url_offline?

odebug "Auditing URL format"
return unless bad_sourceforge_url?

add_error "SourceForge URL format incorrect. See #{Formatter.url(SOURCEFORGE_OSDN_REFERENCE_URL)}",
location: cask.url.location
location: url.location
end

def audit_download_url_is_osdn
return unless cask.url
return if (url = cask.url).nil?
return if block_url_offline?
return unless bad_osdn_url?

add_error "OSDN download urls are disabled.", location: cask.url.location, strict_only: true
add_error "OSDN download urls are disabled.", location: url.location, strict_only: true
end

VERIFIED_URL_REFERENCE_URL = "https://docs.brew.sh/Cask-Cookbook#when-url-and-homepage-domains-differ-add-verified"
Expand Down Expand Up @@ -364,15 +368,15 @@

sig { void }
def audit_no_match
return unless cask.url
return if (url = cask.url).nil?
return if block_url_offline?
return unless verified_present?
return if verified_matches_url?

add_error "Verified URL #{Formatter.url(url_from_verified)} does not match URL " \
"#{Formatter.url(strip_url_scheme(cask.url.to_s))}. " \
"#{Formatter.url(strip_url_scheme(url.to_s))}. " \
"See #{Formatter.url(VERIFIED_URL_REFERENCE_URL)}",
location: cask.url.location
location: url.location
end

sig { void }
Expand Down Expand Up @@ -446,31 +450,31 @@

sig { void }
def audit_download
return if download.blank? || cask.url.blank?
return if (download = self.download).blank? || (url = cask.url).nil?

begin
download.fetch
rescue => e
add_error "download not possible: #{e}", location: cask.url.location
add_error "download not possible: #{e}", location: url.location
end
end

sig { void }
def audit_livecheck_unneeded_long_version
return if cask.version.nil? || cask.url.nil?
return if cask.version.nil? || (url = cask.url).nil?
return if cask.livecheck.strategy != :sparkle
return unless cask.version.csv.second
return if cask.url.to_s.include? cask.version.csv.second
return if cask.version.csv.third.present? && cask.url.to_s.include?(cask.version.csv.third)

add_error "Download does not require additional version components. Use `&:short_version` in the livecheck",
location: cask.url.location,
location: url.location,
strict_only: true
end

sig { void }
def audit_signing
return if !signing? || download.blank? || cask.url.blank?
return if !signing? || download.blank? || (url = cask.url).nil?

odebug "Auditing signing"

Expand All @@ -492,12 +496,12 @@
when Artifact::Binary
system_command("codesign", args: ["--verify", path], print_stderr: false)
else
add_error "Unknown artifact type: #{artifact.class}", location: cask.url.location
add_error "Unknown artifact type: #{artifact.class}", location: url.location
end

next if result.success?

add_error <<~EOS, location: cask.url.location, strict_only: true
add_error <<~EOS, location: url.location, strict_only: true

Check warning on line 504 in Library/Homebrew/cask/audit.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/audit.rb#L504

Added line #L504 was not covered by tests
Signature verification failed:
#{result.merged_output}
macOS on ARM requires software to be signed.
Expand All @@ -510,6 +514,7 @@
sig { void }
def extract_artifacts
return unless online?
return if (download = self.download).nil?

artifacts = cask.artifacts.select do |artifact|
artifact.is_a?(Artifact::Pkg) || artifact.is_a?(Artifact::App) || artifact.is_a?(Artifact::Binary)
Expand Down Expand Up @@ -554,6 +559,7 @@

sig { void }
def audit_rosetta
return if (url = cask.url).nil?
return unless online?
return if Homebrew::SimulateSystem.current_arch != :arm

Expand All @@ -573,7 +579,7 @@
files = Dir[path/"Contents/MacOS/*"].select do |f|
File.executable?(f) && !File.directory?(f) && !f.end_with?(".dylib")
end
add_error "No binaries in App: #{artifact.source}", location: cask.url.location if files.empty?
add_error "No binaries in App: #{artifact.source}", location: url.location if files.empty?

main_binary = get_plist_main_binary(path)
main_binary ||= files.first
Expand All @@ -583,7 +589,7 @@
binary_path = path.to_s.gsub(cask.appdir, tmpdir)
system_command("lipo", args: ["-archs", binary_path], print_stderr: true)
else
add_error "Unknown artifact type: #{artifact.class}", location: cask.url.location
add_error "Unknown artifact type: #{artifact.class}", location: url.location
end

# binary stanza can contain shell scripts, so we just continue if lipo fails.
Expand All @@ -593,7 +599,7 @@

unless /arm64|x86_64/.match?(result.merged_output)
add_error "Artifacts architecture is no longer supported by macOS!",
location: cask.url.location
location: url.location
next
end

Expand All @@ -602,10 +608,10 @@

if supports_arm && mentions_rosetta
add_error "Artifacts does not require Rosetta 2 but the caveats say otherwise!",
location: cask.url.location
location: url.location
elsif !supports_arm && !mentions_rosetta
add_error "Artifacts require Rosetta 2 but this is not indicated by the caveats!",
location: cask.url.location
location: url.location
end
end
end
Expand Down Expand Up @@ -660,7 +666,8 @@

return if min_os.nil? || min_os <= HOMEBREW_MACOS_OLDEST_ALLOWED

cask_min_os = [cask.on_system_block_min_os, cask.depends_on.macos&.minimum_version].compact.max
on_system_block_min_os = cask.on_system_block_min_os

Check warning on line 669 in Library/Homebrew/cask/audit.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/audit.rb#L669

Added line #L669 was not covered by tests
cask_min_os = [on_system_block_min_os, cask.depends_on.macos&.minimum_version].compact.max
odebug "Declared minimum OS version: #{cask_min_os&.to_sym}"
return if cask_min_os&.to_sym == min_os.to_sym
return if cask.on_system_blocks_exist? &&
Expand All @@ -669,8 +676,8 @@
cask_min_os < MacOSVersion.new("11")

min_os_definition = if cask_min_os.present?
if cask.on_system_block_min_os.present? &&
cask.on_system_block_min_os > cask.depends_on.macos&.minimum_version
if on_system_block_min_os.present? &&
on_system_block_min_os > cask.depends_on.macos&.minimum_version
"a block with a minimum OS version of #{cask_min_os.to_sym.inspect}"
else
cask_min_os.to_sym.inspect
Expand Down Expand Up @@ -761,47 +768,53 @@

sig { void }
def audit_github_prerelease_version
return if (url = cask.url).nil?

odebug "Auditing GitHub prerelease"
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil? || repo.nil?

tag = SharedAudits.github_tag_from_url(cask.url)
tag = SharedAudits.github_tag_from_url(url.to_s)

Check warning on line 777 in Library/Homebrew/cask/audit.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/audit.rb#L777

Added line #L777 was not covered by tests
tag ||= cask.version
error = SharedAudits.github_release(user, repo, tag, cask:)
add_error error, location: cask.url.location if error
add_error error, location: url.location if error
end

sig { void }
def audit_gitlab_prerelease_version
return if (url = cask.url).nil?

user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil? || repo.nil?

odebug "Auditing GitLab prerelease"

tag = SharedAudits.gitlab_tag_from_url(cask.url)
tag = SharedAudits.gitlab_tag_from_url(url.to_s)

Check warning on line 792 in Library/Homebrew/cask/audit.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/cask/audit.rb#L792

Added line #L792 was not covered by tests
tag ||= cask.version
error = SharedAudits.gitlab_release(user, repo, tag, cask:)
add_error error, location: cask.url.location if error
add_error error, location: url.location if error
end

sig { void }
def audit_github_repository_archived
# Deprecated/disabled casks may have an archived repository.
return if cask.deprecated? || cask.disabled?
return if (url = cask.url).nil?

user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil? || repo.nil?

metadata = SharedAudits.github_repo_data(user, repo)
return if metadata.nil?

add_error "GitHub repo is archived", location: cask.url.location if metadata["archived"]
add_error "GitHub repo is archived", location: url.location if metadata["archived"]
end

sig { void }
def audit_gitlab_repository_archived
# Deprecated/disabled casks may have an archived repository.
return if cask.deprecated? || cask.disabled?
return if (url = cask.url).nil?

user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil? || repo.nil?
Expand All @@ -811,46 +824,49 @@
metadata = SharedAudits.gitlab_repo_data(user, repo)
return if metadata.nil?

add_error "GitLab repo is archived", location: cask.url.location if metadata["archived"]
add_error "GitLab repo is archived", location: url.location if metadata["archived"]
end

sig { void }
def audit_github_repository
return unless new_cask?
return if (url = cask.url).nil?

user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*})
return if user.nil? || repo.nil?

odebug "Auditing GitHub repo"

error = SharedAudits.github(user, repo)
add_error error, location: cask.url.location if error
add_error error, location: url.location if error
end

sig { void }
def audit_gitlab_repository
return unless new_cask?
return if (url = cask.url).nil?

user, repo = get_repo_data(%r{https?://gitlab\.com/([^/]+)/([^/]+)/?.*})
return if user.nil? || repo.nil?

odebug "Auditing GitLab repo"

error = SharedAudits.gitlab(user, repo)
add_error error, location: cask.url.location if error
add_error error, location: url.location if error
end

sig { void }
def audit_bitbucket_repository
return unless new_cask?
return if (url = cask.url).nil?

user, repo = get_repo_data(%r{https?://bitbucket\.org/([^/]+)/([^/]+)/?.*})
return if user.nil? || repo.nil?

odebug "Auditing Bitbucket repo"

error = SharedAudits.bitbucket(user, repo)
add_error error, location: cask.url.location if error
add_error error, location: url.location if error
end

sig { void }
Expand Down Expand Up @@ -899,9 +915,9 @@

validate_url_for_https_availability(
url, "binary URL",
location: cask.url.location,
user_agents: [cask.url.user_agent],
referer: cask.url&.referer
location: url.location,
user_agents: [url.user_agent],
referer: url.referer
)
end

Expand Down Expand Up @@ -1025,7 +1041,7 @@

# sig { returns(String) }
def url_from_verified
strip_url_scheme(cask.url.verified)
strip_url_scheme(T.must(cask.url).verified)
end

sig { returns(T::Boolean) }
Expand All @@ -1039,7 +1055,7 @@

sig { returns(T::Boolean) }
def verified_present?
cask.url.verified.present?
cask.url&.verified.present?
end

sig { returns(T::Boolean) }
Expand All @@ -1051,7 +1067,7 @@
def block_url_offline?
return false if online?

cask.url.from_block?
!!cask.url&.from_block?
end

sig { returns(Tap) }
Expand Down
2 changes: 2 additions & 0 deletions Library/Homebrew/cask/cask.rb
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ def config_path
end

def checksumable?
return false if (url = self.url).nil?

DownloadStrategyDetector.detect(url.to_s, url.using) <= AbstractFileDownloadStrategy
end

Expand Down
4 changes: 4 additions & 0 deletions Library/Homebrew/cask/cask.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,14 @@ module Cask

def on_system_blocks_exist?; end

sig { returns(T.nilable(MacOSVersion)) }
def on_system_block_min_os; end

def sha256; end

def staged_path; end

sig { returns(T.nilable(::Cask::URL)) }
def url; end

def version; end
Expand Down
Loading