Class: Chef::Cookbook::SyntaxCheck
- Includes:
- Mixin::Checksum, Mixin::ShellOut
- Defined in:
- lib/chef/cookbook/syntax_check.rb
Overview
Chef::Cookbook::SyntaxCheck
Encapsulates the process of validating the ruby syntax of files in Chef cookbooks.
Defined Under Namespace
Classes: PersistentSet
Constant Summary
Constants included from Mixin::ShellOut
Mixin::ShellOut::DEPRECATED_OPTIONS
Instance Attribute Summary collapse
-
#chefignore ⇒ Object
readonly
Returns the value of attribute chefignore.
-
#cookbook_path ⇒ Object
readonly
Returns the value of attribute cookbook_path.
-
#validated_files ⇒ Object
readonly
A PersistentSet object that tracks which files have already been validated.
Class Method Summary collapse
-
.for_cookbook(cookbook_name, cookbook_path = nil) ⇒ Object
Creates a new SyntaxCheck given the
cookbook_nameand acookbook_path.
Instance Method Summary collapse
-
#initialize(cookbook_path) ⇒ SyntaxCheck
constructor
- Create a new SyntaxCheck object === Arguments cookbook_path:
-
the (on disk) path to the cookbook.
-
#invalid_erb_file(erb_file, error_message) ⇒ Object
Debug a syntax error in a template.
-
#invalid_ruby_file(ruby_file, error_message) ⇒ Object
Debugs ruby syntax errors by printing the path to the file and any diagnostic info given in
error_message. - #remove_ignored_files(file_list) ⇒ Object
-
#ruby ⇒ Object
Returns the full path to the running ruby.
- #ruby_files ⇒ Object
- #template_files ⇒ Object
- #untested_ruby_files ⇒ Object
- #untested_template_files ⇒ Object
-
#validate_erb_file_inline(erb_file) ⇒ Object
Validate the ruby code in an erb template.
-
#validate_erb_via_subcommand(erb_file) ⇒ Object
Validate the ruby code in an erb template.
-
#validate_inline? ⇒ Boolean
Whether or not we’re running on a version of ruby that can support inline validation.
-
#validate_ruby_by_subcommand(ruby_file) ⇒ Object
Validate the syntax of a ruby file by shelling out to ‘ruby -c`.
- #validate_ruby_file(ruby_file) ⇒ Object
-
#validate_ruby_file_inline(ruby_file) ⇒ Object
Validate the syntax of a ruby file.
- #validate_ruby_files ⇒ Object
- #validate_template(erb_file) ⇒ Object
- #validate_templates ⇒ Object
- #validated(file) ⇒ Object
- #validated?(file) ⇒ Boolean
Methods included from Mixin::Checksum
Methods included from Mixin::ShellOut
#run_command_compatible_options, #shell_out, #shell_out!
Constructor Details
#initialize(cookbook_path) ⇒ SyntaxCheck
Create a new SyntaxCheck object
Arguments
- cookbook_path:
-
the (on disk) path to the cookbook
95 96 97 98 99 100 |
# File 'lib/chef/cookbook/syntax_check.rb', line 95 def initialize(cookbook_path) @cookbook_path = cookbook_path @chefignore ||= Chefignore.new(cookbook_path) @validated_files = PersistentSet.new end |
Instance Attribute Details
#chefignore ⇒ Object (readonly)
Returns the value of attribute chefignore.
80 81 82 |
# File 'lib/chef/cookbook/syntax_check.rb', line 80 def chefignore @chefignore end |
#cookbook_path ⇒ Object (readonly)
Returns the value of attribute cookbook_path.
74 75 76 |
# File 'lib/chef/cookbook/syntax_check.rb', line 74 def cookbook_path @cookbook_path end |
#validated_files ⇒ Object (readonly)
A PersistentSet object that tracks which files have already been validated.
78 79 80 |
# File 'lib/chef/cookbook/syntax_check.rb', line 78 def validated_files @validated_files end |
Class Method Details
.for_cookbook(cookbook_name, cookbook_path = nil) ⇒ Object
Creates a new SyntaxCheck given the cookbook_name and a cookbook_path. If no cookbook_path is given, Chef::Config.cookbook_path is used.
84 85 86 87 88 89 90 |
# File 'lib/chef/cookbook/syntax_check.rb', line 84 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 |
Instance Method Details
#invalid_erb_file(erb_file, error_message) ⇒ Object
Debug a syntax error in a template.
232 233 234 235 236 237 |
# File 'lib/chef/cookbook/syntax_check.rb', line 232 def invalid_erb_file(erb_file, ) file_relative_path = erb_file[/^#{Regexp.escape(cookbook_path+File::Separator)}(.*)/, 1] Chef::Log.fatal("Erb template #{file_relative_path} has a syntax error:") .each_line { |l| Chef::Log.fatal(l.chomp) } nil end |
#invalid_ruby_file(ruby_file, error_message) ⇒ Object
Debugs ruby syntax errors by printing the path to the file and any diagnostic info given in error_message
280 281 282 283 284 285 |
# File 'lib/chef/cookbook/syntax_check.rb', line 280 def invalid_ruby_file(ruby_file, ) 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:") .each_line { |l| Chef::Log.fatal(l.chomp) } false end |
#remove_ignored_files(file_list) ⇒ Object
102 103 104 105 106 107 108 109 110 |
# File 'lib/chef/cookbook/syntax_check.rb', line 102 def remove_ignored_files(file_list) return file_list unless chefignore.ignores.length > 0 file_list.reject do |full_path| cookbook_pn = Pathname.new cookbook_path full_pn = Pathname.new full_path relative_pn = full_pn.relative_path_from cookbook_pn chefignore.ignored? relative_pn.to_s end end |
#ruby ⇒ Object
Returns the full path to the running ruby.
288 289 290 |
# File 'lib/chef/cookbook/syntax_check.rb', line 288 def ruby Gem.ruby end |
#ruby_files ⇒ Object
112 113 114 |
# File 'lib/chef/cookbook/syntax_check.rb', line 112 def ruby_files remove_ignored_files Dir[File.join(cookbook_path, '**', '*.rb')] end |
#template_files ⇒ Object
127 128 129 |
# File 'lib/chef/cookbook/syntax_check.rb', line 127 def template_files remove_ignored_files Dir[File.join(cookbook_path, '**', '*.erb')] end |
#untested_ruby_files ⇒ Object
116 117 118 119 120 121 122 123 124 125 |
# File 'lib/chef/cookbook/syntax_check.rb', line 116 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 |
#untested_template_files ⇒ Object
131 132 133 134 135 136 137 138 139 140 |
# File 'lib/chef/cookbook/syntax_check.rb', line 131 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 |
#validate_erb_file_inline(erb_file) ⇒ Object
Validate the ruby code in an erb template. Uses RubyVM to do syntax checking, so callers should check #validate_inline? before calling.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/chef/cookbook/syntax_check.rb', line 191 def validate_erb_file_inline(erb_file) old_stderr = $stderr engine = Erubis::Eruby.new engine.convert!(IO.read(erb_file)) ruby_code = engine.src # Even when we're compiling the code w/ RubyVM, syntax errors just # print to $stderr. We want to capture this and handle the printing # ourselves, so we must temporarily swap $stderr to capture the output. tmp_stderr = $stderr = StringIO.new abs_path = File.(erb_file) RubyVM::InstructionSequence.new(ruby_code, erb_file, abs_path, 0) true rescue SyntaxError $stderr = old_stderr invalid_erb_file(erb_file, tmp_stderr.string) false ensure # be paranoid about setting stderr back to the old value. $stderr = old_stderr if defined?(old_stderr) && old_stderr end |
#validate_erb_via_subcommand(erb_file) ⇒ Object
Validate the ruby code in an erb template. Pipes the output of ‘erubis -x` to `ruby -c`, so it works with any ruby version, but is much slower than the inline version. – TODO: This can be removed when ruby 1.8 support is dropped.
222 223 224 225 226 227 228 229 |
# File 'lib/chef/cookbook/syntax_check.rb', line 222 def validate_erb_via_subcommand(erb_file) result = shell_out("erubis -x #{erb_file} | #{ruby} -c") result.error! true rescue Mixlib::ShellOut::ShellCommandFailed invalid_erb_file(erb_file, result.stderr) false end |
#validate_inline? ⇒ Boolean
Whether or not we’re running on a version of ruby that can support inline validation. Inline validation relies on the RubyVM features introduced with ruby 1.9, so 1.8 cannot be supported.
185 186 187 |
# File 'lib/chef/cookbook/syntax_check.rb', line 185 def validate_inline? defined?(RubyVM::InstructionSequence) end |
#validate_ruby_by_subcommand(ruby_file) ⇒ Object
Validate the syntax of a ruby file by shelling out to ‘ruby -c`. Should work for all ruby versions, but is slower and uses more resources than the inline strategy.
269 270 271 272 273 274 275 276 |
# File 'lib/chef/cookbook/syntax_check.rb', line 269 def validate_ruby_by_subcommand(ruby_file) result = shell_out("#{ruby} -c #{ruby_file}") result.error! true rescue Mixlib::ShellOut::ShellCommandFailed invalid_ruby_file(ruby_file, result.stderr) false end |
#validate_ruby_file(ruby_file) ⇒ Object
173 174 175 176 177 178 179 180 |
# File 'lib/chef/cookbook/syntax_check.rb', line 173 def validate_ruby_file(ruby_file) Chef::Log.debug("Testing #{ruby_file} for syntax errors...") if validate_inline? validate_ruby_file_inline(ruby_file) else validate_ruby_by_subcommand(ruby_file) end end |
#validate_ruby_file_inline(ruby_file) ⇒ Object
Validate the syntax of a ruby file. Uses (Ruby 1.9+ only) RubyVM to compile the code without evaluating it or spawning a new process. Callers should check #validate_inline? before calling.
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/chef/cookbook/syntax_check.rb', line 242 def validate_ruby_file_inline(ruby_file) # Even when we're compiling the code w/ RubyVM, syntax errors just # print to $stderr. We want to capture this and handle the printing # ourselves, so we must temporarily swap $stderr to capture the output. old_stderr = $stderr tmp_stderr = $stderr = StringIO.new abs_path = File.(ruby_file) file_content = IO.read(abs_path) # We have to wrap this in a block so the user code evaluates in a # similar context as what Chef does normally. Otherwise RubyVM # will reject some common idioms, like using `return` to end evaluation # of a recipe. See also CHEF-5199 wrapped_content = "Object.new.instance_eval do\n#{file_content}\nend\n" RubyVM::InstructionSequence.new(wrapped_content, ruby_file, abs_path, 0) true rescue SyntaxError $stderr = old_stderr invalid_ruby_file(ruby_file, tmp_stderr.string) false ensure # be paranoid about setting stderr back to the old value. $stderr = old_stderr if defined?(old_stderr) && old_stderr end |
#validate_ruby_files ⇒ Object
150 151 152 153 154 155 |
# File 'lib/chef/cookbook/syntax_check.rb', line 150 def validate_ruby_files untested_ruby_files.each do |ruby_file| return false unless validate_ruby_file(ruby_file) validated(ruby_file) end end |
#validate_template(erb_file) ⇒ Object
164 165 166 167 168 169 170 171 |
# File 'lib/chef/cookbook/syntax_check.rb', line 164 def validate_template(erb_file) Chef::Log.debug("Testing template #{erb_file} for syntax errors...") if validate_inline? validate_erb_file_inline(erb_file) else validate_erb_via_subcommand(erb_file) end end |
#validate_templates ⇒ Object
157 158 159 160 161 162 |
# File 'lib/chef/cookbook/syntax_check.rb', line 157 def validate_templates untested_template_files.each do |template| return false unless validate_template(template) validated(template) end end |
#validated(file) ⇒ Object
146 147 148 |
# File 'lib/chef/cookbook/syntax_check.rb', line 146 def validated(file) validated_files.add(checksum(file)) end |
#validated?(file) ⇒ Boolean
142 143 144 |
# File 'lib/chef/cookbook/syntax_check.rb', line 142 def validated?(file) validated_files.include?(checksum(file)) end |