Skip to content
This repository has been archived by the owner on Sep 19, 2020. It is now read-only.

Commit

Permalink
Revert changes since 0.11.0, refs #14.
Browse files Browse the repository at this point in the history
3e48a..9750f
  • Loading branch information
Andrew Crump committed Feb 29, 2012
1 parent 66e6eea commit 36bf9d7
Show file tree
Hide file tree
Showing 11 changed files with 57 additions and 375 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ chef_dsl_metadata.json
coverage
doc
pkg
*.swp
tmp
Gemfile.lock
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ gem 'foodcritic', :path => '.'
group :test do
gem 'aruba', '~> 0.4.11'
gem 'cucumber', '~> 1.1.8'
gem 'minitest', '~> 2.11.2'
gem 'simplecov', '~> 0.5.4'
end

Expand Down
11 changes: 2 additions & 9 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
require 'rubygems'
require 'bundler'
require 'rake/testtask'
require 'cucumber'
require 'cucumber/rake/task'
require 'yard'
task :default => ['chef_dsl_metadata.json', :install, :test, :features]
task :default => ['chef_dsl_metadata.json', :install, :features]

Bundler.setup
Bundler::GemHelper.install_tasks

Rake::TestTask.new do |t|
t.pattern = 'spec/**/*_spec.rb'
end

Cucumber::Rake::Task.new(:features) do |t|
t.cucumber_opts = ['-f', 'progress']
if ENV.has_key?('FC_FORK_PROCESS') and ENV['FC_FORK_PROCESS'] == true.to_s
t.cucumber_opts += ['-t', '~@repl', 'features']
end
t.cucumber_opts += ['-t', '~@repl', 'features'] if ENV.has_key?('FC_FORK_PROCESS') and ENV['FC_FORK_PROCESS'] == true.to_s
end

YARD::Rake::YardocTask.new
25 changes: 8 additions & 17 deletions features/continuous_integration_support.feature
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,11 @@ Feature: Continuous Integration Support
And the build status should be <build_status>

Examples:
| cookbook_matches | tag_arguments | warnings_shown | build_status |
| FC002,FC003,FC004 | | FC002,FC003,FC004 | successful |
| FC002,FC003,FC004 | -t style | FC002,FC004 | successful |
| FC002,FC003,FC004 | -f style | FC002,FC003,FC004 | failed |
| FC002,FC003,FC004 | -f FC005 | FC002,FC003,FC004 | successful |
| FC002,FC003,FC004 | -f FC003,FC004 | FC002,FC003,FC004 | failed |
| FC002,FC003,FC004 | --epic-fail FC003 | FC002,FC003,FC004 | failed |
| FC002,FC003 | -f any | FC002,FC003 | failed |
| FC002,FC003 | -f any -f ~FC014 | FC002,FC003 | failed |
| FC002,FC003 | -f any,~FC014 | FC002,FC003 | failed |
| FC002 | -f ~FC002 | FC002 | successful |
| FC002,FC003 | -f ~FC002 | FC002,FC003 | failed |
| FC002,FC003 | -f any -f ~FC002 | FC002,FC003 | failed |
| FC002 | -f any,~FC002 | FC002 | failed |
| FC002 | -f any -f ~FC002 | FC002 | successful |
| FC002,FC003 | -f any,~FC002 | FC002,FC003 | failed |
| FC002,FC003 | -f ~FC002 -f ~FC004 | FC002,FC003 | failed |
| cookbook_matches | tag_arguments | warnings_shown | build_status |
| FC002,FC003,FC004 | | FC002,FC003,FC004 | successful |
| FC002,FC003,FC004 | -t style | FC002,FC004 | successful |
| FC002,FC003,FC004 | -f style | FC002,FC003,FC004 | failed |
| FC002,FC003,FC004 | -f FC005 | FC002,FC003,FC004 | successful |
| FC002,FC003,FC004 | -f FC003,FC004 | FC002,FC003,FC004 | failed |
| FC002,FC003,FC004 | --epic-fail FC003 | FC002,FC003,FC004 | failed |
| FC002,FC003 | -f any | FC002,FC003 | failed |
9 changes: 0 additions & 9 deletions features/support/cookbook_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ def cookbook_that_matches_rules(codes)
end
end
write_recipe(recipe)
write_readme('Hello World') # Don't trigger FC011
end

# Create a cookbook with a LRWP
Expand Down Expand Up @@ -274,14 +273,6 @@ def nil_if_unspecified(str)
str == 'unspecified' ? nil : str
end

# Create a README with the provided content.
#
# @param [String] content The recipe content.
# @param [String] cookbook_name Optional name of the cookbook.
def write_readme(content, cookbook_name = 'example')
write_file "cookbooks/#{cookbook_name}/README.md", content.strip
end

# Create a recipe with the provided content.
#
# @param [String] content The recipe content.
Expand Down
9 changes: 2 additions & 7 deletions lib/foodcritic/domain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ def to_s

# A rule to be matched against.
class Rule
attr_accessor :code, :name, :cookbook, :recipe, :provider, :resource
attr_writer :tags
attr_accessor :code, :name, :cookbook, :recipe, :provider, :resource, :tags

# Create a new rule
#
Expand All @@ -63,10 +62,6 @@ def initialize(code, name)
@tags = [code]
end

def tags
['any'] + @tags
end

# Returns a string representation of this rule.
#
# @return [String] Rule as a string.
Expand All @@ -75,4 +70,4 @@ def to_s
end
end

end
end
115 changes: 36 additions & 79 deletions lib/foodcritic/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ module Helpers
# @param [Nokogiri::XML::Node] node The node to create a match for
# @return [Hash] Hash with the matched node name and position with the recipe
def match(node)
raise ArgumentError.new unless node.respond_to?(:xpath)
pos = node.xpath('descendant::pos').first
return nil if pos.nil?
{:matched => node.respond_to?(:name) ? node.name : '', :line => pos['line'].to_i, :column => pos['column'].to_i}
{:matched => node.respond_to?(:name) ? node.name : '', :line => pos['line'], :column => pos['column']}
end

# Create a match for a specified file. Use this if the presence of the file triggers the warning rather than content.
Expand All @@ -25,142 +23,107 @@ def match(node)
# @return [Hash] Hash with the match details
# @see FoodCritic::Helpers#match
def file_match(file)
raise ArgumentError.new("Filename cannot be nil") if file.nil?
{:filename => file, :matched => file, :line => 1, :column => 1}
end

# Does the specified recipe check for Chef Solo?
#
# @param [Nokogiri::XML::Node] ast The AST of the cookbook recipe to check.
# @return [Boolean] True if there is a test for Chef::Config[:solo] in the
# recipe
# @return [Boolean] True if there is a test for Chef::Config[:solo] in the recipe
def checks_for_chef_solo?(ast)
! ast.xpath(%q{//if/aref[count(descendant::const[@value = 'Chef' or
@value = 'Config']) = 2
and count(descendant::ident[@value='solo']) > 0]}).empty?
! ast.xpath(%q{//if/aref[count(descendant::const[@value = 'Chef' or @value = 'Config']) = 2 and
count(descendant::ident[@value='solo']) > 0]}).empty?
end

# Is the chef-solo-search library available?
#
# @param [String] recipe_path The path to the current recipe
# @return [Boolean] True if the chef-solo-search library is available.
def chef_solo_search_supported?(recipe_path)
return false if recipe_path.nil? || ! File.exists?(recipe_path)
cbk_tree_path = Pathname.new(File.join(recipe_path, '../../..'))
search_libs = Dir[File.join(cbk_tree_path.realpath, "*/libraries/search.rb")]
search_libs = Dir[File.join(Pathname.new(File.join(recipe_path, '../../..')).realpath, "*/libraries/search.rb")]
search_libs.any? do |lib|
! read_file(lib).xpath(%q{//class[count(descendant::const[@value='Chef'
or @value='Recipe']) = 2]/descendant::def/ident[@value='search']}).empty?
! read_file(lib).xpath(%q{//class[count(descendant::const[@value='Chef' or @value='Recipe']) = 2]/
descendant::def/ident[@value='search']}).empty?
end
end

# Searches performed by the specified recipe.
#
# @param [Nokogiri::XML::Node] ast The AST of the cookbook recipe to check.
# @return [Array] The AST nodes in the recipe where searches are performed
# @return [Boolean] True if the recipe performs a search
def searches(ast)
return [] unless ast.respond_to?(:xpath)
ast.xpath("//fcall/ident[@value = 'search']")
end

# Searches performed by the specified recipe that are literal strings.
# Searches with a query formed from a subexpression will be ignored.
# Searches performed by the specified recipe that are literal strings. Searches with a query formed from a
# subexpression will be ignored.
#
# @param [Nokogiri::XML::Node] ast The AST of the cookbook recipe to check
# @return [Array] The matching nodes
# @param [Nokogiri::XML::Node] ast The AST of the cookbook recipe to check.
# @return [Nokogiri::XML::Node] The matching nodes
def literal_searches(ast)
return [] unless ast.respond_to?(:xpath)
ast.xpath("//method_add_arg[fcall/ident/@value = 'search' and
count(descendant::string_embexpr) = 0]/descendant::tstring_content")
ast.xpath("//method_add_arg[fcall/ident/@value = 'search' and count(descendant::string_embexpr) = 0]/descendant::tstring_content")
end

class AttFilter
def is_att_type(value)
return [] unless value.respond_to?(:select)
value.select{|n| %w{node default override set normal}.include?(n.to_s)}
end
end

# Find attribute accesses by type.
#
# @param [Nokogiri::XML::Node] ast The AST of the cookbook recipe to check
# @param [Symbol] accessed_via The approach used to access the attributes
# (:string, :symbol or :vivified)
# @param [Boolean] exclude_with_dots Exclude attribute accesses that mix
# strings/symbols with dot notation.
# @param [Symbol] accessed_via The approach used to access the attributes (:string, :symbol or :vivified)
# @param [Boolean] exclude_with_dots Exclude attribute accesses that mix strings/symbols with dot notation.
# @return [Array] The matching nodes if any
def attribute_access(ast, accessed_via, exclude_with_dots)
return [] unless ast.respond_to?(:xpath)
unless [:vivified, :symbol, :string].include? accessed_via
raise ArgumentError.new "Node type not recognised"
end

(if accessed_via == :vivified
calls = ast.xpath(%q{//*[self::call or self::field]
[is_att_type(vcall/ident/@value) or
is_att_type(var_ref/ident/@value)][@value='.']}, AttFilter.new)
calls.select do |call|
call.xpath("aref/args_add_block").size == 0 and
(call.xpath("descendant::ident").size > 1 and
! dsl_methods.include?(call.xpath("ident/@value").to_s.to_sym))
(if accessed_via == :vivified
calls = ast.xpath(%Q{//*[self::call or self::field][is_att_type(vcall/ident/@value) or
is_att_type(var_ref/ident/@value)][@value='.']}, AttFilter.new)
calls.select do |call|
call.xpath("aref/args_add_block").size == 0 and (call.xpath("descendant::ident").size > 1 and
! dsl_methods.include?(call.xpath("ident/@value").to_s.to_sym))
end
else
accessed_via = 'tstring_content' if accessed_via == :string
expr = '//*[self::aref_field or self::aref][is_att_type(descendant::ident'
expr += '[not(ancestor::aref/call)]' if exclude_with_dots
expr += "/@value)]/descendant::#{accessed_via}"
ast.xpath(expr, AttFilter.new)
end
else
accessed_via = 'tstring_content' if accessed_via == :string
expr = '//*[self::aref_field or self::aref]'
expr += '[is_att_type(descendant::ident'
expr += '[not(ancestor::aref/call)]' if exclude_with_dots
expr += "/@value)]/descendant::#{accessed_via}"
ast.xpath(expr, AttFilter.new)
end
).sort
end

# Find Chef resources of the specified type.
# TODO: Include blockless resources
#
# @param [Nokogiri::XML::Node] ast The AST of the cookbook recipe to check
# @param [String] type The type of resource to look for (or nil for all
# resources)
# @param [String] type The type of resource to look for (or nil for all resources)
# @return [Array] AST nodes of Chef resources.
def find_resources(ast, type = nil)
return [] unless ast.respond_to?(:xpath)
scope_type = ''
scope_type = "[@value='#{type}']" unless type.nil?
ast.xpath("//method_add_block[command/ident#{scope_type}]")
ast.xpath(%Q{//method_add_block[command/ident#{type.nil? ? '' : "[@value='#{type}']"}]})
end

# Return the type, e.g. 'package' for a given resource
#
# @param [Nokogiri::XML::Node] resource The resource AST
# @return [String] The type of resource
def resource_type(resource)
unless resource.respond_to?(:xpath)
raise ArgumentError.new "Resource must support #xpath"
end
type = resource.xpath('string(command/ident/@value)')
if type.empty?
raise ArgumentError.new "Provided AST node is not a resource"
end
type
resource.xpath('string(command/ident/@value)')
end

# Retrieve the name attribute associated with the specified resource.
#
# @param [Nokogiri::XML::Node] resource The resource AST to lookup the name
# attribute under
# @param [Nokogiri::XML::Node] resource The resource AST to lookup the name attribute under
# @return [String] The name attribute value
def resource_name(resource)
unless resource.respond_to?(:xpath)
raise ArgumentError.new "Resource must support #xpath"
end
resource.xpath('string(command//tstring_content/@value)')
end

# Retrieve a single-valued attribute from the specified resource.
#
# @param [String] name The attribute name
# @param [Nokogiri::XML::Node] resource The resource AST to lookup the
# attribute under
# @param [Nokogiri::XML::Node] resource The resource AST to lookup the attribute under
# @return [String] The attribute value for the specified attribute
def resource_attribute(name, resource)
resource_attributes(resource)[name]
Expand All @@ -172,8 +135,7 @@ def resource_attribute(name, resource)
# @return [Hash] The resource attributes
def resource_attributes(resource)
atts = {:name => resource_name(resource)}
resource.xpath('do_block/descendant::command
[count(ancestor::do_block) = 1]').each do |att|
resource.xpath('do_block/descendant::command[count(ancestor::do_block) = 1]').each do |att|
if att.xpath('descendant::symbol').empty?
att_value = att.xpath('string(descendant::tstring_content/@value)')
else
Expand All @@ -189,20 +151,15 @@ def resource_attributes(resource)
# @param [Nokogiri::XML::Node] ast The recipe AST
# @return [Hash] The matching resources
def resources_by_type(ast)
unless ast.respond_to?(:xpath)
raise ArgumentError.new "AST must support #xpath"
end
result = Hash.new{|hash, key| hash[key] = Array.new}
find_resources(ast).each do |resource|
result[resource_type(resource)] << resource
end
find_resources(ast).each{|resource| result[resource_type(resource)] << resource}
result
end

# Retrieve the attributes as a hash for all resources of a given type.
#
# @param [Nokogiri::XML::Node] ast The recipe AST
# @return [Hash] Resources keyed by type, with an array for each
# @return [Hash] An array of resource attributes keyed by type.
def resource_attributes_by_type(ast)
result = {}
resources_by_type(ast).each do |type,resources|
Expand Down
15 changes: 9 additions & 6 deletions lib/foodcritic/linter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ def initialize
# @option options [Array] fail_tags The tags to fail the build on
# @return [FoodCritic::Review] A review of your cookbooks, with any warnings issued.
def check(cookbook_path, options)
raise ArgumentError.new "Cookbook path is required" if cookbook_path.nil?
@last_cookbook_path, @last_options = cookbook_path, options
load_rules unless defined? @rules
warnings = []; last_dir = nil; matched_rule_tags = Set.new
Expand All @@ -55,14 +54,13 @@ def check(cookbook_path, options)
rule_matches += matches(rule.cookbook, cookbook_dir) if last_dir != cookbook_dir
rule_matches.each do |match|
warnings << Warning.new(rule, {:filename => file}.merge(match))
matched_rule_tags << rule.tags
matched_rule_tags += rule.tags
end
end
last_dir = cookbook_dir
end

@review = Review.new(cookbook_path, warnings,
should_fail_build?(options[:fail_tags], matched_rule_tags))
@review = Review.new(cookbook_path, warnings, should_fail_build?(options[:fail_tags], matched_rule_tags))

binding.pry if options[:repl]
@review
Expand Down Expand Up @@ -116,8 +114,13 @@ def files_to_process(dir)
# @param [Set] matched_tags The tags of warnings we have matches for
# @return [Boolean] True if the build should be failed
def should_fail_build?(fail_tags, matched_tags)
return false if fail_tags.empty?
matched_tags.any?{|tags| matching_tags?(fail_tags, tags)}
if fail_tags.empty?
false
elsif fail_tags.include? 'any'
true
else
matching_tags?(fail_tags, matched_tags)
end
end

# Evaluate the specified tags
Expand Down
Loading

0 comments on commit 36bf9d7

Please sign in to comment.