puppet-lint-0.3.2/0000755000004100000410000000000012056100642014021 5ustar www-datawww-datapuppet-lint-0.3.2/.travis.yml0000644000004100000410000000020012056100642016122 0ustar www-datawww-datarvm: - 1.8.7 - 1.9.2 - 1.9.3 branches: only: - master - dust_bunny notifications: email: - tim@github.com puppet-lint-0.3.2/LICENSE0000644000004100000410000000203612056100642015027 0ustar www-datawww-dataCopyright (c) 2011 Tim Sharpe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. puppet-lint-0.3.2/README.md0000644000004100000410000001706212056100642015306 0ustar www-datawww-data# Puppet-lint [![Build Status](https://secure.travis-ci.org/rodjek/puppet-lint.png)](http://travis-ci.org/rodjek/puppet-lint) [![Dependency Status](https://gemnasium.com/rodjek/puppet-lint.png)](http://gemnasium.com/rodjek/puppet-lint) The goal of this project is to implement as many of the recommended Puppet style guidelines from the [Puppet Labs style guide](http://docs.puppetlabs.com/guides/style_guide.html) as practical. ## Installation gem install puppet-lint ## Testing your manifests ### By hand You can test a single manifest file by running puppet-lint ### Rake task If you want to test your entire Puppet manifest directory, you can add `require 'puppet-lint/tasks/puppet-lint'` to your Rakefile and then run rake lint ## Implemented tests At the moment, the following tests have been implemented: ### Spacing, Indentation & Whitespace * Must use two-space soft tabs. * Must not use literal tab characters. * Must not contain trailing white space. * Should not exceed an 80 character line width * An exception has been made for `source => 'puppet://...'` lines as splitting these over multiple lines decreases the readability of the manifests. * Should align arrows (`=>`) within blocks of attributes. ### Quoting * All strings that do not contain variables should be enclosed in single quotes. * An exception has been made for double quoted strings containing \n or \t. * All strings that contain variables must be enclosed in double quotes. * All variables should be enclosed in braces when interpolated in a string. * Variables standing by themselves should not be quoted. ### Resources * All resource titles should be quoted. * If a resource declaration includes an `ensure` attribute, it should be the first attribute specified. * Symbolic links should be declared by using an ensure value of `link` and explicitly specifying a value for the `target` attribute. * File modes should be represented as a 4 digit string enclosed in single quotes or use symbolic file modes. ### Conditionals * You should not intermingle conditionals inside resource declarations (i.e. selectors inside resources). * Case statements should have a default case. ### Classes * Relationship declarations with the chaining syntax should only be used in the 'left to right' direction. * Classes should not be defined inside a class. * Defines should not be defined inside a class. * Classes should not inherit between namespaces. * Required parameters in class & defined type definitions should be listed before optional parameters. * When using top-scope variables, including facts, Puppet modules should explicitly specify the empty namespace. ## Fixing problems ### right_to_left_relationship ``` WARNING: right-to-left (<-) relationship on line X ``` While right to left relationships are perfectly valid, it's highly recommended that you don't use them as most people think and read from left to right and this can lead to confusion. Bad: ``` Service['httpd'] <- Package['httpd'] ``` Good: ``` Package['httpd'] -> Service['httpd'] ``` ### autoloader_layout ``` ERROR: mymodule::myclass not in autoload module layout on line X ``` Puppet attempts to autoload only the required manifests for the resources and classes specified in your manifests. In order to do this, the autoloader expects your manifests to be laid out on disk in a particular format. For example, when you use `mymodule::myclass` in your manifests, Puppet will attempt to read `/mymodule/manifests/myclass.pp`. The only exception to this is when you reference `mymodule` itself (without any subclass/subtype) in which case it will read `/mymodule/manifests/init.pp`. ### parameter_order ``` WARNING: optional parameter listed before required parameter on line X ``` In parameterised class and defined type definitions, parameters that are required should be listed before optional parameters (those with default values). Bad: ``` class foo($bar='baz', $gronk) { ``` Good: ``` class foo($gronk, $bar='baz') { ``` ### inherits_across_namespaces Placeholder ### nested_classes_or_defines Placeholder ### variable_scope Placeholder ### selector_inside_resource Placeholder ### case_without_default Placeholder ### unquoted_resource_title Placeholder ### ensure_first_param Placeholder ### unquoted_file_mode Placeholder ### 4digit_file_mode Placeholder ### ensure_not_symlink_target Placeholder ### double_quoted_strings Placeholder ### only_variable_string Placeholder ### variables_not_enclosed Placeholder ### single_quote_string_with_variables Placeholder ### quoted_booleans Placeholder ### variable_contains_dash Placeholder ### hard_tabs Placeholder ### trailing_whitespace Placeholder ### 80chars Placeholder ### 2sp_soft_tabs Placeholder ### arrow_alignment Placeholder ## Disabling checks ### puppet-lint You can disable any of the checks when running the `puppet-lint` command by adding a `--no--check` flag to the command. For example, if you wanted to skip the 80 character check, you would run ``` puppet-lint --no-80chars-check /path/to/my/manifest.pp ``` puppet-lint will also check for a `.puppet-lint.rc` file in the current directory and your home directory and read in flags from there, so if you wanted to always skip the hard tab character check, you could create `~./puppet-lint.rc` containing ``` --no-hard_tabs-check ``` For a list of all the flags just type: ``` puppet-lint --help ``` ### Rake task You can also disable checks when running puppet-lint through the supplied Rake task. Simply add the following line after the `require` statement in your `Rakefile`. ``` ruby PuppetLint.configuration.send("disable_") ``` So, to disable the 80 character check, you would add: ``` ruby PuppetLint.configuration.send("disable_80chars") ``` The Rake task also supports ignoring certain paths from being linted: ``` ruby PuppetLint.configuration.ignore_paths = ["vendor/**/*.pp"] ``` ## Reporting bugs or incorrect results If you find a bug in puppet-lint or its results, please create an issue in the [repo issues tracker](https://github.com/rodjek/puppet-lint/issues/). Bonus points will be awarded if you also include a patch that fixes the issue. ## Thank You Many thanks to the following people for contributing to puppet-lint * James Turnbull (@kartar) * Jan Vansteenkiste (@vStone) * Julian Simpson (@simpsonjulian) * S. Zachariah Sprackett (@zsprackett) As well as the many people who have reported the issues they've had! ## License Copyright (c) 2011 Tim Sharpe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. puppet-lint-0.3.2/Rakefile0000644000004100000410000000126512056100642015472 0ustar www-datawww-datarequire 'rake' require 'rspec/core/rake_task' task :default => :test #task :default => [:test, :rdoc] RSpec::Core::RakeTask.new(:test) RSpec::Core::RakeTask.new(:cov) do |t| t.rcov = true t.rcov_opts = '--exclude "spec" --xrefs' end ### RDOC Tasks ### require 'rdoc' if (RDoc::VERSION.split('.') <=> ['2','4','2']) >= 0 require 'rdoc/task' RDoc::Task.new(:rdoc) do |rdoc| rdoc.main = "README.md" rdoc.rdoc_files.include("README.md", "lib/**/*.rb") rdoc.options << "--all" end else require 'rake/rdoctask' Rake::RDocTask.new(:rdoc) do |rdoc| rdoc.main = "README.md" rdoc.rdoc_files.include("README.md", "lib/**/*.rb") rdoc.options << "--all" end end puppet-lint-0.3.2/spec/0000755000004100000410000000000012056100642014753 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/0000755000004100000410000000000012056100642017234 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/lexer_spec.rb0000644000004100000410000005226312056100642021722 0ustar www-datawww-datarequire 'spec_helper' describe PuppetLint::Lexer do before do @lexer = PuppetLint::Lexer.new end context 'invalid code' do it 'should bork' do expect { @lexer.tokenise('%') }.to raise_error(PuppetLint::LexerError) end end context '#new_token' do it 'should calculate the line number for an empty string' do token = @lexer.new_token(:TEST, 'test', :chunk => '') token.line.should == 1 end it 'should calculate the line number for a multi line string' do token = @lexer.new_token(:TEST, 'test', :chunk => "foo\nbar") token.line.should == 2 end it 'should calculate the column number for an empty string' do token = @lexer.new_token(:TEST, 'test', :chunk => '') token.column.should == 1 end it 'should calculate the column number for a single line string' do token = @lexer.new_token(:TEST, 'test', :chunk => 'this is a test') token.column.should == 14 end it 'should calculate the column number for a multi line string' do token = @lexer.new_token(:TEST, 'test', :chunk => "foo\nbar\nbaz\ngronk") token.column.should == 5 end end context '#get_string_segment' do it 'should get a segment with a single terminator' do data = StringScanner.new('foo"bar') value, terminator = @lexer.get_string_segment(data, '"') value.should == 'foo' terminator.should == '"' end it 'should get a segment with multiple terminators' do data = StringScanner.new('foo"bar$baz') value, terminator = @lexer.get_string_segment(data, "'$") value.should == 'foo"bar' terminator.should == '$' end it 'should not get a segment with an escaped terminator' do data = StringScanner.new('foo"bar') value, terminator = @lexer.get_string_segment(data, '$') value.should be_nil terminator.should be_nil end end context '#interpolate_string' do it 'should handle a string with no variables' do @lexer.interpolate_string('foo bar baz"',1, 1) token = @lexer.tokens.first @lexer.tokens.length.should == 1 token.type.should == :STRING token.value.should == 'foo bar baz' token.line.should == 1 token.column.should == 1 end it 'should handle a string with a newline' do @lexer.interpolate_string(%{foo\nbar"}, 1, 1) token = @lexer.tokens.first @lexer.tokens.length.should == 1 token.type.should == :STRING token.value.should == "foo\nbar" token.line.should == 1 token.column.should == 1 end it 'should handle a string with a single variable and suffix' do @lexer.interpolate_string('${foo}bar"', 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == '' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :VARIABLE tokens[1].value.should == 'foo' tokens[1].line.should == 1 tokens[1].column.should == 3 tokens[2].type.should == :DQPOST tokens[2].value.should == 'bar' tokens[2].line.should == 1 tokens[2].column.should == 8 end it 'should handle a string with a single variable and surrounding text' do @lexer.interpolate_string('foo${bar}baz"', 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == 'foo' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :VARIABLE tokens[1].value.should == 'bar' tokens[1].line.should == 1 tokens[1].column.should == 6 tokens[2].type.should == :DQPOST tokens[2].value.should == 'baz' tokens[2].line.should == 1 tokens[2].column.should == 11 end it 'should handle a string with multiple variables and surrounding text' do @lexer.interpolate_string('foo${bar}baz${gronk}meh"', 1, 1) tokens = @lexer.tokens tokens.length.should == 5 tokens[0].type.should == :DQPRE tokens[0].value.should == 'foo' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :VARIABLE tokens[1].value.should == 'bar' tokens[1].line.should == 1 tokens[1].column.should == 6 tokens[2].type.should == :DQMID tokens[2].value.should == 'baz' tokens[2].line.should == 1 tokens[2].column.should == 11 tokens[3].type.should == :VARIABLE tokens[3].value.should == 'gronk' tokens[3].line.should == 1 tokens[3].column.should == 15 tokens[4].type.should == :DQPOST tokens[4].value.should == 'meh' tokens[4].line.should == 1 tokens[4].column.should == 22 end it 'should handle a string with only a single variable' do @lexer.interpolate_string('${bar}"', 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == '' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :VARIABLE tokens[1].value.should == 'bar' tokens[1].line.should == 1 tokens[1].column.should == 3 tokens[2].type.should == :DQPOST tokens[2].value.should == '' tokens[2].line.should == 1 tokens[2].column.should == 8 end it 'should handle a string with only many variables' do @lexer.interpolate_string('${bar}${gronk}"', 1, 1) tokens = @lexer.tokens tokens.length.should == 5 tokens[0].type.should == :DQPRE tokens[0].value.should == '' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :VARIABLE tokens[1].value.should == 'bar' tokens[1].line.should == 1 tokens[1].column.should == 3 tokens[2].type.should == :DQMID tokens[2].value.should == '' tokens[2].line.should == 1 tokens[2].column.should == 8 tokens[3].type.should == :VARIABLE tokens[3].value.should == 'gronk' tokens[3].line.should == 1 tokens[3].column.should == 9 tokens[4].type.should == :DQPOST tokens[4].value.should == '' tokens[4].line.should == 1 tokens[4].column.should == 16 end it 'should handle a string with only an unenclosed variable' do @lexer.interpolate_string('$foo"', 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == '' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :UNENC_VARIABLE tokens[1].value.should == 'foo' tokens[1].line.should == 1 tokens[1].column.should == 2 tokens[2].type.should == :DQPOST tokens[2].value.should == '' tokens[2].line.should == 1 tokens[2].column.should == 6 end it 'should handle a string with a nested string inside it' do @lexer.interpolate_string(%q{string with ${'a nested single quoted string'} inside it"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == 'string with ' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :SSTRING tokens[1].value.should == 'a nested single quoted string' tokens[1].line.should == 1 tokens[1].column.should == 16 tokens[2].type.should == :DQPOST tokens[2].value.should == ' inside it' tokens[2].line.should == 1 tokens[2].column.should == 48 end it 'should handle a string with nested math' do @lexer.interpolate_string(%q{string with ${(3+5)/4} nested math"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 9 tokens[0].type.should == :DQPRE tokens[0].value.should == 'string with ' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :LPAREN tokens[1].line.should == 1 tokens[1].column.should == 16 tokens[2].type.should == :NUMBER tokens[2].value.should == '3' tokens[2].line.should == 1 tokens[2].column.should == 17 tokens[3].type.should == :PLUS tokens[3].line.should == 1 tokens[3].column.should == 18 tokens[4].type.should == :NUMBER tokens[4].value.should == '5' tokens[4].line.should == 1 tokens[4].column.should == 19 tokens[5].type.should == :RPAREN tokens[5].line.should == 1 tokens[5].column.should == 20 tokens[6].type.should == :DIV tokens[6].line.should == 1 tokens[6].column.should == 21 tokens[7].type.should == :NUMBER tokens[7].value.should == '4' tokens[7].line.should == 1 tokens[7].column.should == 22 tokens[8].type.should == :DQPOST tokens[8].value.should == ' nested math' tokens[8].line.should == 1 tokens[8].column.should == 24 end it 'should handle a string with a nested array' do @lexer.interpolate_string(%q{string with ${['an array ', $v2]} in it"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 8 tokens[0].type.should == :DQPRE tokens[0].value.should == 'string with ' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :LBRACK tokens[1].line.should == 1 tokens[1].column.should == 16 tokens[2].type.should == :SSTRING tokens[2].value.should == 'an array ' tokens[2].line.should == 1 tokens[2].column.should == 17 tokens[3].type.should == :COMMA tokens[3].line.should == 1 tokens[3].column.should == 28 tokens[4].type.should == :WHITESPACE tokens[4].value.should == ' ' tokens[4].line.should == 1 tokens[4].column.should == 29 tokens[5].type.should == :VARIABLE tokens[5].value.should == 'v2' tokens[5].line.should == 1 tokens[5].column.should == 30 tokens[6].type.should == :RBRACK tokens[6].line.should == 1 tokens[6].column.should == 33 tokens[7].type.should == :DQPOST tokens[7].value.should == ' in it' tokens[7].line.should == 1 tokens[7].column.should == 35 end it 'should handle a string of $s' do @lexer.interpolate_string(%q{$$$$"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 1 tokens[0].type.should == :STRING tokens[0].value.should == '$$$$' tokens[0].line.should == 1 tokens[0].column.should == 1 end it 'should handle "$foo$bar"' do @lexer.interpolate_string(%q{$foo$bar"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 5 tokens[0].type.should == :DQPRE tokens[0].value.should == '' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :UNENC_VARIABLE tokens[1].value.should == 'foo' tokens[1].line.should == 1 tokens[1].column.should == 2 tokens[2].type.should == :DQMID tokens[2].value.should == '' tokens[2].line.should == 1 tokens[2].column.should == 6 tokens[3].type.should == :UNENC_VARIABLE tokens[3].value.should == 'bar' tokens[3].line.should == 1 tokens[3].column.should == 6 tokens[4].type.should == :DQPOST tokens[4].value.should == '' tokens[4].line.should == 1 tokens[4].column.should == 10 end it 'should handle "foo$bar$"' do @lexer.interpolate_string(%q{foo$bar$"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == 'foo' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :UNENC_VARIABLE tokens[1].value.should == 'bar' tokens[1].line.should == 1 tokens[1].column.should == 5 tokens[2].type.should == :DQPOST tokens[2].value.should == '$' tokens[2].line.should == 1 tokens[2].column.should == 9 end it 'should handle "foo$$bar"' do @lexer.interpolate_string(%q{foo$$bar"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == 'foo$' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :UNENC_VARIABLE tokens[1].value.should == 'bar' tokens[1].line.should == 1 tokens[1].column.should == 6 tokens[2].type.should == :DQPOST tokens[2].value.should == '' tokens[2].line.should == 1 tokens[2].column.should == 10 end it 'should handle an empty string' do @lexer.interpolate_string(%q{"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 1 tokens[0].type.should == :STRING tokens[0].value.should == '' tokens[0].line.should == 1 tokens[0].column.should == 1 end it 'should handle "$foo::::bar"' do @lexer.interpolate_string(%q{$foo::::bar"}, 1, 1) tokens = @lexer.tokens tokens.length.should == 3 tokens[0].type.should == :DQPRE tokens[0].value.should == '' tokens[0].line.should == 1 tokens[0].column.should == 1 tokens[1].type.should == :UNENC_VARIABLE tokens[1].value.should == 'foo' tokens[1].line.should == 1 tokens[1].column.should == 2 tokens[2].type.should == :DQPOST tokens[2].value.should == '::::bar' tokens[2].line.should == 1 tokens[2].column.should == 6 end end [ 'case', 'class', 'default', 'define', 'import', 'if', 'elsif', 'else', 'inherits', 'node', 'and', 'or', 'undef', 'true', 'false', 'in', 'unless', ].each do |keyword| it "should handle '#{keyword}' as a keyword" do token = @lexer.tokenise(keyword).first token.type.should == keyword.upcase.to_sym token.value.should == keyword end end [ [:LBRACK, '['], [:RBRACK, ']'], [:LBRACE, '{'], [:RBRACE, '}'], [:LPAREN, '('], [:RPAREN, ')'], [:EQUALS, '='], [:ISEQUAL, '=='], [:GREATEREQUAL, '>='], [:GREATERTHAN, '>'], [:LESSTHAN, '<'], [:LESSEQUAL, '<='], [:NOTEQUAL, '!='], [:NOT, '!'], [:COMMA, ','], [:DOT, '.'], [:COLON, ':'], [:AT, '@'], [:LLCOLLECT, '<<|'], [:RRCOLLECT, '|>>'], [:LCOLLECT, '<|'], [:RCOLLECT, '|>'], [:SEMIC, ';'], [:QMARK, '?'], [:BACKSLASH, '\\'], [:FARROW, '=>'], [:PARROW, '+>'], [:APPENDS, '+='], [:PLUS, '+'], [:MINUS, '-'], [:DIV, '/'], [:TIMES, '*'], [:LSHIFT, '<<'], [:RSHIFT, '>>'], [:MATCH, '=~'], [:NOMATCH, '!~'], [:IN_EDGE, '->'], [:OUT_EDGE, '<-'], [:IN_EDGE_SUB, '~>'], [:OUT_EDGE_SUB, '<~'], ].each do |name, string| it "should have a token named '#{name.to_s}'" do token = @lexer.tokenise(string).first token.type.should == name token.value.should == string end end context ':CLASSREF' do it 'should match single capitalised alphanumeric term' do token = @lexer.tokenise('One').first token.type.should == :CLASSREF token.value.should == 'One' end it 'should match two capitalised alphanumeric terms sep by ::' do token = @lexer.tokenise('One::Two').first token.type.should == :CLASSREF token.value.should == 'One::Two' end it 'should match many capitalised alphanumeric terms sep by ::' do token = @lexer.tokenise('One::Two::Three::Four::Five').first token.type.should == :CLASSREF token.value.should == 'One::Two::Three::Four::Five' end it 'should match capitalised terms prefixed by ::' do token = @lexer.tokenise('::One').first token.type.should == :CLASSREF token.value.should == '::One' end end context ':NAME' do it 'should match lowercase alphanumeric terms' do token = @lexer.tokenise('one-two').first token.type.should == :NAME token.value.should == 'one-two' end it 'should match lowercase alphanumeric terms sep by ::' do token = @lexer.tokenise('one::two').first token.type.should == :NAME token.value.should == 'one::two' end it 'should match many lowercase alphanumeric terms sep by ::' do token = @lexer.tokenise('one::two::three::four::five').first token.type.should == :NAME token.value.should == 'one::two::three::four::five' end it 'should match lowercase alphanumeric terms prefixed by ::' do token = @lexer.tokenise('::1one::2two::3three').first token.type.should == :NAME token.value.should == '::1one::2two::3three' end end context ':NUMBER' do it 'should match numeric terms' do token = @lexer.tokenise('1234567890').first token.type.should == :NUMBER token.value.should == '1234567890' end it 'should match float terms' do token = @lexer.tokenise('12345.6789').first token.type.should == :NUMBER token.value.should == '12345.6789' end it 'should match hexadecimal terms' do token = @lexer.tokenise('0xCAFE1029').first token.type.should == :NUMBER token.value.should == '0xCAFE1029' end it 'should match float with exponent terms' do token = @lexer.tokenise('10e23').first token.type.should == :NUMBER token.value.should == '10e23' end it 'should match float with negative exponent terms' do token = @lexer.tokenise('10e-23').first token.type.should == :NUMBER token.value.should == '10e-23' end it 'should match float with exponent terms' do token = @lexer.tokenise('1.234e5').first token.type.should == :NUMBER token.value.should == '1.234e5' end end context ':COMMENT' do it 'should match everything on a line after #' do token = @lexer.tokenise('foo # bar baz')[2] token.type.should == :COMMENT token.value.should == 'bar baz' end end context ':MLCOMMENT' do it 'should match comments on a single line' do token = @lexer.tokenise('/* foo bar */').first token.type.should == :MLCOMMENT token.value.should == 'foo bar' end it 'should match comments on multiple lines' do token = @lexer.tokenise("/*\n * foo bar\n*/").first token.type.should == :MLCOMMENT token.value.should == 'foo bar' end end context ':SLASH_COMMENT' do it 'should match everyone on a line after //' do token = @lexer.tokenise('foo // bar baz')[2] token.type.should == :SLASH_COMMENT token.value.should == 'bar baz' end end context ':SSTRING' do it 'should match a single quoted string' do token = @lexer.tokenise("'single quoted string'").first token.type.should == :SSTRING token.value.should == 'single quoted string' end it "should match a single quoted string with an escaped '" do token = @lexer.tokenise(%q{'single quoted string with "\\'"'}).first token.type.should == :SSTRING token.value.should == 'single quoted string with "\\\'"' end it "should match a single quoted string with an escaped $" do token = @lexer.tokenise(%q{'single quoted string with "\$"'}).first token.type.should == :SSTRING token.value.should == 'single quoted string with "\\$"' end it "should match a single quoted string with an escaped ." do token = @lexer.tokenise(%q{'single quoted string with "\."'}).first token.type.should == :SSTRING token.value.should == 'single quoted string with "\\."' end it "should match a single quoted string with an escaped \\n" do token = @lexer.tokenise(%q{'single quoted string with "\n"'}).first token.type.should == :SSTRING token.value.should == 'single quoted string with "\\n"' end it "should match a single quoted string with an escaped \\" do token = @lexer.tokenise(%q{'single quoted string with "\\\\"'}).first token.type.should == :SSTRING token.value.should == 'single quoted string with "\\\\"' end it "should match an empty string" do token = @lexer.tokenise("''").first token.type.should == :SSTRING token.value.should == '' end it "should match an empty string ending with \\\\" do token = @lexer.tokenise("'foo\\\\'").first token.type.should == :SSTRING token.value.should == %{foo\\\\} end end context ':REGEX' do it 'should match anything enclosed in //' do token = @lexer.tokenise('/this is a regex/').first token.type.should == :REGEX token.value.should == 'this is a regex' end it 'should not match if there is \n in the regex' do token = @lexer.tokenise("/this is \n a regex/").first token.type.should_not == :REGEX end it 'should not consider \/ to be the end of the regex' do token = @lexer.tokenise('/this is \/ a regex/').first token.type.should == :REGEX token.value.should == 'this is \\/ a regex' end it 'should not match chained division' do tokens = @lexer.tokenise('$x = $a/$b/$c') tokens.select { |r| r.type == :REGEX }.should == [] end end context ':STRING' do it 'should parse strings with \\\\\\' do expect { @lexer.tokenise("exec { \"/bin/echo \\\\\\\"${environment}\\\\\\\"\": }") }.to_not raise_error(PuppetLint::LexerError) end end end puppet-lint-0.3.2/spec/puppet-lint/bin_spec.rb0000644000004100000410000001743012056100642021350 0ustar www-datawww-datarequire 'spec_helper' require 'rspec/mocks' require 'optparse' class CommandRun attr_accessor :stdout, :stderr, :exitstatus def initialize(args) out = StringIO.new err = StringIO.new $stdout = out $stderr = err PuppetLint.configuration.defaults @exitstatus = PuppetLint::Bin.new(args).run PuppetLint.configuration.defaults @stdout = out.string.strip @stderr = err.string.strip $stdout = STDOUT $stderr = STDERR end end describe PuppetLint::Bin do subject do if args.is_a? Array sane_args = args else sane_args = [args] end CommandRun.new(sane_args) end context 'when running normally' do let(:args) { 'spec/fixtures/test/manifests/init.pp' } its(:exitstatus) { should == 0 } end context 'when running without arguments' do let(:args) { [] } its(:exitstatus) { should == 1 } end context 'when asked to display version' do let(:args) { '--version' } its(:exitstatus) { should == 0 } its(:stdout) { should == "Puppet-lint #{PuppetLint::VERSION}" } end context 'when passed multiple files' do let(:args) { [ 'spec/fixtures/test/manifests/warning.pp', 'spec/fixtures/test/manifests/fail.pp', ] } its(:exitstatus) { should == 1 } its(:stdout) { should == [ 'WARNING: optional parameter listed before required parameter on line 2', 'ERROR: test::foo not in autoload module layout on line 2', ].join("\n") } end context 'when passed a malformed file' do let(:args) { 'spec/fixtures/test/manifests/malformed.pp' } its(:exitstatus) { should == 1 } its(:stdout) { should == 'ERROR: Syntax error (try running `puppet parser validate `) on line 1' } end context 'when limited to errors only' do let(:args) { [ '--error-level', 'error', 'spec/fixtures/test/manifests/warning.pp', 'spec/fixtures/test/manifests/fail.pp', ] } its(:exitstatus) { should == 1 } its(:stdout) { should match(/^ERROR/) } end context 'when limited to errors only' do let(:args) { [ '--error-level', 'warning', 'spec/fixtures/test/manifests/warning.pp', 'spec/fixtures/test/manifests/fail.pp', ] } its(:exitstatus) { should == 1 } its(:stdout) { should match(/^WARNING/) } end context 'when asked to display filenames ' do let(:args) { ['--with-filename', 'spec/fixtures/test/manifests/fail.pp'] } its(:exitstatus) { should == 1 } its(:stdout) { should match(%r{^spec/fixtures/test/manifests/fail\.pp -}) } end context 'when not asked to fail on warnings' do let(:args) { ['spec/fixtures/test/manifests/warning.pp'] } its(:exitstatus) { should == 0 } its(:stdout) { should match(/optional parameter/) } end context 'when asked to provide context to problems' do let(:args) { [ '--with-context', 'spec/fixtures/test/manifests/warning.pp', ] } its(:exitstatus) { should == 0 } its(:stdout) { should == [ 'WARNING: optional parameter listed before required parameter on line 2', '', " define test::warning($foo='bar', $baz) { }", ' ^', ].join("\n") } end context 'when asked to fail on warnings' do let(:args) { [ '--fail-on-warnings', 'spec/fixtures/test/manifests/warning.pp', ] } its(:exitstatus) { should == 1 } its(:stdout) { should match(/optional parameter/) } end context 'when used with an invalid option' do let(:args) { '--foo-bar-baz' } its(:exitstatus) { should == 1 } its(:stdout) { should match(/invalid option/) } end context 'when passed a file that does not exist' do let(:args) { 'spec/fixtures/test/manifests/enoent.pp' } its(:exitstatus) { should == 1 } its(:stdout) { should match(/specified file does not exist/) } end context 'when passed a directory' do let(:args) { 'spec/fixtures/' } its(:exitstatus) { should == 1 } its(:stdout) { should match(/^ERROR/) } end context 'when disabling a check' do let(:args) { [ '--no-autoloader_layout', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 0 } its(:stdout) { should == "" } end context 'when changing the log format' do context 'to print %{filename}' do let(:args) { [ '--log-format', '%{filename}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should == 'fail.pp' } end context 'to print %{path}' do let(:args) { [ '--log-format', '%{path}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should == 'spec/fixtures/test/manifests/fail.pp' } end context 'to print %{fullpath}' do let(:args) { [ '--log-format', '%{fullpath}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should match(%r{^/.+/spec/fixtures/test/manifests/fail\.pp$}) } end context 'to print %{linenumber}' do let(:args) { [ '--log-format', '%{linenumber}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should == '2' } end context 'to print %{kind}' do let(:args) { [ '--log-format', '%{kind}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should == 'error' } end context 'to print %{KIND}' do let(:args) { [ '--log-format', '%{KIND}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should == 'ERROR' } end context 'to print %{check}' do let(:args) { [ '--log-format', '%{check}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should == 'autoloader_layout' } end context 'to print %{message}' do let(:args) { [ '--log-format', '%{message}', 'spec/fixtures/test/manifests/fail.pp' ] } its(:exitstatus) { should == 1 } its(:stdout) { should == 'test::foo not in autoload module layout' } end context 'when loading options from a file' do let(:args) { 'spec/fixtures/test/manifests/fail.pp' } it 'should have ~/.puppet-lintrc as depreciated' do OptionParser.any_instance.stub(:load). with(File.expand_path('~/.puppet-lintrc')).and_return(true) OptionParser.any_instance.stub(:load). with(File.expand_path('~/.puppet-lint.rc')).and_return(false) OptionParser.any_instance.stub(:load). with('.puppet-lintrc').and_return(false) OptionParser.any_instance.stub(:load). with('.puppet-lint.rc').and_return(false) OptionParser.any_instance.stub(:load). with('/etc/puppet-lint.rc').and_return(false) msg = 'Depreciated: Found ~/.puppet-lintrc instead of ~/.puppet-lint.rc' subject.stderr.should == msg end it 'should have .puppet-lintrc as depreciated' do OptionParser.any_instance.stub(:load). with(File.expand_path('~/.puppet-lintrc')).and_return(false) OptionParser.any_instance.stub(:load). with(File.expand_path('~/.puppet-lint.rc')).and_return(false) OptionParser.any_instance.stub(:load). with('.puppet-lintrc').and_return(true) OptionParser.any_instance.stub(:load). with('.puppet-lint.rc').and_return(false) OptionParser.any_instance.stub(:load). with('/etc/puppet-lint.rc').and_return(false) msg = 'Depreciated: Read .puppet-lintrc instead of .puppet-lint.rc' subject.stderr.should == msg end end end end puppet-lint-0.3.2/spec/puppet-lint/lexer/0000755000004100000410000000000012056100642020353 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/lexer/token_spec.rb0000644000004100000410000000071512056100642023035 0ustar www-datawww-datarequire 'spec_helper' describe PuppetLint::Lexer::Token do subject do PuppetLint::Lexer::Token.new(:NAME, 'foo', 1, 2) end it { should respond_to(:type) } it { should respond_to(:value) } it { should respond_to(:line) } it { should respond_to(:column) } its(:type) { should == :NAME } its(:value) { should == 'foo' } its(:line) { should == 1 } its(:column) { should == 2 } its(:inspect) { should == "" } end puppet-lint-0.3.2/spec/puppet-lint/configuration_spec.rb0000644000004100000410000000251412056100642023444 0ustar www-datawww-datarequire 'spec_helper' describe PuppetLint::Configuration do subject { PuppetLint::Configuration.new } it 'should create check methods on the fly' do method = Proc.new { true } subject.add_check('foo', &method) subject.should respond_to(:foo_enabled?) subject.should_not respond_to(:bar_enabled?) subject.should respond_to(:enable_foo) subject.should respond_to(:disable_foo) subject.disable_foo subject.settings['foo_disabled'].should == true subject.foo_enabled?.should == false subject.enable_foo subject.settings['foo_disabled'].should == false subject.foo_enabled?.should == true end it 'should know what checks have been added' do method = Proc.new { true } subject.add_check('foo', &method) subject.checks.should include('foo') end it 'should respond nil to unknown config options' do subject.foobarbaz.should == nil end it 'should create options on the fly' do subject.add_option('bar') subject.bar.should == nil subject.bar = 'aoeui' subject.bar.should == 'aoeui' end it 'should be able to set sane defaults' do subject.defaults subject.settings.should == { 'with_filename' => false, 'fail_on_warnings' => false, 'error_level' => :all, 'log_format' => '', 'with_context' => false, } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/0000755000004100000410000000000012056100642020715 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/0000755000004100000410000000000012056100642023507 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/right_to_left_relationship_spec.rb0000644000004100000410000000106412056100642032461 0ustar www-datawww-datarequire 'spec_helper' describe 'right_to_left_relationship' do describe 'chain 2 resources left to right' do let(:code) { "Class[foo] -> Class[bar]" } its(:problems) { should be_empty } end describe 'chain 2 resources right to left' do let(:code) { "Class[foo] <- Class[bar]" } its(:problems) { should have_problem({ :kind => :warning, :message => "right-to-left (<-) relationship", :linenumber => 1, :column => 12, }) should_not have_problem :kind => :error } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb0000644000004100000410000000367112056100642030033 0ustar www-datawww-datarequire 'spec_helper' describe 'variable_scope' do describe 'class with no variables declared accessing top scope' do let(:code) { " class foo { $bar = $baz }" } its(:problems) { should have_problem({ :kind => :warning, :message => "top-scope variable being used without an explicit namespace", :linenumber => 3, :column => 16, }) should_not have_problem :kind => :error } end describe 'class with no variables declared accessing top scope explicitly' do let(:code) { " class foo { $bar = $::baz }" } its(:problems) { should be_empty } end describe 'class with variables declared accessing local scope' do let(:code) { " class foo { $bar = 1 $baz = $bar }" } its(:problems) { should be_empty } end describe 'class with parameters accessing local scope' do let(:code) { " class foo($bar='UNSET') { $baz = $bar }" } its(:problems) { should be_empty } end describe 'defined type with no variables declared accessing top scope' do let(:code) { " define foo() { $bar = $fqdn }" } its(:problems) { should have_problem({ :kind => :warning, :message => "top-scope variable being used without an explicit namespace", :linenumber => 3, :column => 16, }) should_not have_problem :kind => :error } end describe 'defined type with no variables declared accessing top scope explicitly' do let(:code) { " define foo() { $bar = $::fqdn }" } its(:problems) { should be_empty } end describe '$name should be auto defined' do let(:code) { " define foo() { $bar = $name $baz = $title $gronk = $module_name $meep = $1 }" } its(:problems) { should be_empty } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/class_parameter_defaults_spec.rb0000644000004100000410000000304512056100642032104 0ustar www-datawww-datarequire 'spec_helper' describe 'class_parameter_defaults' do describe 'parameterised class with a default value' do let(:code) { "class foo($bar, $baz='gronk') { }" } its(:problems) { should only_have_problem({ :kind => :warning, :message => 'parameterised class parameter without a default value', :linenumber => 1, :column => 11, }) } end describe 'parameterised class with multiple params with a default value' do let(:code) { "class foo($bar, $baz, $gronk) { }" } its(:problems) do should have_problem({ :kind => :warning, :message => 'parameterised class parameter without a default value', :linenumber => 1, :column => 11, }) should have_problem({ :kind => :warning, :message => 'parameterised class parameter without a default value', :linenumber => 1, :column => 17, }) should have_problem({ :kind => :warning, :message => 'parameterised class parameter without a default value', :linenumber => 1, :column => 23, }) end end describe 'class without parameters' do let(:code) {" class myclass { if ( $::lsbdistcodename == 'squeeze' ) { #TODO } } "} its(:problems) { should == [] } end describe 'parameterised class with a function value' do let(:code) { "class foo($bar = baz($gronk)) { }" } its(:problems) { should == [] } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/nested_classes_or_defines_spec.rb0000644000004100000410000000255412056100642032250 0ustar www-datawww-datarequire 'spec_helper' describe 'nested_classes_or_defines' do describe 'class on its own' do let(:code) { "class foo { }" } its(:problems) { should be_empty } end describe 'class inside a class' do let(:code) { " class foo { class bar { } }" } its(:problems) { should have_problem({ :kind => :warning, :message => "class defined inside a class", :linenumber => 3, :column => 9, }) should_not have_problem :kind => :error } end describe 'instantiating a parametised class inside a class' do let(:code) { " class bar { class { 'foo': bar => 'foobar' } }" } its(:problems) { should be_empty } end describe 'instantiating a parametised class inside a define' do let(:code) { " define bar() { class { 'foo': bar => 'foobar' } }" } its(:problems) { should be_empty } end describe 'define inside a class' do let(:code) { " class foo { define bar() { } }" } its(:problems) { should have_problem({ :kind => :warning, :message => "define defined inside a class", :linenumber => 3, :column => 9, }) should_not have_problem :kind => :error } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/names_containing_dash_spec.rb0000644000004100000410000000223212056100642031360 0ustar www-datawww-datarequire 'spec_helper' describe 'names_containing_dash' do describe 'module named foo-bar' do let(:code) { 'class foo-bar { }' } let(:fullpath) { '/etc/puppet/modules/foo-bar/manifests/init.pp' } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'class name containing a dash', :linenumber => 1, :column => 7, }) end end describe 'define named foo-bar' do let(:code) { 'define foo::foo-bar { }' } let(:fullpath) { '/etc/puppet/modules/foo/manifests/foo-bar.pp' } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'defined type name containing a dash', :linenumber => 1, :column => 8, }) end end describe 'class named bar-foo' do let(:code) { 'class foo::bar-foo { }' } let(:fullpath) { '/etc/puppet/modules/foo/manifests/bar-foo.pp' } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'class name containing a dash', :linenumber => 1, :column => 7, }) end end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/autoloader_layout_spec.rb0000644000004100000410000000251112056100642030601 0ustar www-datawww-datarequire 'spec_helper' describe 'autoloader_layout' do describe 'foo::bar in foo/manifests/bar.pp' do let(:code) { "class foo::bar { }" } let(:fullpath) { '/etc/puppet/modules/foo/manifests/bar.pp' } its(:problems) { should be_empty } end describe 'foo::bar::baz in foo/manifests/bar/baz.pp' do let(:code) { 'define foo::bar::baz() { }' } let(:fullpath) { '/etc/puppet/modules/foo/manifests/bar/baz.pp' } its(:problems) { should be_empty } end describe 'foo in foo/manifests/init.pp' do let(:code) { 'class foo { }' } let(:fullpath) { '/etc/puppet/modules/foo/manifests/init.pp' } its(:problems) { should be_empty } end describe 'foo::bar in foo/manifests/init.pp' do let(:code) { 'class foo::bar { }' } let(:fullpath) { '/etc/puppet/modules/foo/manifests/init.pp' } its(:problems) { should only_have_problem({ :kind => :error, :message => "foo::bar not in autoload module layout", :linenumber => 1, :column => 7, }) } end describe 'foo included in bar/manifests/init.pp' do let(:code) { " class bar { class {'foo': someparam => 'somevalue', } } " } let(:fullpath) { '/etc/puppet/modules/bar/manifests/init.pp' } its(:problems) { should be_empty } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/class_inherits_from_params_class_spec.rb0000644000004100000410000000132312056100642033632 0ustar www-datawww-datarequire 'spec_helper' describe 'class_inherits_from_params_class' do describe 'parameterised class that inherits from a params class' do let(:code) { " # commented class foo($bar = $name) inherits foo::params { }" } its(:problems) { should have_problem({ :kind => :warning, :message => "class inheriting from params class", :linenumber => 3, :column => 40, }) should_not have_problem :kind => :error } end describe 'class without parameters' do let(:code) {" class myclass { if ( $::lsbdistcodename == 'squeeze' ) { #TODO } } "} its(:problems) { should == [] } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb0000644000004100000410000000305012056100642030217 0ustar www-datawww-datarequire 'spec_helper' describe 'parameter_order' do describe 'define with attrs in order' do let(:code) { "define foo($bar, $baz='gronk') { }" } its(:problems) { should be_empty } end describe 'define with parameter that calls a function' do let(:code) { "define foo($bar=extlookup($name)) {}" } its(:problems) { should == [] } end describe 'define with attrs out of order' do let(:code) { "define foo($bar='baz', $gronk) { }" } its(:problems) { should have_problem({ :kind => :warning, :message => "optional parameter listed before required parameter", :linenumber => 1, :column => 24, }) should_not have_problem :kind => :error } end describe 'class/define parameter set to another variable' do let(:code) { " define foo($bar, $baz = $name, $gronk=$::fqdn) { }" } its(:problems) { should be_empty } end describe 'class/define parameter set to another variable with incorrect order' do let(:code) { " define foo($baz = $name, $bar, $gronk=$::fqdn) { }" } its(:problems) { should have_problem({ :kind => :warning, :message => "optional parameter listed before required parameter", :linenumber => 2, :column => 32, }) should_not have_problem :kind => :error } end describe 'issue-101' do let(:code) { " define b ( $foo, $bar='', $baz={} ) { } " } its(:problems) { should == [] } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_classes/inherits_across_namespaces_spec.rb0000644000004100000410000000143612056100642032450 0ustar www-datawww-datarequire 'spec_helper' describe 'inherits_across_namespaces' do describe 'class inheriting from parent in same module namespace' do let(:code) { "class foo::bar inherits foo { }" } its(:problems) { should be_empty } end describe 'class inheriting from sister in same module namespace' do let(:code) { "class foo::bar inherits foo::baz { }" } its(:problems) { should be_empty } end describe 'class inheriting from another module namespace' do let(:code) { "class foo::bar inherits baz { }" } its(:problems) { should have_problem({ :kind => :warning, :message => "class inherits across module namespaces", :linenumber => 1, :column => 25, }) should_not have_problem :kind => :error } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_variables/0000755000004100000410000000000012056100642024022 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_variables/variable_contains_dash_spec.rb0000644000004100000410000000111612056100642032042 0ustar www-datawww-datarequire 'spec_helper' describe 'variable_contains_dash' do describe 'a variable containing a dash' do let(:code) { '$foo-bar' } its(:problems) { should have_problem({ :kind => :warning, :message => 'variable contains a dash', :linenumber => 1, :column => 1, }) } end describe 'variable containing a dash' do let(:code) { '" $foo-bar"' } its(:problems) { should have_problem({ :kind => :warning, :message => 'variable contains a dash', :linenumber => 1, :column => 3, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_conditionals/0000755000004100000410000000000012056100642024540 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb0000644000004100000410000000213512056100642032302 0ustar www-datawww-datarequire 'spec_helper' describe 'case_without_default' do describe 'case statement with a default case' do let(:code) { " case $foo { bar: { } default: { } }" } its(:problems) { should be_empty } end describe 'case statement without a default case' do let(:code) { " case $foo { bar: { } baz: { } }" } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'case statement without a default case', :linenumber => 2, :column => 7, }) end end describe 'issue-117' do let(:code) { " $mem = inline_template('<% mem,unit = scope.lookupvar(\'::memorysize\').split mem = mem.to_f # Normalize mem to bytes case unit when nil: mem *= (1<<0) when \'kB\': mem *= (1<<10) when \'MB\': mem *= (1<<20) when \'GB\': mem *= (1<<30) when \'TB\': mem *= (1<<40) end %><%= mem.to_i %>') "} its(:problems) { should == [] } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_conditionals/selector_inside_resource_spec.rb0000644000004100000410000000122612056100642033162 0ustar www-datawww-datarequire 'spec_helper' describe 'selector_inside_resource' do describe 'resource with a selector' do let(:code) { " file { 'foo': ensure => $bar ? { true => present, default => absent, }, }" } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'selector inside resource block', :linenumber => 3, :column => 16, }) end end describe 'resource with a variable as a attr value' do let(:code) { " file { 'foo', ensure => $bar, }" } its(:problems) { should be_empty } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_documentation/0000755000004100000410000000000012056100642024723 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb0000644000004100000410000000161712056100642031140 0ustar www-datawww-datarequire 'spec_helper' describe 'documentation' do describe 'undocumented class' do let(:code) { "class test {}" } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'class not documented', :linenumber => 1, :column => 1, }) end end describe 'documented class' do let(:code) { " # foo class test {} "} its(:problems) { should == [] } end describe 'undocumented defined type' do let(:code) { "define test {}" } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'defined type not documented', :linenumber => 1, :column => 1, }) end end describe 'documented defined type' do let(:code) { " # foo define test {} "} its(:problems) { should == [] } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_comments/0000755000004100000410000000000012056100642023677 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb0000644000004100000410000000050212056100642030252 0ustar www-datawww-datarequire 'spec_helper' describe 'slash_comments' do describe 'slash comments' do let(:code) { "// foo" } its(:problems) do should only_have_problem({ :kind => :warning, :message => '// comment found', :linenumber => 1, :column => 1, }) end end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_comments/star_comments_spec.rb0000644000004100000410000000054012056100642030113 0ustar www-datawww-datarequire 'spec_helper' describe 'star_comments' do describe 'slash asterisk comment' do let(:code) { " /* foo */ "} its(:problems) do should only_have_problem({ :kind => :warning, :message => '/* */ comment found', :linenumber => 2, :column => 7, }) end end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_resources/0000755000004100000410000000000012056100642024064 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_resources/file_mode_spec.rb0000644000004100000410000000220112056100642027341 0ustar www-datawww-datarequire 'spec_helper' describe 'file_mode' do describe '3 digit file mode' do let(:code) { "file { 'foo': mode => '777' }" } its(:problems) { should only_have_problem :kind => :warning, :message => "mode should be represented as a 4 digit octal value or symbolic mode", :linenumber => 1 } end describe '4 digit file mode' do let(:code) { "file { 'foo': mode => '0777' }" } its(:problems) { should be_empty } end describe 'file mode as a variable' do let(:code) { "file { 'foo': mode => $file_mode }" } its(:problems) { should be_empty } end describe 'symbolic file mode' do let(:code) { "file { 'foo': mode => 'u=rw,og=r' }" } its(:problems) { should be_empty } end describe 'file mode undef unquoted' do let(:code) { "file { 'foo': mode => undef }" } its(:problems) { should be_empty } end describe 'file mode undef quoted' do let(:code) { "file { 'foo': mode => 'undef' }" } its(:problems) { should only_have_problem :kind => :warning, :message => "mode should be represented as a 4 digit octal value or symbolic mode", :linenumber => 1 } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_resources/ensure_not_symlink_target_spec.rb0000644000004100000410000000115212056100642032717 0ustar www-datawww-datarequire 'spec_helper' describe 'ensure_not_symlink_target' do describe 'file resource creating a symlink with seperate target attr' do let(:code) { " file { 'foo': ensure => link, target => '/foo/bar', }" } its(:problems) { should be_empty } end describe 'file resource creating a symlink with target specified in ensure' do let(:code) { " file { 'foo': ensure => '/foo/bar', }" } its(:problems) { should only_have_problem :kind => :warning, :message => "symlink target specified in ensure attr", :linenumber => 3 } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb0000644000004100000410000000041612056100642031273 0ustar www-datawww-datarequire 'spec_helper' describe 'unquoted_file_mode' do describe '4 digit unquoted file mode' do let(:code) { "file { 'foo': mode => 0777 }" } its(:problems) { should only_have_problem :kind => :warning, :message => "unquoted file mode" } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb0000644000004100000410000000452612056100642032406 0ustar www-datawww-datarequire 'spec_helper' describe 'unquoted_resource_title' do describe 'quoted resource title on single line resource' do let(:code) { "file { 'foo': }" } its(:problems) { should be_empty } end describe 'unquoted resource title on single line resource' do let(:code) { "file { foo: }" } its(:problems) { should only_have_problem :kind => :warning, :message => "unquoted resource title", :linenumber => 1 } end describe 'quoted resource title on multi line resource' do let(:code) { " file { 'foo': }" } its(:problems) { should be_empty } end describe 'unquoted resource title on multi line resource' do let(:code) { " file { foo: }" } its(:problems) { should only_have_problem :kind => :warning, :message => "unquoted resource title", :linenumber => 2 } end describe 'condensed resources with quoted titles' do let(:code) { " file { 'foo': ; 'bar': ; }" } its(:problems) { should be_empty } end describe 'condensed resources with an unquoted title' do let(:code) { " file { 'foo': ; bar: ; }" } its(:problems) { should only_have_problem :kind => :warning, :message => "unquoted resource title", :linenumber => 4 } end describe 'single line resource with an array of titles (all quoted)' do let(:code) { "file { ['foo', 'bar']: }" } its(:problems) { should be_empty } end describe 'resource inside a case statement' do let(:code) { " case $ensure { 'absent': { file { \"some_file_${name}\": ensure => absent, } } }" } its(:problems) { should == [] } end describe 'issue #116' do let(:code) { " $config_file_init = $::operatingsystem ? { /(?i:Debian|Ubuntu|Mint)/ => '/etc/default/foo', default => '/etc/sysconfig/foo', }" } its(:problems) { should == [] } end describe 'case statement' do let(:code) { %{ case $operatingsystem { centos: { $version = '1.2.3' } solaris: { $version = '3.2.1' } default: { fail("Module ${module_name} is not supported on ${operatingsystem}") } }} } its(:problems) { should == [] } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb0000644000004100000410000000171112056100642031313 0ustar www-datawww-datarequire 'spec_helper' describe 'ensure_first_param' do describe 'ensure as only attr in a single line resource' do let(:code) { "file { 'foo': ensure => present }" } its(:problems) { should be_empty } end describe 'ensure as only attr in a multi line resource' do let(:code) { " file { 'foo': ensure => present, }" } its(:problems) { should be_empty } end describe 'ensure as second attr in a multi line resource' do let(:code) { " file { 'foo': mode => '0000', ensure => present, }" } its(:problems) { should only_have_problem :kind => :warning, :message => "ensure found on line but it's not the first attribute", :linenumber => 4 } end describe 'ensure as first attr in a multi line resource' do let(:code) { " file { 'foo': ensure => present, mode => '0000', }" } its(:problems) { should be_empty } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_resources/duplicate_params_spec.rb0000644000004100000410000000374112056100642030745 0ustar www-datawww-datarequire 'spec_helper' describe 'duplicate_params' do describe 'resource with duplicate parameters' do let(:code) { " file { '/tmp/foo': ensure => present, foo => bar, baz => gronk, foo => meh, }" } its(:problems) { should only_have_problem({ :kind => :error, :message => 'duplicate parameter found in resource', :linenumber => 6, :column => 9, }) } end describe 'bug #145: resource with a hash and no duplicate parameters' do let (:code) { " class {'fooname': hashes => [ { foo => 'bar01',}, { foo => 'bar02', }, ], }" } its (:problems) { should be_empty } end describe 'bug #145: resource with a hash and duplicate parameters in subhash' do let (:code) { " class {'fooname': hashes => [ { foo => 'bar01', foo => 'bar02', }, ], }" } its (:problems) { should only_have_problem({ :kind => :error, :message => 'duplicate parameter found in resource', :linenumber => 5, :column => 13, }) } end describe 'bug #145: resource with a hash and duplicate parameters in parent type' do let (:code) { " class {'fooname': hashes => [ { foo => 'bar01', }, { foo => 'bar02', }, ], something => { hash => 'mini', }, hashes => 'dupe', }" } its (:problems) { should only_have_problem({ :kind => :error, :message => 'duplicate parameter found in resource', :linenumber => 8, :column => 9, }) } end describe 'bug #145: more hash tests and no duplicate parameters' do let (:code) { " class test { $foo = { param => 'value', } $bar = { param => 'bar', } }" } its (:problems) { should be_empty } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_strings/0000755000004100000410000000000012056100642023543 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb0000644000004100000410000000125612056100642031612 0ustar www-datawww-datarequire 'spec_helper' describe 'variables_not_enclosed' do describe 'variable not enclosed in {}' do let(:code) { '" $gronk"' } its(:problems) { should only_have_problem({ :kind => :warning, :message => 'variable not enclosed in {}', :linenumber => 1, :column => 3, }) } end describe 'variable not enclosed in {} after many tokens' do let(:code) { ("'groovy'\n" * 20) + '" $gronk"' } its(:problems) { should only_have_problem({ :kind => :warning, :message => 'variable not enclosed in {}', :linenumber => 21, :column => 3, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb0000644000004100000410000000432412056100642031511 0ustar www-datawww-datarequire 'spec_helper' describe 'double_quoted_strings' do describe 'double quoted string containing a variable inside single quotes' do let(:code) { "exec { \"/usr/bin/wget -O - '${source}' | /usr/bin/apt-key add -\": }" } its(:problems) { should be_empty } end describe 'multiple strings in a line' do let(:code) { "\"aoeu\" '${foo}'" } its(:problems) { should have_problem({ :kind => :warning, :message => 'double quoted string containing no variables', :linenumber => 1, :column => 1, }) } end describe 'double quoted string nested in a single quoted string' do let(:code) { "'grep \"status=sent\" /var/log/mail.log'" } its(:problems) { should be_empty } end describe 'double quoted string after a comment' do let(:code) { "service { 'foo': } # \"bar\"" } its(:problems) { should be_empty } end describe 'double quoted string containing newline but no variables' do let(:code) { %{"foo\n"} } its(:problems) { should be_empty } end describe 'double quoted string with backslash for continuation' do let(:code) { %{ class puppet::master::maintenance ( ) { cron { 'puppet_master_reports_cleanup': command => "/usr/bin/find /var/lib/puppet/reports -type f -mtime +15 \ -delete && /usr/bin/find /var/lib/puppet/reports -mindepth 1 \ -empty -type d -delete", minute => '15', hour => '5', } } } } its(:problems) { should == [] } end describe 'double quoted true' do let(:code) { "class { 'foo': boolFlag => \"true\" }" } its(:problems) { should have_problem({ :kind => :warning, :message => 'double quoted string containing no variables', :linenumber => 1, :column => 28, }) } end describe 'double quoted false' do let(:code) { "class { 'foo': boolFlag => \"false\" }" } its(:problems) { should have_problem({ :kind => :warning, :message => 'double quoted string containing no variables', :linenumber => 1, :column => 28, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_strings/single_quote_string_with_variables_spec.rb0000644000004100000410000000060312056100642034250 0ustar www-datawww-datarequire 'spec_helper' describe 'single_quote_string_with_variables' do describe 'multiple strings in a line' do let(:code) { "\"aoeu\" '${foo}'" } its(:problems) { should have_problem({ :kind => :error, :message => 'single quoted string containing a variable found', :linenumber => 1, :column => 8, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_strings/quoted_booleans_spec.rb0000644000004100000410000000237012056100642030267 0ustar www-datawww-datarequire 'spec_helper' describe 'quoted_booleans' do describe 'quoted false' do let(:code) { "class { 'foo': boolFlag => 'false' }" } its(:problems) { should only_have_problem({ :kind => :warning, :message => 'quoted boolean value found', :linenumber => 1, :column => 28, }) } end describe 'quoted true' do let(:code) { "class { 'foo': boolFlag => 'true' }" } its(:problems) { should only_have_problem({ :kind => :warning, :message => 'quoted boolean value found', :linenumber => 1, :column => 28, }) } end describe 'double quoted true' do let(:code) { "class { 'foo': boolFlag => \"true\" }" } its(:problems) { should have_problem({ :kind => :warning, :message => 'quoted boolean value found', :linenumber => 1, :column => 28, }) } end describe 'double quoted false' do let(:code) { "class { 'foo': boolFlag => \"false\" }" } its(:problems) { should have_problem({ :kind => :warning, :message => 'quoted boolean value found', :linenumber => 1, :column => 28, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb0000644000004100000410000000055312056100642031321 0ustar www-datawww-datarequire 'spec_helper' describe 'only_variable_string' do describe 'string containing only a variable' do let(:code) { '"${foo}"' } its(:problems) { should only_have_problem({ :kind => :warning, :message => 'string containing only a variable', :linenumber => 1, :column => 3, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_whitespace/0000755000004100000410000000000012056100642024206 5ustar www-datawww-datapuppet-lint-0.3.2/spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb0000644000004100000410000000052312056100642031572 0ustar www-datawww-datarequire 'spec_helper' describe 'trailing_whitespace' do describe 'line with trailing whitespace' do let(:code) { "foo " } its(:problems) { should have_problem({ :kind => :error, :message => 'trailing whitespace found', :linenumber => 1, :column => 4, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_whitespace/2sp_soft_tabs_spec.rb0000644000004100000410000000057512056100642030324 0ustar www-datawww-datarequire 'spec_helper' describe '2sp_soft_tabs' do describe 'line indented by 3 spaces' do let(:code) { " file { 'foo': foo => bar, }" } its(:problems) { should have_problem({ :kind => :error, :message => 'two-space soft tabs not used', :linenumber => 3, :column => 1, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb0000644000004100000410000000175712056100642027027 0ustar www-datawww-data# encoding: utf-8 require 'spec_helper' describe '80chars' do describe 'file resource with a source line > 80c' do let(:code) { " file { source => 'puppet:///modules/certificates/etc/ssl/private/wildcard.example.com.crt', }" } its(:problems) { should be_empty } end describe 'length of lines with UTF-8 characters' do let(:code) { " # ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ # ┃ Configuration ┃ # ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛" } its(:problems) { should be_empty } end describe '81 character line' do let(:code) { 'a' * 81 } its(:problems) { should have_problem({ :kind => :warning, :message => 'line has more than 80 characters', :linenumber => 1, :column => 80, }) } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb0000644000004100000410000001110312056100642030731 0ustar www-datawww-datarequire 'spec_helper' describe 'arrow_alignment' do describe 'selectors inside a resource' do let(:code) { " file { 'foo': ensure => $ensure, require => $ensure ? { present => Class['tomcat::install'], absent => undef; }, foo => bar, }" } its(:problems) { should be_empty } end describe 'selectors in the middle of a resource' do let(:code) { " file { 'foo': ensure => $ensure ? { present => directory, absent => undef, }, owner => 'tomcat6', }" } its(:problems) { should be_empty } end describe 'selector inside a resource' do let(:code) { " ensure => $ensure ? { present => directory, absent => undef, }, owner => 'foo4', group => 'foo4', mode => '0755'," } its(:problems) { should be_empty } end describe 'selector inside a hash inside a resource' do let(:code) { " server => { ensure => ensure => $ensure ? { present => directory, absent => undef, }, ip => '192.168.1.1' }, owner => 'foo4', group => 'foo4', mode => '0755'," } its(:problems) { should be_empty } end describe 'issue #37' do let(:code) { " class { 'lvs::base': virtualeservers => { '192.168.2.13' => { vport => '11025', service => 'smtp', scheduler => 'wlc', protocol => 'tcp', checktype => 'external', checkcommand => '/path/to/checkscript', real_servers => { 'server01' => { real_server => '192.168.2.14', real_port => '25', forwarding => 'masq', }, 'server02' => { real_server => '192.168.2.15', real_port => '25', forwarding => 'masq', } } } } }" } its(:problems) { should be_empty } end describe 'single resource with a misaligned =>' do let(:code) { " file { '/tmp/foo': foo => 1, bar => 2, gronk => 3, baz => 4, meh => 5, }" } its(:problems) do should have_problem({ :kind => :warning, :message => 'indentation of => is not properly aligned', :linenumber => 5, :column => 15, }) should have_problem({ :kind => :warning, :message => 'indentation of => is not properly aligned', :linenumber => 6, :column => 14, }) end end describe 'complex resource with a misaligned =>' do let(:code) { " file { '/tmp/foo': foo => 1, bar => $baz ? { gronk => 2, meh => 3, }, meep => 4, bah => 5, }" } its(:problems) do should have_problem({ :kind => :warning, :message => 'indentation of => is not properly aligned', :linenumber => 4, :column => 14, }) should have_problem({ :kind => :warning, :message => 'indentation of => is not properly aligned', :linenumber => 6, :column => 15, }) should have_problem({ :kind => :warning, :message => 'indentation of => is not properly aligned', :linenumber => 8, :column => 14, }) end end describe 'multi-resource with a misaligned =>' do let(:code) { " file { '/tmp/foo': ; '/tmp/bar': foo => 'bar'; '/tmp/baz': gronk => 'bah', meh => 'no' }" } its(:problems) do should only_have_problem({ :kind => :warning, :message => 'indentation of => is not properly aligned', :linenumber => 8, :column => 15, }) end end describe 'multiple single line resources' do let(:code) { " file { 'foo': ensure => file } package { 'bar': ensure => present }" } its(:problems) { should be_empty } end describe 'resource with unaligned => in commented line' do let(:code) { " file { 'foo': ensure => directory, # purge => true, }" } its(:problems) { should be_empty } end describe 'single line resource spread out on multiple lines' do let(:code) {" file { 'foo': ensure => present, }" } its(:problems) { should == [] } end end puppet-lint-0.3.2/spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb0000644000004100000410000000050012056100642027467 0ustar www-datawww-datarequire 'spec_helper' describe 'hard_tabs' do describe 'hard tabs indents' do let(:code) { "\tfoo => bar," } its(:problems) { should have_problem({ :kind => :error, :message => 'tab character found', :linenumber => 1, :column => 1, }) } end end puppet-lint-0.3.2/spec/puppet-lint_spec.rb0000644000004100000410000000077012056100642020577 0ustar www-datawww-datarequire 'spec_helper' describe PuppetLint do subject { PuppetLint.new } it 'should accept manifests as a string' do subject.code = "class foo { }" subject.data.should_not be_nil end it 'should have support for % with a hash' do string = 'replace %{hash}' % {:hash => 'replaced'} string.should match 'replace replaced' end it 'should not break regular % support' do string = 'replace %s %s' % ['get','replaced'] string.should match 'replace get replaced' end end puppet-lint-0.3.2/spec/spec_helper.rb0000644000004100000410000000654012056100642017576 0ustar www-datawww-datarequire 'rspec/autorun' require 'puppet-lint' module RSpec module LintExampleGroup def subject klass = PuppetLint::Checks.new fileinfo = {} fileinfo[:fullpath] = self.respond_to?(:fullpath) ? fullpath : '' klass.load_data(fileinfo, code) klass.send("lint_check_#{self.class.top_level_description}") klass end end end RSpec.configure do |c| c.mock_framework = :rspec c.include RSpec::LintExampleGroup, :type => :lint, :example_group => { :file_path => Regexp.compile(%w{spec puppet-lint plugins}.join('[\\\/]')) } end #class PuppetLint::Warning < Exception; end #class PuppetLint::Error < Exception; end #PuppetLint::CheckPlugin.any_instance.stub(:warn) do |arg| # raise PuppetLint::Warning #end #PuppetLint::CheckPlugin.any_instance.stub(:error) do |arg| # raise PuppetLint::Error #end # filter_array_of_hashes(array, filter) -> an_array # # Filters out hashes by applying filter_hash to each hash # in the array. All set value/key pairs in filter_hash must # match before a hash is allowed. # Returns all hashes that matched in an array. # # filter_array_of_hashes( # [ # {:filter => 1, :name => 'one'}, # {:filter => 2, :name => 'two'}, # {:filter => 3, :name => 'three'}, # ], # { :filter => 2 } # ) # => [{:filter=>2, :name=>"two"}] # # filter_array_of_hashes([{:f => 1}, {:f => 2}], {}) # => [{:f=>1}, {:f=>2}] # def filter_array_of_hashes(array_of_hashes, filter_hash) array_of_hashes.select { |hash_to_check| val = true filter_hash.each do |k,v| if ! hash_to_check.key?(k) val = false break elsif hash_to_check[k].to_s != v.to_s val = false break end end val } end RSpec::Matchers.define :have_problem do |filter| match do |problems| filter_array_of_hashes(problems, filter).length > 0 end failure_message_for_should do |problems| message = "could not find any problems matching the filter." message << " * filter = #{filter.inspect} * problems = [ " problems.each { |prob| message << " #{prob.inspect}," } message << " ]" message end failure_message_for_should_not do |problems| message = "some problems matched the filter." message << " * filter = #{filter.inspect} * matched = [ " filter_array_of_hashes(problems, filter).each { |prob| message << " #{prob.inspect}," } message << " ]" message end end RSpec::Matchers.define :only_have_problem do |filter| match do |actual| res = filter_array_of_hashes(actual, filter) res.length == actual.length && res.length == 1 end failure_message_for_should do |problems| filtered_problems = filter_array_of_hashes(actual, filter) if filtered_problems.length > 1 message = "Multiple problems found matching the filter." else left = problems - filter_array_of_hashes(actual, filter) message = "There were problems not matching filter." message << " * filter = #{filter.inspect} * unmatched = [ " left.each { |prob| message << " #{prob.inspect}," } message << " ]" end message end failure_message_for_should_not do |problems| message = "There were no problems found besides the ones matched with filter." message << " * filter = #{filter.inspect} " message end end puppet-lint-0.3.2/spec/fixtures/0000755000004100000410000000000012056100642016624 5ustar www-datawww-datapuppet-lint-0.3.2/spec/fixtures/test/0000755000004100000410000000000012056100642017603 5ustar www-datawww-datapuppet-lint-0.3.2/spec/fixtures/test/manifests/0000755000004100000410000000000012056100642021574 5ustar www-datawww-datapuppet-lint-0.3.2/spec/fixtures/test/manifests/warning.pp0000644000004100000410000000006112056100642023577 0ustar www-datawww-data# foo define test::warning($foo='bar', $baz) { } puppet-lint-0.3.2/spec/fixtures/test/manifests/init.pp0000644000004100000410000000002512056100642023075 0ustar www-datawww-data# foo class test { } puppet-lint-0.3.2/spec/fixtures/test/manifests/fail.pp0000644000004100000410000000003212056100642023043 0ustar www-datawww-data# foo class test::foo { } puppet-lint-0.3.2/spec/fixtures/test/manifests/malformed.pp0000644000004100000410000000004612056100642024103 0ustar www-datawww-dataclass { 'apacheds': master => true' } puppet-lint-0.3.2/puppet-lint.gemspec0000644000004100000410000000147612056100642017657 0ustar www-datawww-data$:.push File.expand_path("../lib", __FILE__) require 'puppet-lint/version' Gem::Specification.new do |s| s.name = 'puppet-lint' s.version = PuppetLint::VERSION s.homepage = 'https://github.com/rodjek/puppet-lint/' s.summary = 'Ensure your Puppet manifests conform with the Puppetlabs style guide' s.description = 'Checks your Puppet manifests against the Puppetlabs style guide and alerts you to any discrepancies.' s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.add_development_dependency 'rspec' s.add_development_dependency 'rdoc' s.add_development_dependency 'rcov' s.authors = ['Tim Sharpe'] s.email = 'tim@sharpe.id.au' end puppet-lint-0.3.2/bin/0000755000004100000410000000000012056100642014571 5ustar www-datawww-datapuppet-lint-0.3.2/bin/puppet-lint0000755000004100000410000000021212056100642016773 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib]) require 'puppet-lint' exit PuppetLint::Bin.new(ARGV).run puppet-lint-0.3.2/metadata.yml0000644000004100000410000001762412056100642016336 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: puppet-lint version: !ruby/object:Gem::Version version: 0.3.2 prerelease: platform: ruby authors: - Tim Sharpe autorequire: bindir: bin cert_chain: [] date: 2012-10-19 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rdoc requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rcov requirement: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' description: ! "Checks your Puppet manifests against the Puppetlabs\n style guide and alerts you to any discrepancies." email: tim@sharpe.id.au executables: - puppet-lint extensions: [] extra_rdoc_files: [] files: - .gitignore - .travis.yml - Gemfile - LICENSE - README.md - Rakefile - bin/puppet-lint - lib/puppet-lint.rb - lib/puppet-lint/bin.rb - lib/puppet-lint/configuration.rb - lib/puppet-lint/lexer.rb - lib/puppet-lint/lexer/token.rb - lib/puppet-lint/plugin.rb - lib/puppet-lint/plugins.rb - lib/puppet-lint/plugins/check_classes.rb - lib/puppet-lint/plugins/check_comments.rb - lib/puppet-lint/plugins/check_conditionals.rb - lib/puppet-lint/plugins/check_documentation.rb - lib/puppet-lint/plugins/check_resources.rb - lib/puppet-lint/plugins/check_strings.rb - lib/puppet-lint/plugins/check_variables.rb - lib/puppet-lint/plugins/check_whitespace.rb - lib/puppet-lint/tasks/puppet-lint.rb - lib/puppet-lint/version.rb - puppet-lint.gemspec - spec/fixtures/test/manifests/fail.pp - spec/fixtures/test/manifests/init.pp - spec/fixtures/test/manifests/malformed.pp - spec/fixtures/test/manifests/warning.pp - spec/puppet-lint/bin_spec.rb - spec/puppet-lint/configuration_spec.rb - spec/puppet-lint/lexer/token_spec.rb - spec/puppet-lint/lexer_spec.rb - spec/puppet-lint/plugins/check_classes/autoloader_layout_spec.rb - spec/puppet-lint/plugins/check_classes/class_inherits_from_params_class_spec.rb - spec/puppet-lint/plugins/check_classes/class_parameter_defaults_spec.rb - spec/puppet-lint/plugins/check_classes/inherits_across_namespaces_spec.rb - spec/puppet-lint/plugins/check_classes/names_containing_dash_spec.rb - spec/puppet-lint/plugins/check_classes/nested_classes_or_defines_spec.rb - spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb - spec/puppet-lint/plugins/check_classes/right_to_left_relationship_spec.rb - spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb - spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb - spec/puppet-lint/plugins/check_comments/star_comments_spec.rb - spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb - spec/puppet-lint/plugins/check_conditionals/selector_inside_resource_spec.rb - spec/puppet-lint/plugins/check_documentation/documentation_spec.rb - spec/puppet-lint/plugins/check_resources/duplicate_params_spec.rb - spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb - spec/puppet-lint/plugins/check_resources/ensure_not_symlink_target_spec.rb - spec/puppet-lint/plugins/check_resources/file_mode_spec.rb - spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb - spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb - spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb - spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb - spec/puppet-lint/plugins/check_strings/quoted_booleans_spec.rb - spec/puppet-lint/plugins/check_strings/single_quote_string_with_variables_spec.rb - spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb - spec/puppet-lint/plugins/check_variables/variable_contains_dash_spec.rb - spec/puppet-lint/plugins/check_whitespace/2sp_soft_tabs_spec.rb - spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb - spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb - spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb - spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb - spec/puppet-lint_spec.rb - spec/spec_helper.rb homepage: https://github.com/rodjek/puppet-lint/ licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 1.8.23 signing_key: specification_version: 3 summary: Ensure your Puppet manifests conform with the Puppetlabs style guide test_files: - spec/fixtures/test/manifests/fail.pp - spec/fixtures/test/manifests/init.pp - spec/fixtures/test/manifests/malformed.pp - spec/fixtures/test/manifests/warning.pp - spec/puppet-lint/bin_spec.rb - spec/puppet-lint/configuration_spec.rb - spec/puppet-lint/lexer/token_spec.rb - spec/puppet-lint/lexer_spec.rb - spec/puppet-lint/plugins/check_classes/autoloader_layout_spec.rb - spec/puppet-lint/plugins/check_classes/class_inherits_from_params_class_spec.rb - spec/puppet-lint/plugins/check_classes/class_parameter_defaults_spec.rb - spec/puppet-lint/plugins/check_classes/inherits_across_namespaces_spec.rb - spec/puppet-lint/plugins/check_classes/names_containing_dash_spec.rb - spec/puppet-lint/plugins/check_classes/nested_classes_or_defines_spec.rb - spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb - spec/puppet-lint/plugins/check_classes/right_to_left_relationship_spec.rb - spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb - spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb - spec/puppet-lint/plugins/check_comments/star_comments_spec.rb - spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb - spec/puppet-lint/plugins/check_conditionals/selector_inside_resource_spec.rb - spec/puppet-lint/plugins/check_documentation/documentation_spec.rb - spec/puppet-lint/plugins/check_resources/duplicate_params_spec.rb - spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb - spec/puppet-lint/plugins/check_resources/ensure_not_symlink_target_spec.rb - spec/puppet-lint/plugins/check_resources/file_mode_spec.rb - spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb - spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb - spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb - spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb - spec/puppet-lint/plugins/check_strings/quoted_booleans_spec.rb - spec/puppet-lint/plugins/check_strings/single_quote_string_with_variables_spec.rb - spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb - spec/puppet-lint/plugins/check_variables/variable_contains_dash_spec.rb - spec/puppet-lint/plugins/check_whitespace/2sp_soft_tabs_spec.rb - spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb - spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb - spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb - spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb - spec/puppet-lint_spec.rb - spec/spec_helper.rb puppet-lint-0.3.2/Gemfile0000644000004100000410000000014612056100642015315 0ustar www-datawww-datasource :rubygems gem 'rake' gem 'rspec' gem 'rdoc' gem 'ruby-prof' gem 'rcov', :platform => :ruby_18 puppet-lint-0.3.2/.gitignore0000644000004100000410000000010112056100642016001 0ustar www-datawww-data*.gem .bundle/ .rbenv-version Gemfile.lock vendor/gems coverage/ puppet-lint-0.3.2/lib/0000755000004100000410000000000012056100642014567 5ustar www-datawww-datapuppet-lint-0.3.2/lib/puppet-lint/0000755000004100000410000000000012056100642017050 5ustar www-datawww-datapuppet-lint-0.3.2/lib/puppet-lint/version.rb0000644000004100000410000000005112056100642021056 0ustar www-datawww-dataclass PuppetLint VERSION = '0.3.2' end puppet-lint-0.3.2/lib/puppet-lint/tasks/0000755000004100000410000000000012056100642020175 5ustar www-datawww-datapuppet-lint-0.3.2/lib/puppet-lint/tasks/puppet-lint.rb0000644000004100000410000000150112056100642023000 0ustar www-datawww-datarequire 'puppet-lint' require 'rake' require 'rake/tasklib' class PuppetLint class RakeTask < ::Rake::TaskLib def initialize(*args) desc 'Run puppet-lint' task :lint do PuppetLint.configuration.with_filename = true RakeFileUtils.send(:verbose, true) do linter = PuppetLint.new matched_files = FileList['**/*.pp'] if ignore_paths = PuppetLint.configuration.ignore_paths matched_files = matched_files.exclude(*ignore_paths) end matched_files.to_a.each do |puppet_file| linter.file = puppet_file linter.run end fail if linter.errors? || ( linter.warnings? && PuppetLint.configuration.fail_on_warnings ) end end end end end PuppetLint::RakeTask.new puppet-lint-0.3.2/lib/puppet-lint/bin.rb0000755000004100000410000000667012056100642020161 0ustar www-datawww-datarequire 'optparse' class PuppetLint::Bin def initialize(args) @args = args end def run help = <<-EOHELP Puppet-lint Basic Command Line Usage: puppet-lint [OPTIONS] [PATH] PATH The path to the Puppet manifest. Options: EOHELP opts = OptionParser.new do |opts| opts.banner = help opts.on("--version", "Display current version.") do puts "Puppet-lint " + PuppetLint::VERSION return 0 end opts.on('--with-context', 'Show where in the manifest the problem is') do PuppetLint.configuration.with_context = true end opts.on("--with-filename", "Display the filename before the warning") do PuppetLint.configuration.with_filename = true end opts.on("--fail-on-warnings", "Return a non-zero exit status for warnings.") do PuppetLint.configuration.fail_on_warnings = true end opts.on("--error-level LEVEL", [:all, :warning, :error], "The level of error to return.", "(warning, error, all)") do |el| PuppetLint.configuration.error_level = el end opts.on("--log-format FORMAT", "Change the log format.", "Overrides --with-filename.", "The following placeholders can be used:", "%{filename} - Filename without path.", "%{path} - Path as provided.", "%{fullpath} - Full path.", "%{linenumber} - Line number.", "%{kind} - The kind of message.", " - (warning, error)", "%{KIND} - Uppercase version of %{kind}", "%{check} - Name of the check.", "%{message} - The message." ) do |format| PuppetLint.configuration.log_format = format end opts.separator "" opts.separator " Disable checks:" PuppetLint.configuration.checks.each do |check| opts.on("--no-#{check}-check", "Skip the #{check} check") do PuppetLint.configuration.send("disable_#{check}") end end opts.load('/etc/puppet-lint.rc') if ENV['HOME'] opts.load(File.expand_path('~/.puppet-lint.rc')) if opts.load(File.expand_path('~/.puppet-lintrc')) $stderr.puts 'Depreciated: Found ~/.puppet-lintrc instead of ~/.puppet-lint.rc' end end opts.load('.puppet-lint.rc') if opts.load('.puppet-lintrc') $stderr.puts 'Depreciated: Read .puppet-lintrc instead of .puppet-lint.rc' end end begin opts.parse!(@args) rescue OptionParser::InvalidOption puts "puppet-lint: #{$!.message}" puts "puppet-lint: try 'puppet-lint --help' for more information" return 1 end if @args[0].nil? puts "puppet-lint: no file specified" puts "puppet-lint: try 'puppet-lint --help' for more information" return 1 end begin path = @args[0] if File.directory?(path) path = Dir.glob("#{path}/**/*.pp") else path = @args end return_val = 0 path.each do |f| l = PuppetLint.new l.file = f l.run if l.errors? or (l.warnings? and PuppetLint.configuration.fail_on_warnings) return_val = 1 end end return return_val rescue PuppetLint::NoCodeError puts "puppet-lint: no file specified or specified file does not exist" puts "puppet-lint: try 'puppet-lint --help' for more information" return 1 end end end puppet-lint-0.3.2/lib/puppet-lint/configuration.rb0000644000004100000410000000252412056100642022247 0ustar www-datawww-dataclass PuppetLint class Configuration def self.add_check(check) define_method("#{check}_enabled?") do settings["#{check}_disabled"] == true ? false : true end define_method("disable_#{check}") do settings["#{check}_disabled"] = true end define_method("enable_#{check}") do settings["#{check}_disabled"] = false end end def method_missing(method, *args, &block) if method.to_s =~ /^(\w+)=$/ option = $1 add_option(option.to_s) if settings[option].nil? settings[option] = args[0] else nil end end def add_option(option) self.class.add_option(option) end def self.add_option(option) define_method("#{option}=") do |value| settings[option] = value end define_method(option) do settings[option] end end def add_check(check, &b) self.class.add_check(check) check_method[check] = b end def settings @settings ||= {} end def check_method @check_method ||= {} end def checks check_method.keys end def defaults settings.clear self.with_filename = false self.fail_on_warnings = false self.error_level = :all self.log_format = '' self.with_context = false end end end puppet-lint-0.3.2/lib/puppet-lint/plugins.rb0000644000004100000410000000063712056100642021064 0ustar www-datawww-dataclass PuppetLint class Plugins end end require 'puppet-lint/plugins/check_classes' require 'puppet-lint/plugins/check_comments' require 'puppet-lint/plugins/check_conditionals' require 'puppet-lint/plugins/check_documentation' require 'puppet-lint/plugins/check_strings' require 'puppet-lint/plugins/check_variables' require 'puppet-lint/plugins/check_whitespace' require 'puppet-lint/plugins/check_resources' puppet-lint-0.3.2/lib/puppet-lint/lexer.rb0000644000004100000410000002221112056100642020512 0ustar www-datawww-datarequire 'pp' require 'strscan' require 'puppet-lint/lexer/token' require 'set' class PuppetLint class LexerError < StandardError attr_reader :line_no, :column def initialize(code, offset) chunk = code[0..offset] @line_no = chunk.count("\n") + 1 if @line_no == 1 @column = chunk.length else @column = chunk.length - chunk.rindex("\n") - 1 end @column = 1 if @column == 0 end end class Lexer KEYWORDS = { 'class' => true, 'case' => true, 'default' => true, 'define' => true, 'import' => true, 'if' => true, 'else' => true, 'elsif' => true, 'inherits' => true, 'node' => true, 'and' => true, 'or' => true, 'undef' => true, 'true' => true, 'false' => true, 'in' => true, 'unless' => true, } REGEX_PREV_TOKENS = { :NODE => true, :LBRACE => true, :RBRACE => true, :MATCH => true, :NOMATCH => true, :COMMA => true, } KNOWN_TOKENS = [ [:CLASSREF, /\A(((::){0,1}[A-Z][-\w]*)+)/], [:NUMBER, /\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b/], [:NAME, /\A(((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)/], [:LBRACK, /\A(\[)/], [:RBRACK, /\A(\])/], [:LBRACE, /\A(\{)/], [:RBRACE, /\A(\})/], [:LPAREN, /\A(\()/], [:RPAREN, /\A(\))/], [:ISEQUAL, /\A(==)/], [:MATCH, /\A(=~)/], [:FARROW, /\A(=>)/], [:EQUALS, /\A(=)/], [:APPENDS, /\A(\+=)/], [:PARROW, /\A(\+>)/], [:PLUS, /\A(\+)/], [:GREATEREQUAL, /\A(>=)/], [:RSHIFT, /\A(>>)/], [:GREATERTHAN, /\A(>)/], [:LESSEQUAL, /\A(<=)/], [:LLCOLLECT, /\A(<<\|)/], [:OUT_EDGE, /\A(<-)/], [:OUT_EDGE_SUB, /\A(<~)/], [:LCOLLECT, /\A(<\|)/], [:LSHIFT, /\A(<<)/], [:LESSTHAN, /\A(<)/], [:NOMATCH, /\A(!~)/], [:NOTEQUAL, /\A(!=)/], [:NOT, /\A(!)/], [:RRCOLLECT, /\A(\|>>)/], [:RCOLLECT, /\A(\|>)/], [:IN_EDGE, /\A(->)/], [:IN_EDGE_SUB, /\A(~>)/], [:MINUS, /\A(-)/], [:COMMA, /\A(,)/], [:DOT, /\A(\.)/], [:COLON, /\A(:)/], [:AT, /\A(@)/], [:SEMIC, /\A(;)/], [:QMARK, /\A(\?)/], [:BACKSLASH, /\A(\\)/], [:TIMES, /\A(\*)/], ] FORMATTING_TOKENS = { :WHITESPACE => true, :NEWLINE => true, :COMMENT => true, :MLCOMMENT => true, :SLASH_COMMENT => true, :INDENT => true, } def tokens @tokens ||= [] end def tokenise(code) code.chomp! i = 0 while i < code.size chunk = code[i..-1] found = false KNOWN_TOKENS.each do |type, regex| if value = chunk[regex, 1] if type == :NAME if KEYWORDS.include? value tokens << new_token(value.upcase.to_sym, value, :chunk => code[0..i]) else tokens << new_token(type, value, :chunk => code[0..i]) end else tokens << new_token(type, value, :chunk => code[0..i]) end i += value.size found = true break end end unless found if var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+)/, 1] tokens << new_token(:VARIABLE, var_name, :chunk => code[0..i]) i += var_name.size + 1 elsif chunk.match(/\A'(.*?)'/m) str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*'/m) tokens << new_token(:SSTRING, str_content[0..-2], :chunk => code[0..i]) i += str_content.size + 1 elsif chunk.match(/\A"/) str_contents = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*"/m) _ = code[0..i].split("\n") interpolate_string(str_contents, _.count, _.last.length) i += str_contents.size + 1 elsif comment = chunk[/\A(#.*)/, 1] comment_size = comment.size comment.sub!(/# ?/, '') tokens << new_token(:COMMENT, comment, :chunk => code[0..i]) i += comment_size elsif slash_comment = chunk[/\A(\/\/.*)/, 1] slash_comment_size = slash_comment.size slash_comment.sub!(/\/\/ ?/, '') tokens << new_token(:SLASH_COMMENT, slash_comment, :chunk => code[0..i]) i += slash_comment_size elsif mlcomment = chunk[/\A(\/\*.*?\*\/)/m, 1] mlcomment_size = mlcomment.size mlcomment.sub!(/\A\/\* ?/, '') mlcomment.sub!(/ ?\*\/\Z/, '') mlcomment.gsub!(/^ ?\* ?/, '') mlcomment.gsub!(/\n/, ' ') mlcomment.strip! tokens << new_token(:MLCOMMENT, mlcomment, :chunk => code[0..i]) i += mlcomment_size elsif chunk.match(/\A\/.*?\//) && possible_regex? str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*\//m) tokens << new_token(:REGEX, str_content[0..-2], :chunk => code[0..i]) i += str_content.size + 1 elsif indent = chunk[/\A\n([ \t]+)/m, 1] tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i]) tokens << new_token(:INDENT, indent, :chunk => code[0..i+1]) i += indent.size + 1 elsif whitespace = chunk[/\A([ \t]+)/, 1] tokens << new_token(:WHITESPACE, whitespace, :chunk => code[0..i]) i += whitespace.size elsif chunk.match(/\A\n/) tokens << new_token(:NEWLINE, '\n', :chunk => code[0..i]) i += 1 elsif chunk.match(/\A\//) tokens << new_token(:DIV, '/', :chunk => code[0..i]) i += 1 else raise PuppetLint::LexerError.new(code, i) end end end tokens end def possible_regex? prev_token = tokens.reject { |r| FORMATTING_TOKENS.include? r.type }.last return true if prev_token.nil? if REGEX_PREV_TOKENS.include? prev_token.type true else false end end def new_token(type, value, opts = {}) if opts[:chunk] line_no = opts[:chunk].count("\n") + 1 if line_no == 1 column = opts[:chunk].length else column = opts[:chunk].length - opts[:chunk].rindex("\n") - 1 end column += 1 if column == 0 else column = opts[:column] line_no = opts[:line] end token = Token.new(type, value, line_no, column) unless tokens.last.nil? token.prev_token = tokens.last tokens.last.next_token = token unless FORMATTING_TOKENS.include?(token.type) prev_nf_idx = tokens.rindex { |r| ! FORMATTING_TOKENS.include? r.type } unless prev_nf_idx.nil? prev_nf_token = tokens[prev_nf_idx] prev_nf_token.next_code_token = token token.prev_code_token = prev_nf_token end end end token end def get_string_segment(string, terminators) str = string.scan_until(/([^\\]|^|[^\\])([\\]{2})*[#{terminators}]+/) begin [str[0..-2], str[-1,1]] rescue [nil, nil] end end def interpolate_string(string, line, column) ss = StringScanner.new(string) first = true value, terminator = get_string_segment(ss, '"$') until value.nil? if terminator == "\"" if first tokens << new_token(:STRING, value, :line => line, :column => column) first = false else line += value.count("\n") token_column = column + (ss.pos - value.size) tokens << new_token(:DQPOST, value, :line => line, :column => token_column) end else if first tokens << new_token(:DQPRE, value, :line => line, :column => column) first = false else line += value.count("\n") token_column = column + (ss.pos - value.size) tokens << new_token(:DQMID, value, :line => line, :column => token_column) end if ss.scan(/\{/).nil? var_name = ss.scan(/(::)?([\w-]+::)*[\w-]+/) unless var_name.nil? token_column = column + (ss.pos - var_name.size) tokens << new_token(:UNENC_VARIABLE, var_name, :line => line, :column => token_column) end else contents = ss.scan_until(/\}/)[0..-2] if contents.match(/\A(::)?([\w-]+::)*[\w-]+\Z/) token_column = column + (ss.pos - contents.size - 1) tokens << new_token(:VARIABLE, contents, :line => line, :column => token_column) else lexer = PuppetLint::Lexer.new lexer.tokenise(contents) lexer.tokens.each do |token| tok_col = column + token.column + (ss.pos - contents.size - 1) tok_line = token.line + line - 1 tokens << new_token(token.type, token.value, :line => tok_line, :column => tok_col) end end end end value, terminator = get_string_segment(ss, '"$') end end end end puppet-lint-0.3.2/lib/puppet-lint/plugin.rb0000644000004100000410000001706212056100642020701 0ustar www-datawww-dataclass PuppetLint::Checks attr_reader :problems attr_reader :manifest_lines def initialize @problems = [] @default_info = {:check => 'unknown', :linenumber => 0, :column => 0} PuppetLint.configuration.checks.each do |check| method = PuppetLint.configuration.check_method[check] self.class.send(:define_method, "lint_check_#{check}", &method) end end # notify(kind, message_hash) #=> nil # # Adds the message to the problems array. # The _kind_ gets added to the _message_hash_ by setting the key :_kind_. # Typically, the _message_hash_ should contain following keys: # message:: which contains a string value describing the problem # linenumber:: which contains the line number on which the problem occurs. # Besides the :_kind_ value that is being set, some other key/values are also # added. Typically, this is # check:: which contains the name of the check that is being executed. # linenumber:: which defaults to 0 if the message does not already contain one. # # notify :warning, :message => "Something happened", :linenumber => 4 # => {:kind=>:warning, :message=>"Something happened", :linenumber=>4, :check=>'unknown'} # def notify(kind, message_hash) message_hash[:kind] = kind message_hash.merge!(@default_info) {|key, v1, v2| v1 } @problems << message_hash message_hash end def load_data(fileinfo, data) lexer = PuppetLint::Lexer.new begin @tokens = lexer.tokenise(data) rescue PuppetLint::LexerError => e notify :error, { :message => 'Syntax error (try running `puppet parser validate `)', :linenumber => e.line_no, :column => e.column, } @tokens = [] end @fileinfo = fileinfo @data = data end def run(fileinfo, data) load_data(fileinfo, data) enabled_checks.each do |check| @default_info[:check] = check self.send("lint_check_#{check}") end @problems end def enabled_checks @enabled_checks ||= Proc.new do self.public_methods.select { |method| method.to_s.start_with? 'lint_check_' }.map { |method| method.to_s[11..-1] }.select { |name| PuppetLint.configuration.send("#{name}_enabled?") } end.call end def tokens @tokens end def fullpath @fileinfo[:fullpath] end def title_tokens @title_tokens ||= Proc.new do result = [] tokens.each_index do |token_idx| if tokens[token_idx].type == :COLON # gather a list of tokens that are resource titles if tokens[token_idx-1].type == :RBRACK array_start_idx = tokens.rindex { |r| r.type == :LBRACK } title_array_tokens = tokens[(array_start_idx + 1)..(token_idx - 2)] result += title_array_tokens.select { |token| {:STRING => true, :NAME => true}.include? token.type } else next_token = tokens[token_idx].next_code_token if next_token.type != :LBRACE result << tokens[token_idx - 1] end end end end result end.call end # Internal: Calculate the positions of all resource declarations within the # tokenised manifest. These positions only point to the content of the # resource declaration, they do not include resource types or # titles/namevars. # # Returns an Array of Hashes, each containing: # :start - An Integer position in the `tokens` Array pointing to the first # Token of a resource declaration parameters (type :NAME). # :end - An Integer position in the `tokens` Array pointing to the last # Token of a resource declaration parameters (type :RBRACE). def resource_indexes @resource_indexes ||= Proc.new do result = [] tokens.each_index do |token_idx| if tokens[token_idx].type == :COLON next_token = tokens[token_idx].next_code_token depth = 1 if next_token.type != :LBRACE tokens[(token_idx + 1)..-1].each_index do |idx| real_idx = token_idx + idx + 1 if tokens[real_idx].type == :LBRACE depth += 1 elsif {:SEMIC => true, :RBRACE => true}.include? tokens[real_idx].type unless tokens[real_idx].type == :SEMIC && depth > 1 depth -= 1 if depth == 0 result << {:start => token_idx + 1, :end => real_idx} break end end end end end end end result end.call end # Internal: Calculate the positions of all class definitions within the # tokenised manifest. # # Returns an Array of Hashes, each containing: # :start - An Integer position in the `tokens` Array pointing to the first # token of a class (type :CLASS). # :end - An Integer position in the `tokens` Array pointing to the last # token of a class (type :RBRACE). def class_indexes @class_indexes ||= Proc.new do result = [] tokens.each_index do |token_idx| if tokens[token_idx].type == :CLASS depth = 0 in_params = false tokens[token_idx+1..-1].each_index do |class_token_idx| idx = class_token_idx + token_idx + 1 if tokens[idx].type == :LPAREN in_params = true elsif tokens[idx].type == :RPAREN in_params = false elsif tokens[idx].type == :LBRACE depth += 1 unless in_params elsif tokens[idx].type == :RBRACE depth -= 1 unless in_params if depth == 0 && ! in_params if tokens[token_idx].next_code_token.type != :LBRACE result << {:start => token_idx, :end => idx} end break end end end end end result end.call end # Internal: Calculate the positions of all defined type definitions within # the tokenised manifest. # # Returns an Array of Hashes, each containing: # :start - An Integer position in the `tokens` Array pointing to the first # token of a defined type (type :DEFINE). # :end - An Integer position in the `tokens` Array pointing to the last # token of a defined type (type :RBRACE). def defined_type_indexes @defined_type_indexes ||= Proc.new do result = [] tokens.each_index do |token_idx| if tokens[token_idx].type == :DEFINE depth = 0 in_params = false tokens[token_idx+1..-1].each_index do |define_token_idx| idx = define_token_idx + token_idx + 1 if tokens[idx].type == :LPAREN in_params = true elsif tokens[idx].type == :RPAREN in_params = false elsif tokens[idx].type == :LBRACE depth += 1 unless in_params elsif tokens[idx].type == :RBRACE depth -= 1 unless in_params if depth == 0 && ! in_params result << {:start => token_idx, :end => idx} break end end end end end result end.call end def formatting_tokens @formatting_tokens ||= PuppetLint::Lexer::FORMATTING_TOKENS end def manifest_lines @manifest_lines ||= @data.split("\n") end end class PuppetLint::CheckPlugin def self.check(name, &b) PuppetLint.configuration.add_check(name, &b) end end puppet-lint-0.3.2/lib/puppet-lint/lexer/0000755000004100000410000000000012056100642020167 5ustar www-datawww-datapuppet-lint-0.3.2/lib/puppet-lint/lexer/token.rb0000644000004100000410000000363512056100642021643 0ustar www-datawww-dataclass PuppetLint class Lexer class Token # Internal: Returns the Symbol type of the Token. attr_reader :type # Internal: Returns the String value of the Token. attr_reader :value # Internal: Returns the Integer line number of the manifest text where # the Token can be found. attr_reader :line # Internal: Returns the Integer column number of the line of the manifest # text where the Token can be found. attr_reader :column # Internal: Gets/sets the next token in the manifest. attr_accessor :next_token # Internal: Gets/sets the previous token in the manifest. attr_accessor :prev_token # Internal: Gets/sets the next code token (skips whitespace, comments, # etc) in the manifest. attr_accessor :next_code_token # Internal: Gets/sets the previous code tokne (skips whitespace, # comments, etc) in the manifest. attr_accessor :prev_code_token # Internal: Initialise a new Token object. # # type - An upper case Symbol describing the type of Token. # value - The String value of the Token. # line - The Integer line number where the Token can be found in the # manifest. # column - The Integer number of characters from the start of the line to # the start of the Token. # # Returns the instantiated Token. def initialize(type, value, line, column) @value = value @type = type @line = line @column = column @next_token = nil @prev_token = nil @next_code_token = nil @prev_code_token = nil end # Internal: Produce a human friendly description of the Token when # inspected. # # Returns a String describing the Token. def inspect "" end end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/0000755000004100000410000000000012056100642020531 5ustar www-datawww-datapuppet-lint-0.3.2/lib/puppet-lint/plugins/check_strings.rb0000644000004100000410000000573312056100642023714 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin # Public: Check the manifest tokens for any double quoted strings that don't # contain any variables or common escape characters and record a warning for # each instance found. # # Returns nothing. check 'double_quoted_strings' do tokens.select { |r| r.type == :STRING }.each { |r| r.value.gsub!(' '*r.column, "\n") }.select { |r| r.value[/(\t|\\t|\n|\\n)/].nil? }.each do |token| notify :warning, { :message => 'double quoted string containing no variables', :linenumber => token.line, :column => token.column, } end end # Public: Check the manifest tokens for double quoted strings that contain # a single variable only and record a warning for each instance found. # # Returns nothing. check 'only_variable_string' do tokens.each_index do |token_idx| token = tokens[token_idx] if token.type == :DQPRE and token.value == '' if {:VARIABLE => true, :UNENC_VARIABLE => true}.include? tokens[token_idx + 1].type if tokens[token_idx + 2].type == :DQPOST if tokens[token_idx + 2].value == '' notify :warning, { :message => 'string containing only a variable', :linenumber => tokens[token_idx + 1].line, :column => tokens[token_idx + 1].column, } end end end end end end # Public: Check the manifest tokens for any variables in a string that have # not been enclosed by braces ({}) and record a warning for each instance # found. # # Returns nothing. check 'variables_not_enclosed' do tokens.select { |r| r.type == :UNENC_VARIABLE }.each do |token| notify :warning, { :message => 'variable not enclosed in {}', :linenumber => token.line, :column => token.column, } end end # Public: Check the manifest tokens for any single quoted strings containing # a enclosed variable and record an error for each instance found. # # Returns nothing. check 'single_quote_string_with_variables' do tokens.select { |r| r.type == :SSTRING && r.value.include?('${') }.each do |token| notify :error, { :message => 'single quoted string containing a variable found', :linenumber => token.line, :column => token.column, } end end # Public: Check the manifest tokens for any double or single quoted strings # containing only a boolean value and record a warning for each instance # found. # # Returns nothing. check 'quoted_booleans' do tokens.select { |r| {:STRING => true, :SSTRING => true}.include?(r.type) && %w{true false}.include?(r.value) }.each do |token| notify :warning, { :message => 'quoted boolean value found', :linenumber => token.line, :column => token.column, } end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/check_conditionals.rb0000644000004100000410000000400612056100642024701 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckConditionals < PuppetLint::CheckPlugin # Public: Test the manifest tokens for any selectors embedded within resource # declarations and record a warning for each instance found. # # Returns nothing. check 'selector_inside_resource' do resource_indexes.each do |resource| resource_tokens = tokens[resource[:start]..resource[:end]] resource_tokens.each do |token| if token.type == :FARROW if token.next_code_token.type == :VARIABLE unless token.next_code_token.next_code_token.nil? if token.next_code_token.next_code_token.type == :QMARK notify :warning, { :message => 'selector inside resource block', :linenumber => token.line, :column => token.column, } end end end end end end end # Public: Test the manifest tokens for any case statements that do not # contain a "default" case and record a warning for each instance found. # # Returns nothing. check 'case_without_default' do case_indexes = [] tokens.each_index do |token_idx| if tokens[token_idx].type == :CASE depth = 0 tokens[(token_idx + 1)..-1].each_index do |case_token_idx| idx = case_token_idx + token_idx + 1 if tokens[idx].type == :LBRACE depth += 1 elsif tokens[idx].type == :RBRACE depth -= 1 if depth == 0 case_indexes << {:start => token_idx, :end => idx} break end end end end end case_indexes.each do |kase| case_tokens = tokens[kase[:start]..kase[:end]] unless case_tokens.index { |r| r.type == :DEFAULT } notify :warning, { :message => 'case statement without a default case', :linenumber => case_tokens.first.line, :column => case_tokens.first.column, } end end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/check_classes.rb0000644000004100000410000002630312056100642023654 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin # Public: Test the manifest tokens for any right-to-left (<-) chaining # operators and record a warning for each instance found. # # Returns nothing. check 'right_to_left_relationship' do tokens.select { |r| r.type == :OUT_EDGE }.each do |token| notify :warning, { :message => 'right-to-left (<-) relationship', :linenumber => token.line, :column => token.column, } end end # Public: Test the manifest tokens for any classes or defined types that are # not in an appropriately named file for the autoloader to detect and record # an error of each instance found. # # Returns nothing. check 'autoloader_layout' do unless fullpath == '' (class_indexes + defined_type_indexes).each do |class_idx| class_tokens = tokens[class_idx[:start]..class_idx[:end]] title_token = class_tokens[class_tokens.index { |r| r.type == :NAME }] split_title = title_token.value.split('::') mod = split_title.first if split_title.length > 1 expected_path = "#{mod}/manifests/#{split_title[1..-1].join('/')}.pp" else expected_path = "#{title_token.value}/manifests/init.pp" end unless fullpath.end_with? expected_path notify :error, { :message => "#{title_token.value} not in autoload module layout", :linenumber => title_token.line, :column => title_token.column, } end end end end # Public: Check the manifest tokens for any classes or defined types that # have a dash in their name and record a warning for each instance found. # # Returns nothing. check 'names_containing_dash' do (class_indexes + defined_type_indexes).each do |class_idx| class_tokens = tokens[class_idx[:start]..class_idx[:end]] title_token = class_tokens[class_tokens.index { |r| r.type == :NAME }] if title_token.value.include? '-' if class_tokens.first.type == :CLASS obj_type = 'class' else obj_type = 'defined type' end notify :warning, { :message => "#{obj_type} name containing a dash", :linenumber => title_token.line, :column => title_token.column, } end end end check 'class_inherits_from_params_class' do class_indexes.each do |class_idx| class_tokens = tokens[class_idx[:start]..class_idx[:end]] inherits_idx = class_tokens.index { |r| r.type == :INHERITS } unless inherits_idx.nil? inherited_class_token = class_tokens[inherits_idx].next_code_token if inherited_class_token.value.end_with? '::params' notify :warning, { :message => 'class inheriting from params class', :linenumber => inherited_class_token.line, :column => inherited_class_token.column, } end end end end check 'class_parameter_defaults' do class_indexes.each do |class_idx| token_idx = class_idx[:start] depth = 0 lparen_idx = nil rparen_idx = nil class_name_token = tokens[class_idx[:start]].next_code_token if class_name_token.next_code_token.type == :LPAREN tokens[token_idx..-1].each_index do |t| idx = token_idx + t if tokens[idx].type == :LPAREN depth += 1 lparen_idx = idx if depth == 1 elsif tokens[idx].type == :RPAREN depth -= 1 if depth == 0 rparen_idx = idx break end end end end unless lparen_idx.nil? or rparen_idx.nil? param_tokens = tokens[lparen_idx+1..rparen_idx-1].reject { |r| formatting_tokens.include? r.type } paren_stack = [] param_tokens.each_index do |param_tokens_idx| this_token = param_tokens[param_tokens_idx] next_token = this_token.next_code_token prev_token = this_token.prev_code_token if this_token.type == :LPAREN paren_stack.push(true) elsif this_token.type == :RPAREN paren_stack.pop end next unless paren_stack.empty? if this_token.type == :VARIABLE if !next_token.nil? && next_token.type != :EQUALS if !prev_token.nil? && prev_token.type != :EQUALS notify :warning, { :message => 'parameterised class parameter without a default value', :linenumber => this_token.line, :column => this_token.column, } end end end end end end end # Public: Test the manifest tokens for any parameterised classes or defined # types that take parameters and record a warning if there are any optional # parameters listed before required parameters. # # Returns nothing. check 'parameter_order' do defined_type_indexes.each do |class_idx| token_idx = class_idx[:start] depth = 0 lparen_idx = nil rparen_idx = nil tokens[token_idx..-1].each_index do |t| idx = token_idx + t if tokens[idx].type == :LPAREN depth += 1 lparen_idx = idx if depth == 1 elsif tokens[idx].type == :RPAREN depth -= 1 if depth == 0 rparen_idx = idx break end end end unless lparen_idx.nil? or rparen_idx.nil? param_tokens = tokens[lparen_idx+1..rparen_idx-1].reject { |r| formatting_tokens.include? r.type } paren_stack = [] param_tokens.each_index do |param_tokens_idx| this_token = param_tokens[param_tokens_idx] next_token = param_tokens[param_tokens_idx+1] prev_token = param_tokens[param_tokens_idx-1] if this_token.type == :LPAREN paren_stack.push(true) elsif this_token.type == :RPAREN paren_stack.pop end next unless paren_stack.empty? if this_token.type == :VARIABLE if next_token.nil? || next_token.type == :COMMA prev_tokens = param_tokens[0..param_tokens_idx] unless prev_tokens.rindex { |r| r.type == :EQUALS }.nil? unless prev_token.nil? or prev_token.type == :EQUALS msg = 'optional parameter listed before required parameter' notify :warning, { :message => msg, :linenumber => this_token.line, :column => this_token.column, } end end end end end end end end # Public: Test the manifest tokens for any classes that inherit across # namespaces and record a warning for each instance found. # # Returns nothing. check 'inherits_across_namespaces' do class_indexes.each do |class_idx| class_token = tokens[class_idx[:start]] class_name_token = class_token.next_code_token inherits_token = class_name_token.next_code_token next if inherits_token.nil? if inherits_token.type == :INHERITS inherited_class_token = inherits_token.next_code_token inherited_module_name = inherited_class_token.value.split('::').reject { |r| r.empty? }.first class_module_name = class_name_token.value.split('::').reject { |r| r.empty? }.first unless class_module_name == inherited_module_name notify :warning, { :message => "class inherits across module namespaces", :linenumber => inherited_class_token.line, :column => inherited_class_token.column, } end end end end # Public: Test the manifest tokens for any classes or defined types that are # defined inside another class. # # Returns nothing. check 'nested_classes_or_defines' do class_indexes.each do |class_idx| # Skip the first token so that we don't pick up the first :CLASS class_tokens = tokens[class_idx[:start]+1..class_idx[:end]] class_tokens.each do |token| if token.type == :CLASS if token.next_code_token.type != :LBRACE notify :warning, { :message => "class defined inside a class", :linenumber => token.line, :column => token.column, } end end if token.type == :DEFINE notify :warning, { :message => "define defined inside a class", :linenumber => token.line, :column => token.column, } end end end end # Public: Test the manifest tokens for any variables that are referenced in # the manifest. If the variables are not fully qualified or one of the # variables automatically created in the scope, check that they have been # defined in the local scope and record a warning for each variable that has # not. # # Returns nothing. check 'variable_scope' do variables_in_scope = [ 'name', 'title', 'module_name', 'environment', 'clientcert', 'clientversion', 'servername', 'serverip', 'serverversion', 'caller_module_name', ] (class_indexes + defined_type_indexes).each do |idx| object_tokens = tokens[idx[:start]..idx[:end]] object_tokens.reject! { |r| formatting_tokens.include?(r.type) } depth = 0 lparen_idx = nil rparen_idx = nil object_tokens.each_index do |t| if object_tokens[t].type == :LPAREN depth += 1 lparen_idx = t if depth == 1 elsif object_tokens[t].type == :RPAREN depth -= 1 if depth == 0 rparen_idx = t break end end end referenced_variables = [] unless lparen_idx.nil? or rparen_idx.nil? param_tokens = object_tokens[lparen_idx..rparen_idx] param_tokens.each_index do |param_tokens_idx| this_token = param_tokens[param_tokens_idx] next_token = param_tokens[param_tokens_idx+1] if this_token.type == :VARIABLE if {:COMMA => true, :EQUALS => true, :RPAREN => true}.include? next_token.type variables_in_scope << this_token.value end end end end object_tokens.each_index do |object_token_idx| this_token = object_tokens[object_token_idx] next_token = object_tokens[object_token_idx + 1] if this_token.type == :VARIABLE if next_token.type == :EQUALS variables_in_scope << this_token.value else referenced_variables << this_token end end end msg = "top-scope variable being used without an explicit namespace" referenced_variables.each do |token| unless token.value.include? '::' unless variables_in_scope.include? token.value unless token.value =~ /\d+/ notify :warning, { :message => msg, :linenumber => token.line, :column => token.column, } end end end end end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/check_documentation.rb0000644000004100000410000000175612056100642025075 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckDocumentation < PuppetLint::CheckPlugin check 'documentation' do comment_tokens = { :COMMENT => true, :MLCOMMENT => true, :SLASH_COMMENT => true, } whitespace_tokens = { :WHITESPACE => true, :NEWLINE => true, :INDENT => true, } (class_indexes + defined_type_indexes).each do |item_idx| prev_token = tokens[item_idx[:start] - 1] while (!prev_token.nil?) && whitespace_tokens.include?(prev_token.type) prev_token = prev_token.prev_token end unless (!prev_token.nil?) && comment_tokens.include?(prev_token.type) first_token = tokens[item_idx[:start]] if first_token.type == :CLASS type = 'class' else type = 'defined type' end notify :warning, { :message => "#{type} not documented", :linenumber => first_token.line, :column => first_token.column, } end end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/check_resources.rb0000644000004100000410000001412712056100642024232 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckResources < PuppetLint::CheckPlugin # Public: Check the manifest tokens for any resource titles / namevars that # are not quoted and record a warning for each instance found. # # Return nothing. check 'unquoted_resource_title' do title_tokens.each do |token| if token.type == :NAME notify :warning, { :message => 'unquoted resource title', :linenumber => token.line, :column => token.column, } end end end # Public: Check the tokens of each resource instance for an ensure parameter # and if found, check that it is the first parameter listed. If it is not # the first parameter, record a warning. # # Returns nothing. check 'ensure_first_param' do resource_indexes.each do |resource| resource_tokens = tokens[resource[:start]..resource[:end]] param_tokens = resource_tokens.select { |resource_token| resource_token.type == :NAME && resource_token.next_code_token.type == :FARROW } ensure_attr_index = param_tokens.index { |resource_token| resource_token.value == 'ensure' } unless ensure_attr_index.nil? if ensure_attr_index > 0 ensure_token = param_tokens[ensure_attr_index] notify :warning, { :message => "ensure found on line but it's not the first attribute", :linenumber => ensure_token.line, :column => ensure_token.column, } end end end end check 'duplicate_params' do resource_indexes.each do |resource| resource_tokens = tokens[resource[:start]..resource[:end]].reject { |r| formatting_tokens.include? r.type } seen_params = {} level = 0 resource_tokens.each_with_index do |token, idx| case token.type when :LBRACE level += 1 next when :RBRACE seen_params[level] = {} level -= 1 next end seen_params[level] ||= {} if token.type == :FARROW prev_token = resource_tokens[idx - 1] next unless prev_token.type == :NAME if seen_params[level].include? prev_token.value notify :error, { :message => 'duplicate parameter found in resource', :linenumber => prev_token.line, :column => prev_token.column, } else seen_params[level][prev_token.value] = true end end end end end # Public: Check the tokens of each File resource instance for a mode # parameter and if found, record a warning if the value of that parameter is # not a quoted string. # # Returns nothing. check 'unquoted_file_mode' do resource_indexes.each do |resource| resource_tokens = tokens[resource[:start]..resource[:end]] prev_tokens = tokens[0..resource[:start]] lbrace_idx = prev_tokens.rindex { |r| r.type == :LBRACE } resource_type_token = tokens[lbrace_idx].prev_code_token if resource_type_token.value == "file" resource_tokens.select { |resource_token| resource_token.type == :NAME and resource_token.value == 'mode' }.each do |resource_token| value_token = resource_token.next_code_token.next_code_token if {:NAME => true, :NUMBER => true}.include? value_token.type notify :warning, { :message => 'unquoted file mode', :linenumber => value_token.line, :column => value_token.column, } end end end end end # Public: Check the tokens of each File resource instance for a mode # parameter and if found, record a warning if the value of that parameter is # not a 4 digit octal value (0755) or a symbolic mode ('o=rwx,g+r'). # # Returns nothing. check 'file_mode' do msg = 'mode should be represented as a 4 digit octal value or symbolic mode' sym_mode = /\A([ugoa]*[-=+][-=+rstwxXugo]*)(,[ugoa]*[-=+][-=+rstwxXugo]*)*\Z/ resource_indexes.each do |resource| resource_tokens = tokens[resource[:start]..resource[:end]] prev_tokens = tokens[0..resource[:start]] lbrace_idx = prev_tokens.rindex { |r| r.type == :LBRACE } resource_type_token = tokens[lbrace_idx].prev_code_token if resource_type_token.value == "file" resource_tokens.select { |resource_token| resource_token.type == :NAME and resource_token.value == 'mode' }.each do |resource_token| value_token = resource_token.next_code_token.next_code_token break if value_token.value =~ /\A[0-7]{4}\Z/ break if value_token.type == :VARIABLE break if value_token.value =~ sym_mode break if value_token.type == :UNDEF notify :warning, { :message => msg, :linenumber => value_token.line, :column => value_token.column, } end end end end # Public: Check the tokens of each File resource instance for an ensure # parameter and record a warning if the value of that parameter looks like # a symlink target (starts with a '/'). # # Returns nothing. check 'ensure_not_symlink_target' do resource_indexes.each do |resource| resource_tokens = tokens[resource[:start]..resource[:end]] prev_tokens = tokens[0..resource[:start]] lbrace_idx = prev_tokens.rindex { |r| r.type == :LBRACE } resource_type_token = tokens[lbrace_idx].prev_code_token if resource_type_token.value == "file" resource_tokens.select { |resource_token| resource_token.type == :NAME and resource_token.value == 'ensure' }.each do |ensure_token| value_token = ensure_token.next_code_token.next_code_token if value_token.value.start_with? '/' notify :warning, { :message => 'symlink target specified in ensure attr', :linenumber => value_token.line, :column => value_token.column, } end end end end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/check_variables.rb0000644000004100000410000000111512056100642024161 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckVariables < PuppetLint::CheckPlugin # Public: Test the manifest tokens for variables that contain a dash and # record a warning for each instance found. # # Returns nothing. check 'variable_contains_dash' do tokens.select { |r| {:VARIABLE => true, :UNENC_VARIABLE => true}.include? r.type }.each do |token| if token.value.match(/-/) notify :warning, { :message => 'variable contains a dash', :linenumber => token.line, :column => token.column, } end end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/check_comments.rb0000644000004100000410000000166412056100642024047 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckComments < PuppetLint::CheckPlugin # Public: Check the manifest tokens for any comments started with slashes # (//) and record a warning for each instance found. # # Returns nothing. check 'slash_comments' do tokens.select { |token| token.type == :SLASH_COMMENT }.each do |token| notify :warning, { :message => '// comment found', :linenumber => token.line, :column => token.column, } end end # Public: Check the manifest tokens for any comments encapsulated with # slash-asterisks (/* */) and record a warning for each instance found. # # Returns nothing. check 'star_comments' do tokens.select { |token| token.type == :MLCOMMENT }.each do |token| notify :warning, { :message => '/* */ comment found', :linenumber => token.line, :column => token.column, } end end end puppet-lint-0.3.2/lib/puppet-lint/plugins/check_whitespace.rb0000644000004100000410000000633112056100642024352 0ustar www-datawww-dataclass PuppetLint::Plugins::CheckWhitespace < PuppetLint::CheckPlugin # Check the raw manifest string for lines containing hard tab characters and # record an error for each instance found. # # Returns nothing. check 'hard_tabs' do manifest_lines.each_with_index do |line, idx| if line.include? "\t" notify :error, { :message => 'tab character found', :linenumber => idx + 1, :column => line.index("\t") + 1, } end end end # Check the raw manifest string for lines ending with whitespace and record # an error for each instance found. # # Returns nothing. check 'trailing_whitespace' do manifest_lines.each_with_index do |line, idx| if line.end_with? ' ' notify :error, { :message => 'trailing whitespace found', :linenumber => idx + 1, :column => line.rindex(' ') + 1, } end end end # Test the raw manifest string for lines containing more than 80 characters # and record a warning for each instance found. The only exception to this # rule is lines containing puppet:// URLs which would hurt readability if # split. # # Returns nothing. check '80chars' do manifest_lines.each_with_index do |line, idx| unless line =~ /puppet:\/\// if line.scan(/./mu).size > 80 notify :warning, { :message => 'line has more than 80 characters', :linenumber => idx + 1, :column => 80, } end end end end # Check the manifest tokens for any indentation not using 2 space soft tabs # and record an error for each instance found. # # Returns nothing. check '2sp_soft_tabs' do tokens.select { |r| r.type == :INDENT }.reject { |r| r.value.length % 2 == 0 }.each do |token| notify :error, { :message => 'two-space soft tabs not used', :linenumber => token.line, :column => token.column, } end end # Check the manifest tokens for any arrows (=>) in a grouping ({}) that are # not aligned with other arrows in that grouping. # # Returns nothing. check 'arrow_alignment' do resource_indexes.each do |res_idx| indent_depth = [nil] resource_tokens = tokens[res_idx[:start]..res_idx[:end]] resource_tokens.reject! do |token| {:COMMENT => true, :SLASH_COMMENT => true, :MLCOMMENT => true}.include? token.type end # If this is a single line resource, skip it next if resource_tokens.select { |r| r.type == :NEWLINE }.empty? resource_tokens.each_with_index do |token, idx| if token.type == :FARROW indent_length = token.column if indent_depth.last.nil? indent_depth[-1] = indent_length end unless indent_depth.last == indent_length notify :warning, { :message => 'indentation of => is not properly aligned', :linenumber => token.line, :column => token.column, } end elsif token.type == :LBRACE indent_depth.push(nil) elsif token.type == :RBRACE indent_depth.pop end end end end end puppet-lint-0.3.2/lib/puppet-lint.rb0000644000004100000410000000736612056100642017411 0ustar www-datawww-datarequire 'puppet-lint/version' require 'puppet-lint/lexer' require 'puppet-lint/configuration' require 'puppet-lint/plugin' require 'puppet-lint/bin' unless String.respond_to?('prepend') class String def prepend(lead) self.replace "#{lead}#{self}" end end end # If we are using an older ruby version, we back-port the basic functionality # we need for formatting output: 'somestring' % begin if ('%{test}' % {:test => 'replaced'} == 'replaced') # If this works, we are all good to go. end rescue # If the test failed (threw a error), monkeypatch String. # Most of this code came from http://www.ruby-forum.com/topic/144310 but was # simplified for our use. # Basic implementation of 'string' % { } like we need it. needs work. class String Percent = instance_method '%' unless defined? Percent def % *a, &b a.flatten! string = case a.last when Hash expand a.pop else self end if a.empty? string else Percent.bind(string).call(a, &b) end end def expand! vars = {} loop do changed = false vars.each do |var, value| var = var.to_s var.gsub! %r/[^a-zA-Z0-9_]/, '' [ %r/\%\{#{ var }\}/, ].each do |pat| changed = gsub! pat, "#{ value }" end end break unless changed end self end def expand opts = {} dup.expand! opts end end end class PuppetLint::NoCodeError < StandardError; end class PuppetLint attr_reader :data def initialize @data = nil @statistics = {:error => 0, :warning => 0} @fileinfo = {:path => ''} end def self.configuration @configuration ||= PuppetLint::Configuration.new end def configuration self.class.configuration end def file=(path) if File.exist? path @fileinfo[:path] = path @fileinfo[:fullpath] = File.expand_path(path) @fileinfo[:filename] = File.basename(path) @data = File.read(path) end end def code=(value) @data = value end def log_format if configuration.log_format == '' ## recreate previous old log format as far as thats possible. format = '%{KIND}: %{message} on line %{linenumber}' if configuration.with_filename format.prepend '%{path} - ' end configuration.log_format = format end return configuration.log_format end def format_message(message) format = log_format puts format % message end def print_context(message, linter) # XXX: I don't really like the way this has been implemented (passing the # linter object down through layers of functions. Refactor me! return if message[:check] == 'documentation' line = linter.manifest_lines[message[:linenumber] - 1] offset = line.index(/\S/) puts "\n #{line.strip}" printf "%#{message[:column] + 2 - offset}s\n\n", '^' end def report(problems, linter) problems.each do |message| @statistics[message[:kind]] += 1 ## Add some default attributes. message.merge!(@fileinfo) {|key, v1, v2| v1 } message[:KIND] = message[:kind].to_s.upcase if configuration.error_level == message[:kind] or configuration.error_level == :all format_message message print_context(message, linter) if configuration.with_context end end end def errors? @statistics[:error] != 0 end def warnings? @statistics[:warning] != 0 end def run if @data.nil? raise PuppetLint::NoCodeError end linter = PuppetLint::Checks.new problems = linter.run(@fileinfo, @data) report problems, linter end end # Default configuration options PuppetLint.configuration.defaults require 'puppet-lint/plugins'