From faaa1ad4b1c4e069dd6e6809391b999b9bfef586 Mon Sep 17 00:00:00 2001 From: Adam Vandenberg Date: Fri, 12 Nov 2010 21:10:23 -0800 Subject: Move brew-audit to cmds --- Library/Contributions/examples/brew-audit.rb | 243 --------------------------- Library/Homebrew/cmd/audit.rb | 238 ++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 243 deletions(-) delete mode 100755 Library/Contributions/examples/brew-audit.rb create mode 100755 Library/Homebrew/cmd/audit.rb (limited to 'Library') diff --git a/Library/Contributions/examples/brew-audit.rb b/Library/Contributions/examples/brew-audit.rb deleted file mode 100755 index 01ece830d..000000000 --- a/Library/Contributions/examples/brew-audit.rb +++ /dev/null @@ -1,243 +0,0 @@ -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'/) or (text =~ /# system "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 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 - - if text =~ /sha1\s+(\'\'|\"\")/ - problems << " * sha1 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 =~ /(\t|[ ])+$/ - problems << " * Trailing whitespace was found." - end - - if text =~ /if\s+ARGV\.include\?\s+'--HEAD'/ - problems << " * Use \"if ARGV.build_head?\" instead" - end - - if text =~ /make && make/ - problems << " * Use separate make calls." - end - - if ARGV.include? "--warn" - if text =~ /^\t/ - problems << " * Use spaces instead of tabs for indentation" - end - 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| - next if o == '--HEAD' - 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 = [] - - # To do: - # Grab URLs out of patches as well - # urls = ((f.patches rescue []) || []) - - urls = [(f.url rescue nil), (f.head rescue nil)].reject {|p| p.nil?} - - # Check SourceForge urls - urls.each do |p| - # 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 - - # Check Debian urls - urls.each do |p| - next unless p =~ %r[/debian/pool/] - - unless p =~ %r[^http://mirrors\.kernel\.org/debian/pool/] - problems << " * \"mirrors.kernel.org\" is the preferred mirror for debian software." - 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 - - problems += [' * invalid or missing version'] if f.version.to_s.empty? - - # 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 diff --git a/Library/Homebrew/cmd/audit.rb b/Library/Homebrew/cmd/audit.rb new file mode 100755 index 000000000..4096ebff8 --- /dev/null +++ b/Library/Homebrew/cmd/audit.rb @@ -0,0 +1,238 @@ +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'/) or (text =~ /# system "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 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 + + if text =~ /sha1\s+(\'\'|\"\")/ + problems << " * sha1 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 =~ /(\t|[ ])+$/ + problems << " * Trailing whitespace was found." + end + + if text =~ /if\s+ARGV\.include\?\s+'--HEAD'/ + problems << " * Use \"if ARGV.build_head?\" instead" + end + + if text =~ /make && make/ + problems << " * Use separate make calls." + end + + if ARGV.include? "--warn" + if text =~ /^\t/ + problems << " * Use spaces instead of tabs for indentation" + end + 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| + next if o == '--HEAD' + 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 = [] + + urls = [(f.url rescue nil), (f.head rescue nil)].reject {|p| p.nil?} + + # Check SourceForge urls + urls.each do |p| + # 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 + + # Check Debian urls + urls.each do |p| + next unless p =~ %r[/debian/pool/] + + unless p =~ %r[^http://mirrors\.kernel\.org/debian/pool/] + problems << " * \"mirrors.kernel.org\" is the preferred mirror for debian software." + 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 + +module Homebrew extend self +def audit + 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 + + problems += [' * invalid or missing version'] if f.version.to_s.empty? + + # 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 +end -- cgit v1.2.3