diff options
| author | Mike McQuaid | 2017-02-26 20:48:36 +0000 |
|---|---|---|
| committer | Mike McQuaid | 2017-02-26 20:48:36 +0000 |
| commit | c2a460ec6d857ba33c89174d8d93fcaa403c3717 (patch) | |
| tree | 089045fec4c46fb74e3b48868e2b335497df7b84 /Library/Homebrew/dev-cmd/audit.rb | |
| parent | d3ae1cc264dc9eb9b602dd6aa21c4282dc049c79 (diff) | |
| parent | ff93e1624b214c9b48731174a9135789fc3695a8 (diff) | |
| download | brew-c2a460ec6d857ba33c89174d8d93fcaa403c3717.tar.bz2 | |
Merge branch 'master' into filter_all_env_vars_932
Diffstat (limited to 'Library/Homebrew/dev-cmd/audit.rb')
| -rw-r--r-- | Library/Homebrew/dev-cmd/audit.rb | 170 |
1 files changed, 139 insertions, 31 deletions
diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb index 88d9a535c..35d590b69 100644 --- a/Library/Homebrew/dev-cmd/audit.rb +++ b/Library/Homebrew/dev-cmd/audit.rb @@ -1,4 +1,4 @@ -#: * `audit` [`--strict`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [<formulae>]: +#: * `audit` [`--strict`] [`--fix`] [`--online`] [`--new-formula`] [`--display-cop-names`] [`--display-filename`] [<formulae>]: #: Check <formulae> for Homebrew coding style violations. This should be #: run before submitting a new formula. #: @@ -7,6 +7,9 @@ #: If `--strict` is passed, additional checks are run, including RuboCop #: style checks. #: +#: If `--fix` is passed, style violations will be +#: automatically fixed using RuboCop's `--auto-correct` feature. +#: #: If `--online` is passed, additional slower checks that require a network #: connection are run. #: @@ -36,6 +39,7 @@ require "cmd/search" require "cmd/style" require "date" require "blacklist" +require "digest" module Homebrew module_function @@ -62,8 +66,9 @@ module Homebrew end if strict + options = { fix: ARGV.flag?("--fix"), realpath: true } # Check style in a single batch run up front for performance - style_results = check_style_json(files, realpath: true) + style_results = check_style_json(files, options) end ff.each do |f| @@ -169,31 +174,64 @@ class FormulaAuditor @specs = %w[stable devel head].map { |s| formula.send(s) }.compact end - def url_status_code(url, range: false, user_agent: :default) - # The system Curl is too old and unreliable with HTTPS homepages on - # Yosemite and below. - return "200" unless DevelopmentTools.curl_handles_most_https_homepages? + def self.check_http_content(url, user_agents: [:default]) + return unless url.start_with? "http" - extra_args = [ - "--connect-timeout", "15", - "--output", "/dev/null", - "--write-out", "%{http_code}" - ] - extra_args << "--range" << "0-0" if range - extra_args << url + details = nil + user_agent = nil + user_agents.each do |ua| + details = http_content_headers_and_checksum(url, user_agent: ua) + user_agent = ua + break if details[:status].to_s.start_with?("2") + end + + return "The URL #{url} is not reachable" unless details[:status] + unless details[:status].start_with? "2" + return "The URL #{url} is not reachable (HTTP status code #{details[:status]})" + end + + return unless url.start_with? "http:" + + secure_url = url.sub "http", "https" + secure_details = + http_content_headers_and_checksum(secure_url, user_agent: user_agent) + + if !details[:status].to_s.start_with?("2") || + !secure_details[:status].to_s.start_with?("2") + return + end + + etag_match = details[:etag] && + details[:etag] == secure_details[:etag] + content_length_match = + details[:content_length] && + details[:content_length] == secure_details[:content_length] + file_match = details[:file_hash] == secure_details[:file_hash] + + return if !etag_match && !content_length_match && !file_match + "The URL #{url} could use HTTPS rather than HTTP" + end + def self.http_content_headers_and_checksum(url, user_agent: :default) args = curl_args( - extra_args: extra_args, + extra_args: ["--connect-timeout", "15", "--include", url], show_output: true, - user_agent: user_agent + user_agent: user_agent, ) - retries = 3 - status_code = nil - retries.times do - status_code = Open3.popen3(*args) { |_, stdout, _, _| stdout.read } - break if status_code.start_with? "20" + output = Open3.popen3(*args) { |_, stdout, _, _| stdout.read } + + status_code = :unknown + while status_code == :unknown || status_code.to_s.start_with?("3") + headers, _, output = output.partition("\r\n\r\n") + status_code = headers[%r{HTTP\/.* (\d+)}, 1] end - status_code + + { + status: status_code, + etag: headers[%r{ETag: ([wW]\/)?"(([^"]|\\")*)"}, 2], + content_length: headers[/Content-Length: (\d+)/, 1], + file_hash: Digest::SHA256.digest(output), + } end def audit_style @@ -291,6 +329,27 @@ class FormulaAuditor problem "File should end with a newline" unless text.trailing_newline? + versioned_formulae = Dir[formula.path.to_s.gsub(/\.rb$/, "@*.rb")] + needs_versioned_alias = !versioned_formulae.empty? && + formula.tap && + formula.aliases.grep(/.@\d/).empty? + if needs_versioned_alias + _, last_alias_version = File.basename(versioned_formulae.sort.reverse.first) + .gsub(/\.rb$/, "") + .split("@") + major, minor, = formula.version.to_s.split(".") + alias_name = if last_alias_version.split(".").length == 1 + "#{formula.name}@#{major}" + else + "#{formula.name}@#{major}.#{minor}" + end + problem <<-EOS.undent + Formula has other versions so create an alias: + cd #{formula.tap.alias_dir} + ln -s #{formula.path.to_s.gsub(formula.tap.path, "..")} #{alias_name} + EOS + end + return unless @strict present = audit_components @@ -406,7 +465,8 @@ class FormulaAuditor problem "Dependency '#{dep.name}' was renamed; use new name '#{dep_f.name}'." end - if @@aliases.include?(dep.name) + if @@aliases.include?(dep.name) && + (core_formula? || !dep_f.versioned_formula?) problem "Dependency '#{dep.name}' is an alias; use the canonical name '#{dep.to_formula.full_name}'." end @@ -459,6 +519,14 @@ class FormulaAuditor end def audit_conflicts + if formula.conflicts.any? && formula.versioned_formula? + problem <<-EOS + Versioned formulae should not use `conflicts_with`. + Use `keg_only :versioned_formula` instead. + EOS + return + end + formula.conflicts.each do |c| begin Formulary.factory(c.name) @@ -481,6 +549,10 @@ class FormulaAuditor next unless @strict + if o.name == "universal" && !Formula["wine"].recursive_dependencies.map(&:name).include?(formula.name) + problem "macOS has been 64-bit only since 10.6 so universal options are deprecated." + end + if o.name !~ /with(out)?-/ && o.name != "c++11" && o.name != "universal" problem "Options should begin with with/without. Migrate '--#{o.name}' with `deprecated_option`." end @@ -493,7 +565,7 @@ class FormulaAuditor return unless @new_formula return if formula.deprecated_options.empty? - return if formula.name.include?("@") + return if formula.versioned_formula? problem "New formulae should not use `deprecated_option`." end @@ -569,10 +641,16 @@ class FormulaAuditor # People will run into mixed content sometimes, but we should enforce and then add # exemptions as they are discovered. Treat mixed content on homepages as a bug. # Justify each exemptions with a code comment so we can keep track here. - if homepage =~ %r{^http://[^/]*github\.io/} + case homepage + when %r{^http://[^/]*\.github\.io/}, + %r{^http://[^/]*\.sourceforge\.io/} problem "Please use https:// for #{homepage}" end + if homepage =~ %r{^http://([^/]*)\.(sf|sourceforge)\.net(/|$)} + problem "#{homepage} should be `https://#{$1}.sourceforge.io/`" + end + # There's an auto-redirect here, but this mistake is incredibly common too. # Only applies to the homepage and subdomains for now, not the FTP URLs. if homepage =~ %r{^http://((?:build|cloud|developer|download|extensions|git|glade|help|library|live|nagios|news|people|projects|rt|static|wiki|www)\.)?gnome\.org} @@ -597,9 +675,13 @@ class FormulaAuditor return unless @online - status_code = url_status_code(homepage, user_agent: :browser) - return if status_code.start_with? "20" - problem "The homepage #{homepage} is not reachable (HTTP status code #{status_code})" + # The system Curl is too old and unreliable with HTTPS homepages on + # Yosemite and below. + return unless DevelopmentTools.curl_handles_most_https_homepages? + if http_content_problem = FormulaAuditor.check_http_content(homepage, + user_agents: [:browser, :default]) + problem http_content_problem + end end def audit_bottle_spec @@ -649,11 +731,11 @@ class FormulaAuditor %w[Stable Devel HEAD].each do |name| next unless spec = formula.send(name.downcase) - ra = ResourceAuditor.new(spec).audit + ra = ResourceAuditor.new(spec, online: @online, strict: @strict).audit problems.concat ra.problems.map { |problem| "#{name}: #{problem}" } spec.resources.each_value do |resource| - ra = ResourceAuditor.new(resource).audit + ra = ResourceAuditor.new(resource, online: @online, strict: @strict).audit problems.concat ra.problems.map { |problem| "#{name} resource #{resource.name.inspect}: #{problem}" } @@ -680,6 +762,7 @@ class FormulaAuditor unstable_whitelist = %w[ aalib 1.4rc5 + angolmois 2.0.0alpha2 automysqlbackup 3.0-rc6 aview 1.3.0rc1 distcc 3.2rc1 @@ -687,6 +770,8 @@ class FormulaAuditor ftgl 2.1.3-rc5 hidapi 0.8.0-rc1 libcaca 0.99b19 + nethack4 4.3.0-beta2 + opensyobon 1.0rc2 premake 4.4-beta5 pwnat 0.3-beta pxz 4.999.9 @@ -839,7 +924,7 @@ class FormulaAuditor end end - if text =~ /xcodebuild[ (]["'*]/ && !text.include?("SYMROOT=") + if text =~ /xcodebuild[ (]*["'*]*/ && !text.include?("SYMROOT=") problem 'xcodebuild should be passed an explicit "SYMROOT"' end @@ -1200,7 +1285,7 @@ class ResourceAuditor attr_reader :problems attr_reader :version, :checksum, :using, :specs, :url, :mirrors, :name - def initialize(resource) + def initialize(resource, options = {}) @name = resource.name @version = resource.version @checksum = resource.checksum @@ -1208,6 +1293,8 @@ class ResourceAuditor @mirrors = resource.mirrors @using = resource.using @specs = resource.specs + @online = options[:online] + @strict = options[:strict] @problems = [] end @@ -1332,6 +1419,7 @@ class ResourceAuditor %r{^http://(?:[^/]*\.)?bintray\.com/}, %r{^http://tools\.ietf\.org/}, %r{^http://launchpad\.net/}, + %r{^http://github\.com/}, %r{^http://bitbucket\.org/}, %r{^http://anonscm\.debian\.org/}, %r{^http://cpan\.metacpan\.org/}, @@ -1463,6 +1551,26 @@ class ResourceAuditor next unless u =~ %r{https?://(?:central|repo\d+)\.maven\.org/maven2/(.+)$} problem "#{u} should be `https://search.maven.org/remotecontent?filepath=#{$1}`" end + + return unless @online + urls.each do |url| + next if !@strict && mirrors.include?(url) + + strategy = DownloadStrategyDetector.detect(url, using) + if strategy <= CurlDownloadStrategy && !url.start_with?("file") + if http_content_problem = FormulaAuditor.check_http_content(url) + problem http_content_problem + end + elsif strategy <= GitDownloadStrategy + unless Utils.git_remote_exists url + problem "The URL #{url} is not a valid git URL" + end + elsif strategy <= SubversionDownloadStrategy + unless Utils.svn_remote_exists url + problem "The URL #{url} is not a valid svn URL" + end + end + end end def problem(text) |
