├── .dockerignore ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── ci.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .travis.yml ├── CHANGELOG.md ├── Dockerfile ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── appveyor.yml ├── bin └── puppet-lint ├── lib ├── puppet-lint.rb └── puppet-lint │ ├── bin.rb │ ├── checkplugin.rb │ ├── checks.rb │ ├── configuration.rb │ ├── data.rb │ ├── lexer.rb │ ├── lexer │ ├── string_slurper.rb │ └── token.rb │ ├── monkeypatches.rb │ ├── optparser.rb │ ├── plugins.rb │ ├── plugins │ ├── check_classes │ │ ├── arrow_on_right_operand_line.rb │ │ ├── autoloader_layout.rb │ │ ├── class_inherits_from_params_class.rb │ │ ├── code_on_top_scope.rb │ │ ├── inherits_across_namespaces.rb │ │ ├── names_containing_dash.rb │ │ ├── names_containing_uppercase.rb │ │ ├── nested_classes_or_defines.rb │ │ ├── parameter_order.rb │ │ ├── right_to_left_relationship.rb │ │ └── variable_scope.rb │ ├── check_comments │ │ ├── slash_comments.rb │ │ └── star_comments.rb │ ├── check_conditionals │ │ ├── case_without_default.rb │ │ └── selector_inside_resource.rb │ ├── check_documentation │ │ └── documentation.rb │ ├── check_nodes │ │ └── unquoted_node_name.rb │ ├── check_resources │ │ ├── duplicate_params.rb │ │ ├── ensure_first_param.rb │ │ ├── ensure_not_symlink_target.rb │ │ ├── file_mode.rb │ │ ├── unquoted_file_mode.rb │ │ └── unquoted_resource_title.rb │ ├── check_strings │ │ ├── double_quoted_strings.rb │ │ ├── only_variable_string.rb │ │ ├── puppet_url_without_modules.rb │ │ ├── quoted_booleans.rb │ │ ├── single_quote_string_with_variables.rb │ │ └── variables_not_enclosed.rb │ ├── check_variables │ │ ├── variable_contains_dash.rb │ │ └── variable_is_lowercase.rb │ └── check_whitespace │ │ ├── 140chars.rb │ │ ├── 2sp_soft_tabs.rb │ │ ├── 80chars.rb │ │ ├── arrow_alignment.rb │ │ ├── hard_tabs.rb │ │ └── trailing_whitespace.rb │ ├── tasks │ ├── gemfile_rewrite.rb │ ├── puppet-lint.rb │ └── release_test.rb │ └── version.rb ├── puppet-lint.gemspec └── spec ├── fixtures └── test │ └── manifests │ ├── fail.pp │ ├── ignore.pp │ ├── ignore_multiple_block.pp │ ├── ignore_multiple_line.pp │ ├── ignore_reason.pp │ ├── init.pp │ ├── malformed.pp │ ├── mismatched_control_comment.pp │ ├── two_warnings.pp │ ├── unterminated_control_comment.pp │ ├── url_interpolation.pp │ └── warning.pp ├── puppet-lint ├── bin_spec.rb ├── checks_spec.rb ├── configuration_spec.rb ├── data_spec.rb ├── ignore_overrides_spec.rb ├── lexer │ ├── string_slurper_spec.rb │ └── token_spec.rb ├── lexer_spec.rb └── plugins │ ├── check_classes │ ├── arrow_on_right_operand_line_spec.rb │ ├── autoloader_layout_spec.rb │ ├── class_inherits_from_params_class_spec.rb │ ├── code_on_top_scope_spec.rb │ ├── inherits_across_namespaces_spec.rb │ ├── name_contains_uppercase_spec.rb │ ├── names_containing_dash_spec.rb │ ├── nested_classes_or_defines_spec.rb │ ├── parameter_order_spec.rb │ ├── right_to_left_relationship_spec.rb │ └── variable_scope_spec.rb │ ├── check_comments │ ├── slash_comments_spec.rb │ └── star_comments_spec.rb │ ├── check_conditionals │ ├── case_without_default_spec.rb │ └── selector_inside_resource_spec.rb │ ├── check_documentation │ └── documentation_spec.rb │ ├── check_nodes │ └── unquoted_node_name_spec.rb │ ├── check_resources │ ├── duplicate_params_spec.rb │ ├── ensure_first_param_spec.rb │ ├── ensure_not_symlink_target_spec.rb │ ├── file_mode_spec.rb │ ├── unquoted_file_mode_spec.rb │ └── unquoted_resource_title_spec.rb │ ├── check_strings │ ├── double_quoted_strings_spec.rb │ ├── only_variable_string_spec.rb │ ├── puppet_url_without_modules_spec.rb │ ├── quoted_booleans_spec.rb │ ├── single_quote_string_with_variables_spec.rb │ └── variables_not_enclosed_spec.rb │ ├── check_variables │ ├── variable_contains_dash_spec.rb │ └── variable_is_lowercase_spec.rb │ └── check_whitespace │ ├── 140chars_spec.rb │ ├── 2sp_soft_tabs_spec.rb │ ├── 80chars_spec.rb │ ├── arrow_alignment_spec.rb │ ├── hard_tabs_spec.rb │ └── trailing_whitespace_spec.rb ├── puppet-lint_spec.rb └── spec_helper.rb /.dockerignore: -------------------------------------------------------------------------------- 1 | /*.pp 2 | /*.gem 3 | /.bundle 4 | /.git 5 | /.gitignore 6 | /.rspec 7 | /.rubocop_todo.yml 8 | /.rubocop.yml 9 | /.ruby-version 10 | /.travis.yml 11 | /appveyor.yml 12 | /CHANGELOG.md 13 | /coverage 14 | /Gemfile 15 | /Gemfile.lock 16 | /Rakefile 17 | /README.md 18 | /spec 19 | /tmp 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | As of release 2.5.0, this project is being maintained in the main puppetlabs namespace. 2 | 3 | Please [`file new issues`](https://github.com/puppetlabs/puppet-lint/issues) there. 4 | 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | As of release 2.5.0, this project is being maintained in the main [puppetlabs namespace](https://github.com/puppetlabs/puppet-lint). 2 | 3 | Please change the base repository (☝️) to `puppetlabs/puppet-lint` before filing this pull request. 4 | 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | schedule: 8 | - cron: '3 0 * * *' 9 | jobs: 10 | lint: 11 | name: RuboCop 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: ruby/setup-ruby@v1 16 | with: 17 | ruby-version: 2.6 18 | bundler-cache: true 19 | - run: bundle install 20 | - run: bundle exec rake rubocop 21 | 22 | spec: 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | os: 27 | - ubuntu 28 | - windows 29 | ruby: 30 | - 2.1 31 | - 2.2 32 | - 2.3 33 | - 2.4 34 | - 2.5 35 | - 2.6 36 | - 2.7 37 | needs: lint 38 | name: RSpec - ${{ matrix.os }} - Ruby ${{ matrix.ruby }} 39 | runs-on: ${{ matrix.os }}-latest 40 | env: 41 | COVERAGE: yes 42 | steps: 43 | - uses: actions/checkout@v2 44 | - uses: ruby/setup-ruby@v1 45 | with: 46 | ruby-version: ${{ matrix.ruby }} 47 | bundler-cache: true 48 | - run: bundle install 49 | - run: bundle exec rake 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | .bundle/ 3 | .rbenv-version 4 | .ruby-version 5 | Gemfile.lock 6 | vendor/ 7 | coverage/ 8 | *.swp 9 | /_site/ 10 | .idea 11 | /*.pp 12 | /tmp/ 13 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | inherit_from: './.rubocop_todo.yml' 3 | AllCops: 4 | TargetRubyVersion: 1.9 5 | 6 | Style/HashSyntax: 7 | EnforcedStyle: hash_rockets 8 | 9 | Layout/FirstArrayElementLineBreak: 10 | Enabled: true 11 | Layout/FirstHashElementLineBreak: 12 | Enabled: true 13 | Layout/FirstMethodArgumentLineBreak: 14 | Enabled: true 15 | Layout/FirstMethodParameterLineBreak: 16 | Enabled: true 17 | Layout/IndentArray: 18 | EnforcedStyle: consistent 19 | Layout/MultilineArrayBraceLayout: 20 | EnforcedStyle: new_line 21 | Layout/MultilineAssignmentLayout: 22 | Enabled: true 23 | EnforcedStyle: same_line 24 | Layout/MultilineHashBraceLayout: 25 | EnforcedStyle: new_line 26 | Layout/MultilineMethodDefinitionBraceLayout: 27 | EnforcedStyle: new_line 28 | 29 | Style/AutoResourceCleanup: 30 | Enabled: true 31 | Style/BlockDelimiters: 32 | EnforcedStyle: braces_for_chaining 33 | Style/BracesAroundHashParameters: 34 | EnforcedStyle: context_dependent 35 | Style/Encoding: 36 | Enabled: false 37 | Style/MethodCallWithArgsParentheses: 38 | Enabled: true 39 | IgnoreMacros: true 40 | IgnoredMethods: 41 | - puts 42 | - require 43 | - include 44 | - it 45 | - context 46 | - describe 47 | - to 48 | - to_not 49 | - raise 50 | - desc 51 | - task 52 | - exit 53 | - should 54 | - gem 55 | - group 56 | - attr_reader 57 | - attr_accessor 58 | - attr_writer 59 | - source 60 | Style/MethodCalledOnDoEndBlock: 61 | Enabled: true 62 | Style/RegexpLiteral: 63 | EnforcedStyle: percent_r 64 | Style/TrailingCommaInArguments: 65 | EnforcedStyleForMultiline: no_comma 66 | Style/TrailingCommaInLiteral: 67 | EnforcedStyleForMultiline: comma 68 | Style/FormatStringToken: 69 | Enabled: false 70 | Style/FileName: 71 | Exclude: 72 | - 'lib/puppet-lint.rb' 73 | - 'lib/puppet-lint/tasks/puppet-lint.rb' 74 | - 'spec/puppet-lint_spec.rb' 75 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2017-08-28 12:57:58 +1000 using RuboCop version 0.49.1. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 3 10 | # Configuration parameters: Include. 11 | # Include: **/Gemfile, **/gems.rb 12 | Bundler/DuplicatedGem: 13 | Exclude: 14 | - 'Gemfile' 15 | 16 | # Offense count: 2 17 | # Cop supports --auto-correct. 18 | # Configuration parameters: Include, TreatCommentsAsGroupSeparators. 19 | # Include: **/Gemfile, **/gems.rb 20 | Bundler/OrderedGems: 21 | Exclude: 22 | - 'Gemfile' 23 | 24 | # Offense count: 9 25 | # Configuration parameters: AllowSafeAssignment. 26 | Lint/AssignmentInCondition: 27 | Exclude: 28 | - 'lib/puppet-lint/lexer.rb' 29 | - 'lib/puppet-lint/plugins/check_classes/variable_scope.rb' 30 | 31 | # Offense count: 52 32 | Metrics/AbcSize: 33 | Max: 153 34 | 35 | # Offense count: 121 36 | # Configuration parameters: CountComments, ExcludedMethods. 37 | Metrics/BlockLength: 38 | Max: 1136 39 | 40 | # Offense count: 8 41 | # Configuration parameters: CountBlocks. 42 | Metrics/BlockNesting: 43 | Max: 5 44 | 45 | # Offense count: 2 46 | # Configuration parameters: CountComments. 47 | Metrics/ClassLength: 48 | Max: 385 49 | 50 | # Offense count: 20 51 | Metrics/CyclomaticComplexity: 52 | Max: 26 53 | 54 | # Offense count: 238 55 | # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. 56 | # URISchemes: http, https 57 | Metrics/LineLength: 58 | Max: 223 59 | 60 | # Offense count: 65 61 | # Configuration parameters: CountComments. 62 | Metrics/MethodLength: 63 | Max: 105 64 | 65 | # Offense count: 18 66 | Metrics/PerceivedComplexity: 67 | Max: 28 68 | 69 | # Offense count: 1 70 | # Cop supports --auto-correct. 71 | Performance/RedundantBlockCall: 72 | Exclude: 73 | - 'lib/puppet-lint/tasks/puppet-lint.rb' 74 | 75 | # Offense count: 7 76 | # Configuration parameters: EnforcedStyle, SupportedStyles. 77 | # SupportedStyles: nested, compact 78 | Style/ClassAndModuleChildren: 79 | Exclude: 80 | - 'lib/puppet-lint.rb' 81 | - 'lib/puppet-lint/bin.rb' 82 | - 'lib/puppet-lint/checkplugin.rb' 83 | - 'lib/puppet-lint/checks.rb' 84 | - 'lib/puppet-lint/data.rb' 85 | - 'lib/puppet-lint/optparser.rb' 86 | 87 | # Offense count: 20 88 | Style/MultilineBlockChain: 89 | Enabled: false 90 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: ruby 3 | dist: trusty 4 | sudo: false 5 | branches: 6 | only: 7 | - master 8 | bundler_args: --without development system_tests 9 | script: 10 | - bundle exec rake $CHECK 11 | env: 12 | - CHECK=ci 13 | rvm: 14 | - 1.8.7 15 | - 1.9.3 16 | - 2.0.0 17 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.10 2 | 3 | RUN apk add --no-cache ruby=2.5.7-r0 ruby-json=2.5.7-r0 && \ 4 | mkdir /puppet-lint /puppet 5 | 6 | VOLUME /puppet 7 | WORKDIR /puppet 8 | ENTRYPOINT ["/puppet-lint/bin/puppet-lint"] 9 | CMD ["--help"] 10 | 11 | COPY . /puppet-lint/ 12 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | 5 | group :test do 6 | gem 'rake', '~> 10.0' 7 | gem 'rspec-its', '~> 1.0' 8 | gem 'rspec-collection_matchers', '~> 1.0' 9 | 10 | if RUBY_VERSION < '2.0' 11 | # json 2.x requires ruby 2.0. Lock to 1.8 12 | gem 'json', '= 1.8' 13 | # json_pure 2.0.2 requires ruby 2.0, and 2.0.1 requires ruby 1.9. Lock to 1.8.3. 14 | gem 'json_pure', '= 1.8.3' 15 | # addressable 2.4.0 requires ruby 1.9.0. Lock to 2.3.8. 16 | gem 'addressable', '= 2.3.8' 17 | gem 'diff-lcs', '< 1.3' 18 | gem 'rspec', '<= 3.4' 19 | else 20 | gem 'rspec', '~> 3.0' 21 | gem 'json' 22 | end 23 | 24 | if RUBY_VERSION > '1.8' 25 | # requires ruby 1.9+, on 1.8 we'll fall back to the old regex parsing 26 | gem 'rspec-json_expectations', '~> 1.4' 27 | end 28 | 29 | gem 'rubocop', '0.49.1' if RUBY_VERSION > '2.0' 30 | gem 'simplecov', :require => false if ENV['COVERAGE'] == 'yes' 31 | end 32 | 33 | group :development do 34 | if RUBY_VERSION > '1.9' 35 | # For Changelog generation 36 | if RUBY_VERSION >= '2.2.2' 37 | gem 'github_changelog_generator', :require => false 38 | else 39 | gem 'github_changelog_generator', '~> 1.13.0', :require => false 40 | gem 'rack', '~> 1.0', :require => false 41 | end 42 | 43 | gem 'pry' 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Tim Sharpe 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rspec/core/rake_task' 3 | require 'puppet-lint' 4 | require 'puppet-lint/tasks/release_test' 5 | 6 | task :default => :test 7 | 8 | RSpec::Core::RakeTask.new(:test) 9 | 10 | begin 11 | require 'github_changelog_generator/task' 12 | GitHubChangelogGenerator::RakeTask.new(:changelog) do |config| 13 | version = PuppetLint::VERSION 14 | config.user = 'rodjek' 15 | config.project = 'puppet-lint' 16 | config.future_release = version.to_s 17 | config.exclude_labels = %w[duplicate question invalid wontfix release-pr documentation] 18 | config.enhancement_labels = %w[feature] 19 | end 20 | rescue LoadError 21 | $stderr.puts 'Changelog generation requires Ruby 2.0 or higher' 22 | end 23 | 24 | begin 25 | require 'rubocop/rake_task' 26 | 27 | RuboCop::RakeTask.new(:rubocop) do |task| 28 | task.options = %w[-D -E] 29 | task.patterns = [ 30 | 'lib/**/*.rb', 31 | 'spec/**/*.rb', 32 | 'bin/*', 33 | '*.gemspec', 34 | 'Gemfile', 35 | 'Rakefile', 36 | ] 37 | end 38 | rescue LoadError 39 | $stderr.puts 'Rubocop is not available for this version of Ruby.' 40 | end 41 | 42 | task :ci => [:test, :release_test] 43 | 44 | # vim: syntax=ruby 45 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | build: off 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | # ruby versions under test 8 | environment: 9 | matrix: 10 | - RUBY_VERSION: 193 11 | - RUBY_VERSION: 200 12 | 13 | matrix: 14 | allow_failures: 15 | - RUBY_VERSION: 193 16 | 17 | install: 18 | - SET PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% 19 | - SET LOG_SPEC_ORDER=true 20 | - bundle install --jobs 4 --retry 2 --without development 21 | 22 | before_test: 23 | - type Gemfile.lock 24 | - ruby -v 25 | - gem -v 26 | - bundle -v 27 | 28 | test_script: 29 | - bundle exec rake ci 30 | -------------------------------------------------------------------------------- /bin/puppet-lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 4 | 5 | require 'puppet-lint' 6 | 7 | exit PuppetLint::Bin.new(ARGV).run 8 | -------------------------------------------------------------------------------- /lib/puppet-lint/bin.rb: -------------------------------------------------------------------------------- 1 | require 'puppet-lint/optparser' 2 | 3 | # Internal: The logic of the puppet-lint bin script, contained in a class for 4 | # ease of testing. 5 | class PuppetLint::Bin 6 | # Public: Initialise a new PuppetLint::Bin. 7 | # 8 | # args - An Array of command line argument Strings to be passed to the option 9 | # parser. 10 | # 11 | # Examples 12 | # 13 | # PuppetLint::Bin.new(ARGV).run 14 | def initialize(args) 15 | @args = args 16 | end 17 | 18 | # Public: Run puppet-lint as a command line tool. 19 | # 20 | # Returns an Integer exit code to be passed back to the shell. 21 | def run 22 | begin 23 | opts = PuppetLint::OptParser.build(@args) 24 | opts.parse!(@args) 25 | rescue OptionParser::InvalidOption => e 26 | puts "puppet-lint: #{e.message}" 27 | puts "puppet-lint: try 'puppet-lint --help' for more information" 28 | return 1 29 | end 30 | 31 | if PuppetLint.configuration.display_version 32 | puts "puppet-lint #{PuppetLint::VERSION}" 33 | return 0 34 | end 35 | 36 | if PuppetLint.configuration.list_checks 37 | puts PuppetLint.configuration.checks 38 | return 0 39 | end 40 | 41 | if @args[0].nil? 42 | puts 'puppet-lint: no file specified' 43 | puts "puppet-lint: try 'puppet-lint --help' for more information" 44 | return 1 45 | end 46 | 47 | begin 48 | path = @args[0] 49 | path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR 50 | path = if File.directory?(path) 51 | Dir.glob("#{path}/**/*.pp") 52 | else 53 | @args 54 | end 55 | 56 | PuppetLint.configuration.with_filename = true if path.length > 1 57 | 58 | return_val = 0 59 | ignore_paths = PuppetLint.configuration.ignore_paths 60 | 61 | puts '[' if PuppetLint.configuration.json 62 | path.each do |f| 63 | next if ignore_paths.any? { |p| File.fnmatch(p, f) } 64 | l = PuppetLint.new 65 | l.file = f 66 | l.run 67 | l.print_problems 68 | puts ',' if f != path.last && PuppetLint.configuration.json 69 | 70 | if l.errors? || (l.warnings? && PuppetLint.configuration.fail_on_warnings) 71 | return_val = 1 72 | end 73 | 74 | next unless PuppetLint.configuration.fix && l.problems.none? { |r| r[:check] == :syntax } 75 | File.open(f, 'wb') do |fd| 76 | fd.write(l.manifest) 77 | end 78 | end 79 | puts ']' if PuppetLint.configuration.json 80 | 81 | return return_val 82 | rescue PuppetLint::NoCodeError 83 | puts 'puppet-lint: no file specified or specified file does not exist' 84 | puts "puppet-lint: try 'puppet-lint --help' for more information" 85 | return 1 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/puppet-lint/checkplugin.rb: -------------------------------------------------------------------------------- 1 | # Public: A class that contains and provides information for the puppet-lint 2 | # checks. 3 | # 4 | # This class should not be used directly, but instead should be inherited. 5 | # 6 | # Examples 7 | # 8 | # class PuppetLint::Plugin::CheckFoo < PuppetLint::CheckPlugin 9 | # end 10 | class PuppetLint::CheckPlugin 11 | # Internal: Initialise a new PuppetLint::CheckPlugin. 12 | def initialize 13 | @problems = [] 14 | end 15 | 16 | # Internal: Check the manifest for problems and filter out any problems that 17 | # should be ignored. 18 | # 19 | # Returns an Array of problem Hashes. 20 | def run 21 | check 22 | 23 | @problems.each do |problem| 24 | next if problem[:check] == :syntax 25 | next unless PuppetLint::Data.ignore_overrides[problem[:check]].key?(problem[:line]) 26 | 27 | problem[:kind] = :ignored 28 | problem[:reason] = PuppetLint::Data.ignore_overrides[problem[:check]][problem[:line]] 29 | end 30 | 31 | @problems 32 | end 33 | 34 | # Internal: Fix any problems the check plugin has detected. 35 | # 36 | # Returns an Array of problem Hashes. 37 | def fix_problems 38 | @problems.reject { |problem| problem[:kind] == :ignored || problem[:check] == :syntax }.each do |problem| 39 | next unless respond_to?(:fix) 40 | 41 | begin 42 | fix(problem) 43 | problem[:kind] = :fixed 44 | rescue PuppetLint::NoFix # rubocop:disable Lint/HandleExceptions 45 | # noop 46 | end 47 | end 48 | 49 | @problems 50 | end 51 | 52 | private 53 | 54 | # Public: Provides the tokenised manifest to the check plugins. 55 | # 56 | # Returns an Array of PuppetLint::Lexer::Token objects. 57 | def tokens 58 | PuppetLint::Data.tokens 59 | end 60 | 61 | def add_token(index, token) 62 | PuppetLint::Data.insert(index, token) 63 | end 64 | 65 | def remove_token(token) 66 | PuppetLint::Data.delete(token) 67 | end 68 | 69 | # Public: Provides the resource titles to the check plugins. 70 | # 71 | # Returns an Array of PuppetLint::Lexer::Token objects. 72 | def title_tokens 73 | PuppetLint::Data.title_tokens 74 | end 75 | 76 | # Public: Provides positional information for any resource declarations in 77 | # the tokens array to the check plugins. 78 | # 79 | # Returns an Array of Hashes containing the position information. 80 | def resource_indexes 81 | PuppetLint::Data.resource_indexes 82 | end 83 | 84 | # Public: Provides positional information for any class definitions in the 85 | # tokens array to the check plugins. 86 | # 87 | # Returns an Array of Hashes containing the position information. 88 | def class_indexes 89 | PuppetLint::Data.class_indexes 90 | end 91 | 92 | # Public: Provides positional information for any defined type definitions in 93 | # the tokens array to the check plugins. 94 | # 95 | # Returns an Array of Hashes containing the position information. 96 | def defined_type_indexes 97 | PuppetLint::Data.defined_type_indexes 98 | end 99 | 100 | # Public: Provides positional information for any node definitions in the 101 | # tokens array to the check plugins. 102 | # 103 | # Returns an Array of Hashes containing the position information. 104 | def node_indexes 105 | PuppetLint::Data.node_indexes 106 | end 107 | 108 | # Public: Provides the expanded path of the file being analysed to check 109 | # plugins. 110 | # 111 | # Returns the String path. 112 | def fullpath 113 | PuppetLint::Data.fullpath 114 | end 115 | 116 | # Public: Provides the path of the file being analysed as it was provided to 117 | # puppet-lint to the check plugins. 118 | # 119 | # Returns the String path. 120 | def path 121 | PuppetLint::Data.path 122 | end 123 | 124 | # Public: Provides the name of the file being analysed to the check plugins. 125 | # 126 | # Returns the String file name. 127 | def filename 128 | PuppetLint::Data.filename 129 | end 130 | 131 | # Public: Provides a list of formatting tokens to the check plugins. 132 | # 133 | # Returns an Array of Symbol token types. 134 | def formatting_tokens 135 | PuppetLint::Data.formatting_tokens 136 | end 137 | 138 | # Public: Provides a list of manifest lines to the check plugins. 139 | # 140 | # Returns an Array of manifest lines. 141 | def manifest_lines 142 | PuppetLint::Data.manifest_lines 143 | end 144 | 145 | # Internal: Prepare default problem report information. 146 | # 147 | # Returns a Hash of default problem information. 148 | def default_info 149 | @default_info ||= { 150 | :check => self.class.const_get('NAME'), 151 | :fullpath => fullpath, 152 | :path => path, 153 | :filename => filename, 154 | } 155 | end 156 | 157 | # Public: Report a problem with the manifest being checked. 158 | # 159 | # kind - The Symbol problem type (:warning or :error). 160 | # problem - A Hash containing the attributes of the problem 161 | # :message - The String message describing the problem. 162 | # :line - The Integer line number of the location of the problem. 163 | # :column - The Integer column number of the location of the problem. 164 | # :check - The Symbol name of the check that detected the problem. 165 | # 166 | # Returns nothing. 167 | def notify(kind, problem) 168 | problem[:kind] = kind 169 | problem.merge!(default_info) { |_key, v1, _v2| v1 } 170 | 171 | unless [:warning, :error, :fixed].include?(kind) 172 | raise ArgumentError, 'unknown value passed for kind' 173 | end 174 | 175 | [:message, :line, :column, :check].each do |attr| 176 | unless problem.key?(attr) 177 | raise ArgumentError, "problem hash must contain #{attr.inspect}" 178 | end 179 | end 180 | 181 | @problems << problem 182 | end 183 | end 184 | -------------------------------------------------------------------------------- /lib/puppet-lint/checks.rb: -------------------------------------------------------------------------------- 1 | require 'puppet-lint/checkplugin' 2 | 3 | # Internal: Various methods that orchestrate the actions of the puppet-lint 4 | # check plugins. 5 | class PuppetLint::Checks 6 | # Public: Get an Array of problem Hashes. 7 | attr_accessor :problems 8 | 9 | # Public: Initialise a new PuppetLint::Checks object. 10 | def initialize 11 | @problems = [] 12 | end 13 | 14 | # Internal: Tokenise the manifest code and prepare it for checking. 15 | # 16 | # path - The path to the file as passed to puppet-lint as a String. 17 | # content - The String manifest code to be checked. 18 | # 19 | # Returns nothing. 20 | def load_data(path, content) 21 | lexer = PuppetLint::Lexer.new 22 | PuppetLint::Data.path = path 23 | PuppetLint::Data.manifest_lines = content.split("\n", -1) 24 | begin 25 | PuppetLint::Data.tokens = lexer.tokenise(content) 26 | PuppetLint::Data.parse_control_comments 27 | rescue PuppetLint::LexerError => e 28 | message = if e.reason.nil? 29 | 'Syntax error' 30 | else 31 | "Syntax error (#{e.reason})" 32 | end 33 | 34 | problems << { 35 | :kind => :error, 36 | :check => :syntax, 37 | :message => message, 38 | :line => e.line_no, 39 | :column => e.column, 40 | :fullpath => PuppetLint::Data.fullpath, 41 | :path => PuppetLint::Data.path, 42 | :filename => PuppetLint::Data.filename, 43 | } 44 | PuppetLint::Data.tokens = [] 45 | end 46 | end 47 | 48 | # Internal: Run the lint checks over the manifest code. 49 | # 50 | # fileinfo - The path to the file as passed to puppet-lint as a String. 51 | # data - The String manifest code to be checked. 52 | # 53 | # Returns an Array of problem Hashes. 54 | def run(fileinfo, data) 55 | load_data(fileinfo, data) 56 | 57 | checks_run = [] 58 | enabled_checks.each do |check| 59 | klass = PuppetLint.configuration.check_object[check].new 60 | # FIXME: shadowing #problems 61 | problems = klass.run 62 | checks_run << [klass, problems] 63 | end 64 | 65 | checks_run.each do |klass, problems| 66 | if PuppetLint.configuration.fix 67 | @problems.concat(klass.fix_problems) 68 | else 69 | @problems.concat(problems) 70 | end 71 | end 72 | 73 | @problems 74 | rescue PuppetLint::SyntaxError => e 75 | @problems << { 76 | :kind => :error, 77 | :check => :syntax, 78 | :message => 'Syntax error', 79 | :fullpath => File.expand_path(fileinfo, ENV['PWD']), 80 | :filename => File.basename(fileinfo), 81 | :path => fileinfo, 82 | :line => e.token.line, 83 | :column => e.token.column, 84 | } 85 | 86 | @problems 87 | rescue => e 88 | $stdout.puts <<-END.gsub(%r{^ {6}}, '') 89 | Whoops! It looks like puppet-lint has encountered an error that it doesn't 90 | know how to handle. Please open an issue at https://github.com/rodjek/puppet-lint 91 | and paste the following output into the issue description. 92 | --- 93 | puppet-lint version: #{PuppetLint::VERSION} 94 | ruby version: #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} 95 | platform: #{RUBY_PLATFORM} 96 | file path: #{fileinfo} 97 | END 98 | 99 | if File.readable?(fileinfo) 100 | $stdout.puts [ 101 | 'file contents:', 102 | '```', 103 | File.read(fileinfo), 104 | '```', 105 | ].join("\n") 106 | end 107 | 108 | $stdout.puts [ 109 | 'error:', 110 | '```', 111 | "#{e.class}: #{e.message}", 112 | e.backtrace.join("\n"), 113 | '```', 114 | ].join("\n") 115 | 116 | exit 1 117 | end 118 | 119 | # Internal: Get a list of checks that have not been disabled. 120 | # 121 | # Returns an Array of String check names. 122 | def enabled_checks 123 | @enabled_checks ||= begin 124 | PuppetLint.configuration.checks.select do |check| 125 | PuppetLint.configuration.send("#{check}_enabled?") 126 | end 127 | end 128 | end 129 | 130 | # Internal: Render the fixed manifest. 131 | # 132 | # Returns the manifest as a String. 133 | def manifest 134 | PuppetLint::Data.tokens.map(&:to_manifest).join('') 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /lib/puppet-lint/configuration.rb: -------------------------------------------------------------------------------- 1 | class PuppetLint 2 | # Public: A singleton class to store the running configuration of 3 | # puppet-lint. 4 | class Configuration 5 | # Internal: Add helper methods for a new check to the 6 | # PuppetLint::Configuration object. 7 | # 8 | # check - The String name of the check. 9 | # 10 | # Returns nothing. 11 | # 12 | # Signature 13 | # 14 | # _enabled? 15 | # disable_ 16 | # enable_ 17 | def self.add_check(check) 18 | # Public: Determine if the named check is enabled. 19 | # 20 | # Returns true if the check is enabled, otherwise return false. 21 | define_method("#{check}_enabled?") do 22 | settings["#{check}_disabled"] == true ? false : true 23 | end 24 | 25 | # Public: Disable the named check. 26 | # 27 | # Returns nothing. 28 | define_method("disable_#{check}") do 29 | settings["#{check}_disabled"] = true 30 | end 31 | 32 | # Public: Enable the named check. 33 | # 34 | # Returns nothing. 35 | define_method("enable_#{check}") do 36 | settings["#{check}_disabled"] = false 37 | end 38 | end 39 | 40 | # Public: Catch situations where options are being set for the first time 41 | # and create the necessary methods to get & set the option in the future. 42 | # 43 | # args - An Array of values to set the option to. 44 | # method - The String name of the option. 45 | # block - Unused. 46 | # 47 | # Returns nothing. 48 | # 49 | # Signature 50 | # 51 | #