
"brew audit" will now warn about commented-out dependencies. Updated formulae that had these to either take them out or rewrite the comments around them.
209 lines
5.3 KiB
Ruby
Executable File
209 lines
5.3 KiB
Ruby
Executable File
require 'formula'
|
|
require 'utils'
|
|
|
|
def ff
|
|
return Formula.all if ARGV.named.empty?
|
|
return ARGV.formulae
|
|
end
|
|
|
|
def audit_formula_text text
|
|
problems = []
|
|
|
|
# Commented-out cmake support from default template
|
|
if text =~ /# depends_on 'cmake'/
|
|
problems << " * Commented cmake support found."
|
|
end
|
|
|
|
# 2 (or more in an if block) spaces before depends_on, please
|
|
if text =~ /^\ ?depends_on/
|
|
problems << " * Check indentation of 'depends_on'."
|
|
end
|
|
|
|
# FileUtils is included in Formula
|
|
if text =~ /FileUtils\.(\w+)/
|
|
problems << " * Don't need 'FileUtils.' before #{$1}."
|
|
end
|
|
|
|
# Check for long inreplace block vars
|
|
if text =~ /inreplace .* do \|(.{2,})\|/
|
|
problems << " * \"inreplace <filenames> do |s|\" is preferred over \"|#{$1}|\"."
|
|
end
|
|
|
|
# Check for string interpolation of single values.
|
|
if text =~ /(system|inreplace|gsub!|change_make_var!) .* ['"]#\{(\w+)\}['"]/
|
|
problems << " * Don't need to interpolate \"#{$2}\" with #{$1}"
|
|
end
|
|
|
|
# Check for string concatenation; prefer interpolation
|
|
if text =~ /(#\{\w+\s*\+\s*['"][^}]+\})/
|
|
problems << " * Try not to concatenate paths in string interpolation:\n #{$1}"
|
|
end
|
|
|
|
# Prefer formula path shortcuts in Pathname+
|
|
if text =~ %r{\(\s*(prefix\s*\+\s*(['"])(bin|include|lib|libexec|sbin|share))}
|
|
problems << " * \"(#{$1}...#{$2})\" should be \"(#{$3}+...)\""
|
|
end
|
|
|
|
if text =~ %r[((man)\s*\+\s*(['"])(man[1-8])(['"]))]
|
|
problems << " * \"#{$1}\" should be \"#{$4}\""
|
|
end
|
|
|
|
# Prefer formula path shortcuts in strings
|
|
if text =~ %r[(\#\{prefix\}/(bin|include|lib|libexec|sbin|share))]
|
|
problems << " * \"#{$1}\" should be \"\#{#{$2}}\""
|
|
end
|
|
|
|
if text =~ %r[((\#\{prefix\}/share/man/|\#\{man\}/)(man[1-8]))]
|
|
problems << " * \"#{$1}\" should be \"\#{#{$3}}\""
|
|
end
|
|
|
|
if text =~ %r[((\#\{share\}/(man)))[/'"]]
|
|
problems << " * \"#{$1}\" should be \"\#{#{$3}}\""
|
|
end
|
|
|
|
if text =~ %r[(\#\{prefix\}/share/(info|man))]
|
|
problems << " * \"#{$1}\" should be \"\#{#{$2}}\""
|
|
end
|
|
|
|
# Empty checksums
|
|
if text =~ /md5\s+\'\'/
|
|
problems << " * md5 is empty"
|
|
end
|
|
|
|
# Commented-out depends_on
|
|
if text =~ /#\s*depends_on\s+(.+)\s*$/
|
|
problems << " * Commented-out dep #{$1}."
|
|
end
|
|
|
|
# No trailing whitespace, please
|
|
if text =~ /[ ]+$/
|
|
problems << " * Trailing whitespace was found."
|
|
end
|
|
|
|
return problems
|
|
end
|
|
|
|
def audit_formula_options f, text
|
|
problems = []
|
|
|
|
# Find possible options
|
|
options = []
|
|
text.scan(/ARGV\.include\?[ ]*\(?(['"])(.+?)\1/) { |m| options << m[1] }
|
|
options.reject! {|o| o.include? "#"}
|
|
options.uniq!
|
|
|
|
# Find documented options
|
|
begin
|
|
opts = f.options
|
|
documented_options = []
|
|
opts.each{ |o| documented_options << o[0] }
|
|
documented_options.reject! {|o| o.include? "="}
|
|
rescue
|
|
documented_options = []
|
|
end
|
|
|
|
if options.length > 0
|
|
options.each do |o|
|
|
problems << " * Option #{o} is not documented" unless documented_options.include? o
|
|
end
|
|
end
|
|
|
|
if documented_options.length > 0
|
|
documented_options.each do |o|
|
|
problems << " * Option #{o} is unused" unless options.include? o
|
|
end
|
|
end
|
|
|
|
return problems
|
|
end
|
|
|
|
def audit_formula_urls f
|
|
problems = []
|
|
|
|
# Check SourceForge urls
|
|
[(f.url rescue nil), (f.head rescue nil)].each do |p|
|
|
next if p.nil?
|
|
# Is it a filedownload (instead of svnroot)
|
|
next if p =~ %r[/svnroot/]
|
|
next if p =~ %r[svn\.sourceforge]
|
|
|
|
# Is it a sourceforge http(s) URL?
|
|
next unless p =~ %r[^http?://.*\bsourceforge\.]
|
|
|
|
if p =~ /\?use_mirror=/
|
|
problems << " * Update this url (don't use ?use_mirror)."
|
|
end
|
|
|
|
if p =~ /\/download$/
|
|
problems << " * Update this url (don't use /download)."
|
|
end
|
|
|
|
if p =~ %r[^http://prdownloads\.]
|
|
problems << " * Update this url (don't use prdownloads)."
|
|
end
|
|
|
|
if p =~ %r[^http://\w+\.dl\.]
|
|
problems << " * Update this url (don't use specific dl mirrors)."
|
|
end
|
|
end
|
|
|
|
return problems
|
|
end
|
|
|
|
def audit_formula_instance f
|
|
problems = []
|
|
|
|
# Don't depend_on aliases; use full name
|
|
aliases = Formula.aliases
|
|
f.deps.select {|d| aliases.include? d}.each do |d|
|
|
problems << " * Dep #{d} is an alias; switch to the real name."
|
|
end
|
|
|
|
# Check for things we don't like to depend on.
|
|
# We allow non-Homebrew installs whenenever possible.
|
|
f.deps.each do |d|
|
|
case d
|
|
when "git"
|
|
problems << " * Don't use Git as a dependency; we allow non-Homebrew git installs."
|
|
end
|
|
end
|
|
|
|
# Google Code homepages should end in a slash
|
|
if f.homepage =~ %r[^https?://code\.google\.com/p/[^/]+[^/]$]
|
|
problems << " * Google Code homepage should end with a slash."
|
|
end
|
|
|
|
return problems
|
|
end
|
|
|
|
def audit_some_formulae
|
|
ff.each do |f|
|
|
problems = []
|
|
|
|
problems += audit_formula_instance f
|
|
problems += audit_formula_urls f
|
|
|
|
text = ""
|
|
File.open(f.path, "r") { |afile| text = afile.read }
|
|
|
|
# DATA with no __END__
|
|
if (text =~ /\bDATA\b/) and not (text =~ /^\s*__END__\s*$/)
|
|
problems << " * 'DATA' was found, but no '__END__'"
|
|
end
|
|
|
|
# Don't try remaining audits on text in __END__
|
|
text_without_patch = (text.split("__END__")[0]).strip()
|
|
|
|
problems += audit_formula_text(text_without_patch)
|
|
problems += audit_formula_options(f, text_without_patch)
|
|
|
|
unless problems.empty?
|
|
puts "#{f.name}:"
|
|
puts problems * "\n"
|
|
puts
|
|
end
|
|
end
|
|
end
|
|
|
|
audit_some_formulae
|