diff options
Diffstat (limited to 'lib/chef/cookbook/syntax_check.rb')
-rw-r--r-- | lib/chef/cookbook/syntax_check.rb | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/lib/chef/cookbook/syntax_check.rb b/lib/chef/cookbook/syntax_check.rb new file mode 100644 index 0000000000..bf7c45e252 --- /dev/null +++ b/lib/chef/cookbook/syntax_check.rb @@ -0,0 +1,136 @@ +# +# Author:: Daniel DeLeo (<dan@opscode.com>) +# Copyright:: Copyright (c) 2010 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/checksum_cache' +require 'chef/mixin/shell_out' + +class Chef + class Cookbook + # == Chef::Cookbook::SyntaxCheck + # Encapsulates the process of validating the ruby syntax of files in Chef + # cookbooks. + class SyntaxCheck + include Chef::Mixin::ShellOut + + attr_reader :cookbook_path + + # Creates a new SyntaxCheck given the +cookbook_name+ and a +cookbook_path+. + # If no +cookbook_path+ is given, +Chef::Config.cookbook_path+ is used. + def self.for_cookbook(cookbook_name, cookbook_path=nil) + cookbook_path ||= Chef::Config.cookbook_path + unless cookbook_path + raise ArgumentError, "Cannot find cookbook #{cookbook_name} unless Chef::Config.cookbook_path is set or an explicit cookbook path is given" + end + new(File.join(cookbook_path, cookbook_name.to_s)) + end + + # Create a new SyntaxCheck object + # === Arguments + # cookbook_path::: the (on disk) path to the cookbook + def initialize(cookbook_path) + @cookbook_path = cookbook_path + end + + def cache + Chef::ChecksumCache.instance + end + + def ruby_files + Dir[File.join(cookbook_path, '**', '*.rb')] + end + + def untested_ruby_files + ruby_files.reject do |file| + if validated?(file) + Chef::Log.debug("Ruby file #{file} is unchanged, skipping syntax check") + true + else + false + end + end + end + + def template_files + Dir[File.join(cookbook_path, '**', '*.erb')] + end + + def untested_template_files + template_files.reject do |file| + if validated?(file) + Chef::Log.debug("Template #{file} is unchanged, skipping syntax check") + true + else + false + end + end + end + + def validated?(file) + !!cache.lookup_checksum(cache_key(file), File.stat(file)) + end + + def validated(file) + cache.generate_checksum(cache_key(file), file, File.stat(file)) + end + + def cache_key(file) + @cache_keys ||= {} + @cache_keys[file] ||= cache.generate_key(file, "chef-test") + end + + def validate_ruby_files + untested_ruby_files.each do |ruby_file| + return false unless validate_ruby_file(ruby_file) + validated(ruby_file) + end + end + + def validate_templates + untested_template_files.each do |template| + return false unless validate_template(template) + validated(template) + end + end + + def validate_template(erb_file) + Chef::Log.debug("Testing template #{erb_file} for syntax errors...") + result = shell_out("erubis -x #{erb_file} | ruby -c") + result.error! + true + rescue Mixlib::ShellOut::ShellCommandFailed + file_relative_path = erb_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1] + Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:") + result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) } + false + end + + def validate_ruby_file(ruby_file) + Chef::Log.debug("Testing #{ruby_file} for syntax errors...") + result = shell_out("ruby -c #{ruby_file}") + result.error! + true + rescue Mixlib::ShellOut::ShellCommandFailed + file_relative_path = ruby_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1] + Chef::Log.fatal("Cookbook file #{file_relative_path} has a ruby syntax error:") + result.stderr.each_line { |l| Chef::Log.fatal(l.chomp) } + false + end + + end + end +end |