# ENVS
#
#################################################################################
-[ -z "$HOSTNAME" ] && HOSTNAME=`hostname`
-[ -z "$UID" ] && UID=`id -u`
-[ -z "$USER" ] && USER=`whoami`
-
. "${BASH_SOURCE%/*}/bashrc.function"
NB_ROOT=$(realpath ${BASH_SOURCE%/*}/..)
[ -z "$HOME" ] && HOME=`realpath ~/`
-
-. "${BASH_SOURCE%/*}/bashrc.alias"
+[ -z "$HOSTNAME" ] && HOSTNAME=`hostname`
+[ -z "$UID" ] && UID=`id -u`
+[ -z "$USER" ] && USER=`whoami`
#
# PATH
`
export PATH
+export RUBYLIB=`env-add-path "$RUBYLIB" $NB_ROOT/lib`
+export PERL5LIB=`env-add-path "$PERL5LIB" $NB_ROOT/lib`
+export PYTHONPATH=`env-add-path "$PYTHONPATH" $NB_ROOT/lib`
+
#
# OTHERS
#
export EDITOR=vim
#
-# Color / PS1 / ls
+# Color
#
declare color_prompt color char h
case "$TERM" in
linux) color_prompt=yes;;
esac
+#
+# PS1
+#
h='\h'
#case $(tr '[:upper:]' '[:lower:]' <<<"$HOSTNAME") in
case "$HOSTNAME" in
PS1="\u@\h:\W${char} "
fi
+#
# ls
+#
ls_opt='-a'
case "$OSTYPE" in
darwin*)
alias ll='ls -lh'
unset color char color_prompt h
+#
+# ALIASES
+#
+. "${BASH_SOURCE%/*}/bashrc.alias"
--- /dev/null
+# We're doing this instead of a gem dependency so folks using Puppet
+# from their distro packages don't have to install the gem.
+begin
+ require 'puppet'
+rescue LoadError
+ puts 'Unable to require puppet. Please gem install puppet and try again.'
+ exit 1
+end
+
+require 'puppet-lint/configuration'
+require 'puppet-lint/plugin'
+
+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' % <hash>
+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
+ VERSION = '0.1.12'
+
+ attr_reader :code, :file
+
+ 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 report(problems)
+ 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
+ end
+ end
+ end
+
+ def errors?
+ @statistics[:error] != 0
+ end
+
+ def warnings?
+ @statistics[:warning] != 0
+ end
+
+ def checks
+ PuppetLint::CheckPlugin.repository.map do |plugin|
+ plugin.new.checks
+ end.flatten
+ end
+
+ def run
+ if @data.nil?
+ raise PuppetLint::NoCodeError
+ end
+
+ PuppetLint::CheckPlugin.repository.each do |plugin|
+ report plugin.new.run(@fileinfo, @data)
+ end
+ end
+end
+
+# Default configuration options
+PuppetLint.configuration.fail_on_warnings = false
+PuppetLint.configuration.error_level = :all
+PuppetLint.configuration.with_filename = false
+PuppetLint.configuration.log_format = ''
+
+require 'puppet-lint/plugins'
--- /dev/null
+class 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)
+ self.class.add_check(check)
+ end
+
+ def settings
+ @settings ||= {}
+ end
+
+ def checks
+ self.public_methods.select { |method|
+ method =~ /^.+_enabled\?$/
+ }.map { |method|
+ method[0..-10]
+ }
+ end
+ end
+end
--- /dev/null
+class PuppetLint
+
+ module Plugin
+ module ClassMethods
+ def repository
+ @repository ||= []
+ end
+
+ def inherited(klass)
+ repository << klass
+ end
+ end
+
+ def self.included(klass)
+ klass.extend ClassMethods
+ end
+ end
+end
+
+class PuppetLint::CheckPlugin
+ include PuppetLint::Plugin
+ attr_reader :problems, :checks
+
+ def initialize
+ @problems = []
+ @checks = []
+ @default_info = {:check => 'unknown', :linenumber => 0}
+ end
+
+ def register_check(check)
+ @checks << check
+ 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:
+ # <i>message</i>:: which contains a string value describing the problem
+ # <i>linenumber</i>:: 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
+ # <i>check</i>:: which contains the name of the check that is being executed.
+ # <i>linenumber</i>:: 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 run(fileinfo, data)
+ lexer = Puppet::Parser::Lexer.new
+ lexer.string = data
+ @tokens = lexer.fullscan
+ @fileinfo = fileinfo
+ @data = data
+
+ self.public_methods.select { |method|
+ method.to_s.start_with? 'lint_check_'
+ }.each { |method|
+ name = method.to_s[11..-1]
+ @default_info[:check] = name
+ self.send(method) if PuppetLint.configuration.send("#{name}_enabled?")
+ }
+
+ @problems
+ end
+
+ def filter_tokens
+ @title_tokens = []
+ @resource_indexes = []
+ @class_indexes = []
+ @defined_type_indexes = []
+
+ @tokens.each_index do |token_idx|
+ if @tokens[token_idx].first == :COLON
+ # gather a list of tokens that are resource titles
+ if @tokens[token_idx-1].first == :RBRACK
+ title_array_tokens = @tokens[@tokens.rindex { |r| r.first == :LBRACK }+1..token_idx-2]
+ @title_tokens += title_array_tokens.select { |token| [:STRING, :NAME].include? token.first }
+ else
+ if @tokens[token_idx + 1].first != :LBRACE
+ @title_tokens << @tokens[token_idx-1]
+ end
+ end
+
+ # gather a list of start and end indexes for resource attribute blocks
+ if @tokens[token_idx+1].first != :LBRACE
+ @resource_indexes << {:start => token_idx+1, :end => @tokens[token_idx+1..-1].index { |r| [:SEMIC, :RBRACE].include? r.first }+token_idx}
+ end
+ elsif [:CLASS, :DEFINE].include? @tokens[token_idx].first
+ lbrace_count = 0
+ @tokens[token_idx+1..-1].each_index do |class_token_idx|
+ idx = class_token_idx + token_idx
+ if @tokens[idx].first == :LBRACE
+ lbrace_count += 1
+ elsif @tokens[idx].first == :RBRACE
+ lbrace_count -= 1
+ if lbrace_count == 0
+ if @tokens[token_idx].first == :CLASS and @tokens[token_idx + 1].first != :LBRACE
+ @class_indexes << {:start => token_idx, :end => idx}
+ end
+ @defined_type_indexes << {:start => token_idx, :end => idx} if @tokens[token_idx].first == :DEFINE
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def tokens
+ @tokens
+ end
+
+ def path
+ @fileinfo[:path]
+ end
+
+ def fullpath
+ @fileinfo[:fullpath]
+ end
+
+ def data
+ @data
+ end
+
+ def title_tokens
+ filter_tokens if @title_tokens.nil?
+ @title_tokens
+ end
+
+ def resource_indexes
+ filter_tokens if @resource_indexes.nil?
+ @resource_indexes
+ end
+
+ def class_indexes
+ filter_tokens if @class_indexes.nil?
+ @class_indexes
+ end
+
+ def defined_type_indexes
+ filter_tokens if @defined_type_indexes.nil?
+ @defined_type_indexes
+ end
+
+ def manifest_lines
+ @manifest_lines ||= @data.split("\n")
+ end
+
+ def self.check(name, &b)
+ PuppetLint.configuration.add_check name
+ define_method("lint_check_#{name}", b)
+ end
+end
+
--- /dev/null
+class PuppetLint
+ class Plugins
+ end
+end
+
+require 'puppet-lint/plugins/check_classes'
+require 'puppet-lint/plugins/check_conditionals'
+require 'puppet-lint/plugins/check_strings'
+require 'puppet-lint/plugins/check_variables'
+require 'puppet-lint/plugins/check_whitespace'
+require 'puppet-lint/plugins/check_resources'
--- /dev/null
+class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
+ if Puppet::PUPPETVERSION !~ /^0\.2/
+ check 'right_to_left_relationship' do
+ tokens.select { |r| r.first == :OUT_EDGE }.each do |token|
+ notify :warning, :message => "right-to-left (<-) relationship", :linenumber => token.last[:line]
+ end
+ end
+ end
+
+ check 'autoloader_layout' do
+ unless fullpath == ""
+ (class_indexes + defined_type_indexes).each do |class_idx|
+ title_token = tokens[class_idx[:start]+1]
+ split_title = title_token.last[:value].split('::')
+ if split_title.length > 1
+ expected_path = "#{split_title.first}/manifests/#{split_title[1..-1].join('/')}.pp"
+ else
+ expected_path = "#{title_token.last[:value]}/manifests/init.pp"
+ end
+
+ unless fullpath.end_with? expected_path
+ notify :error, :message => "#{title_token.last[:value]} not in autoload module layout", :linenumber => title_token.last[:line]
+ end
+ end
+ end
+ end
+
+ check 'parameter_order' do
+ (class_indexes + defined_type_indexes).each do |class_idx|
+ token_idx = class_idx[:start]
+ header_end_idx = tokens[token_idx..-1].index { |r| r.first == :LBRACE }
+ lparen_idx = tokens[token_idx..(header_end_idx + token_idx)].index { |r| r.first == :LPAREN }
+ rparen_idx = tokens[token_idx..(header_end_idx + token_idx)].rindex { |r| r.first == :RPAREN }
+
+ unless lparen_idx.nil? or rparen_idx.nil?
+ param_tokens = 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]
+ prev_token = param_tokens[param_tokens_idx-1]
+ if this_token.first == :VARIABLE
+ unless next_token.nil?
+ if next_token.first == :COMMA or next_token.first == :RPAREN
+ unless param_tokens[0..param_tokens_idx].rindex { |r| r.first == :EQUALS }.nil?
+ unless prev_token.nil? or prev_token.first == :EQUALS
+ notify :warning, :message => "optional parameter listed before required parameter", :linenumber => this_token.last[:line]
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ check 'inherits_across_namespaces' do
+ class_indexes.each do |class_idx|
+ token_idx = class_idx[:start]
+ if tokens[token_idx+2].first == :INHERITS
+ class_name = tokens[token_idx+1].last[:value]
+ inherited_class = tokens[token_idx+3].last[:value]
+
+ unless class_name =~ /^#{inherited_class}::/
+ notify :warning, :message => "class inherits across namespaces", :linenumber => tokens[token_idx].last[:line]
+ end
+ end
+ end
+ end
+
+ check 'nested_classes_or_defines' do
+ class_indexes.each do |class_idx|
+ class_tokens = tokens[class_idx[:start]..class_idx[:end]]
+ class_tokens[1..-1].each_index do |token_idx|
+ token = class_tokens[1..-1][token_idx]
+ next_token = class_tokens[1..-1][token_idx + 1]
+
+ if token.first == :CLASS
+ if next_token.first != :LBRACE
+ notify :warning, :message => "class defined inside a class", :linenumber => token.last[:line]
+ end
+ end
+
+ if token.first == :DEFINE
+ notify :warning, :message => "define defined inside a class", :linenumber => token.last[:line]
+ end
+ end
+ end
+ end
+
+ check 'variable_scope' do
+ (class_indexes + defined_type_indexes).each do |idx|
+ object_tokens = tokens[idx[:start]..idx[:end]]
+ variables_in_scope = ['name', 'title', 'module_name', 'environment', 'clientcert', 'clientversion', 'servername', 'serverip', 'serverversion', 'caller_module_name']
+ referenced_variables = []
+ header_end_idx = object_tokens.index { |r| r.first == :LBRACE }
+ lparen_idx = object_tokens[0..header_end_idx].index { |r| r.first == :LPAREN }
+ rparen_idx = object_tokens[0..header_end_idx].rindex { |r| r.first == :RPAREN }
+
+ 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.first == :VARIABLE
+ if [:COMMA, :EQUALS, :RPAREN].include? next_token.first
+ variables_in_scope << this_token.last[: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.first == :VARIABLE
+ if next_token.first == :EQUALS
+ variables_in_scope << this_token.last[:value]
+ else
+ referenced_variables << this_token
+ end
+ end
+ end
+
+ referenced_variables.each do |token|
+ unless token.last[:value].include? '::'
+ unless variables_in_scope.include? token.last[:value]
+ unless token.last[:value] =~ /\d+/
+ notify :warning, :message => "top-scope variable being used without an explicit namespace", :linenumber => token.last[:line]
+ end
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+class PuppetLint::Plugins::CheckConditionals < PuppetLint::CheckPlugin
+ check 'selector_inside_resource' do
+ resource_indexes.each do |resource|
+ resource_tokens = tokens[resource[:start]..resource[:end]]
+
+ resource_tokens.each_index do |resource_token_idx|
+ if resource_tokens[resource_token_idx].first == :FARROW
+ if resource_tokens[resource_token_idx + 1].first == :VARIABLE
+ unless resource_tokens[resource_token_idx + 2].nil?
+ if resource_tokens[resource_token_idx + 2].first == :QMARK
+ notify :warning, :message => "selector inside resource block", :linenumber => resource_tokens[resource_token_idx].last[:line]
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ check 'case_without_default' do
+ case_indexes = []
+
+ tokens.each_index do |token_idx|
+ if tokens[token_idx].first == :COLON
+ # gather a list of start and end indexes for resource attribute blocks
+ if tokens[token_idx+1].first != :LBRACE
+ resource_indexes << {:start => token_idx+1, :end => tokens[token_idx+1..-1].index { |r| [:SEMIC, :RBRACE].include? r.first }+token_idx}
+ end
+ end
+
+ if tokens[token_idx].first == :CASE
+ lbrace_count = 0
+ tokens[token_idx+1..-1].each_index do |case_token_idx|
+ idx = case_token_idx + token_idx
+ if tokens[idx].first == :LBRACE
+ lbrace_count += 1
+ elsif tokens[idx].first == :RBRACE
+ lbrace_count -= 1
+ if lbrace_count == 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.first == :DEFAULT }
+ notify :warning, :message => "case statement without a default case", :linenumber => case_tokens.first.last[:line]
+ end
+ end
+ end
+end
--- /dev/null
+# Resources
+# http://docs.puppetlabs.com/guides/style_guide.html#resources
+
+class PuppetLint::Plugins::CheckResources < PuppetLint::CheckPlugin
+ check 'unquoted_resource_title' do
+ title_tokens.each do |token|
+ if token.first == :NAME
+ notify :warning, :message => "unquoted resource title", :linenumber => token.last[:line]
+ end
+ end
+ end
+
+ check 'ensure_first_param' do
+ resource_indexes.each do |resource|
+ resource_tokens = tokens[resource[:start]..resource[:end]]
+ ensure_attr_index = resource_tokens.index { |token| token.first == :NAME and token.last[:value] == 'ensure' }
+ unless ensure_attr_index.nil?
+ if ensure_attr_index > 1
+ ensure_attr_line_no = resource_tokens[ensure_attr_index].last[:line]
+ notify :warning, :message => "ensure found on line but it's not the first attribute", :linenumber => ensure_attr_line_no
+ end
+ end
+ end
+ end
+
+ check 'unquoted_file_mode' do
+ resource_indexes.each do |resource|
+ resource_tokens = tokens[resource[:start]..resource[:end]]
+ resource_type_token = tokens[tokens[0..resource[:start]].rindex { |r| r.first == :LBRACE } - 1]
+ if resource_type_token.last[:value] == "file"
+ resource_tokens.each_index do |resource_token_idx|
+ attr_token = resource_tokens[resource_token_idx]
+ if attr_token.first == :NAME and attr_token.last[:value] == 'mode'
+ value_token = resource_tokens[resource_token_idx + 2]
+ if value_token.first == :NAME
+ notify :warning, :message => "unquoted file mode", :linenumber => value_token.last[:line]
+ end
+ end
+ end
+ end
+ end
+ end
+
+ check 'file_mode' do
+ resource_indexes.each do |resource|
+ resource_tokens = tokens[resource[:start]..resource[:end]]
+ resource_type_token = tokens[tokens[0..resource[:start]].rindex { |r| r.first == :LBRACE } - 1]
+ if resource_type_token.last[:value] == "file"
+ resource_tokens.each_index do |resource_token_idx|
+ attr_token = resource_tokens[resource_token_idx]
+ if attr_token.first == :NAME and attr_token.last[:value] == 'mode'
+ value_token = resource_tokens[resource_token_idx + 2]
+ if value_token.last[:value] !~ /\d{4}/ and value_token.first != :VARIABLE and value_token.last[:value] !~ /^([ugoa]*[-=+][-=+rstwxXugo]*)(,[ugoa]*[-=+][-=+rstwxXugo]*)*$/
+ notify :warning, :message => "mode should be represented as a 4 digit octal value or symbolic file mode", :linenumber => value_token.last[:line]
+ end
+ end
+ end
+ end
+ end
+ end
+
+ check 'ensure_not_symlink_target' do
+ resource_indexes.each do |resource|
+ resource_tokens = tokens[resource[:start]..resource[:end]]
+ resource_type_token = tokens[tokens[0..resource[:start]].rindex { |r| r.first == :LBRACE } - 1]
+ if resource_type_token.last[:value] == "file"
+ resource_tokens.each_index do |resource_token_idx|
+ attr_token = resource_tokens[resource_token_idx]
+ if attr_token.first == :NAME and attr_token.last[:value] == 'ensure'
+ value_token = resource_tokens[resource_token_idx + 2]
+ if value_token.last[:value].start_with? '/'
+ notify :warning, :message => "symlink target specified in ensure attr", :linenumber => value_token.last[:line]
+ end
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+class PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin
+ class ::Puppet::Parser::Lexer
+ class TokenList
+ def del_token(token)
+ @tokens.delete(token)
+ end
+ end
+
+ TOKENS.add_tokens("<single quotes string>" => :SSTRING)
+ TOKENS.del_token(:SQUOTE)
+
+ if Puppet::PUPPETVERSION =~ /^0\.2/
+ TOKENS.add_token :SQUOTE, "'" do |lexer, value|
+ value = lexer.slurpstring(value)
+ [TOKENS[:SSTRING], value]
+ end
+ else
+ TOKENS.add_token :SQUOTE, "'" do |lexer, value|
+ [ TOKENS[:SSTRING], lexer.slurpstring(value,["'"],:ignore_invalid_escapes).first ]
+ end
+ end
+ end
+
+ check 'double_quoted_strings' do
+ tokens.each_index do |token_idx|
+ token = tokens[token_idx]
+
+ if token.first == :STRING
+ unless token.last[:value].include? "\t" or token.last[:value].include? "\n"
+ notify :warning, :message => "double quoted string containing no variables", :linenumber => token.last[:line]
+ end
+ elsif token.first == :DQTEXT
+ unless token.last[:value].include? "\\t" or token.last[:value].include? "\\n" or token.last[:value] =~ /[^\\]?\$\{?/
+ notify :warning, :message => "double quoted string containing no variables", :linenumber => token.last[:line]
+ end
+ end
+ end
+ end
+
+ check 'only_variable_string' do
+ tokens.each_index do |token_idx|
+ token = tokens[token_idx]
+
+ if token.first == :DQPRE and token.last[:value] == ""
+ if tokens[token_idx + 1].first == :VARIABLE
+ if tokens[token_idx + 2].first == :DQPOST and tokens[token_idx + 2].last[:value] == ""
+ notify :warning, :message => "string containing only a variable", :linenumber => tokens[token_idx + 1].last[:line]
+ end
+ end
+ end
+ if token.first == :DQTEXT and token.last[:value] =~ /\A\$\{.+\}\Z/
+ notify :warning, :message => "string containing only a variable", :linenumber => token.last[:line]
+ end
+ end
+ end
+
+ check 'variables_not_enclosed' do
+ tokens.each_index do |token_idx|
+ token = tokens[token_idx]
+
+ if token.first == :DQPRE
+ end_of_string_idx = tokens[token_idx..-1].index { |r| r.first == :DQPOST }
+ tokens[token_idx..end_of_string_idx].each do |t|
+ if t.first == :VARIABLE
+ line = data.split("\n")[t.last[:line] - 1]
+ if line.is_a? String and line.include? "$#{t.last[:value]}"
+ notify :warning, :message => "variable not enclosed in {}", :linenumber => t.last[:line]
+ end
+ end
+ end
+ elsif token.first == :DQTEXT and token.last[:value] =~ /\$\w+/
+ notify :warning, :message => "variable not enclosed in {}", :linenumber => token.last[:line]
+ end
+ end
+ end
+
+ check 'single_quote_string_with_variables' do
+ tokens.each_index do |token_idx|
+ token = tokens[token_idx]
+
+ if token.first == :SSTRING
+ contents = token.last[:value]
+ line_no = token.last[:line]
+
+ if contents.include? '${'
+ notify :error, :message => "single quoted string containing a variable found", :linenumber => token.last[:line]
+ end
+ end
+ end
+ end
+
+ check 'quoted_booleans' do
+ tokens.each_index do |token_idx|
+ token = tokens[token_idx]
+
+ if token.first == :SSTRING
+ contents = token.last[:value]
+ line_no = token.last[:line]
+
+ if ['true', 'false'].include? contents
+ notify :warning, :message => "quoted boolean value found", :linenumber => token.last[:line]
+ end
+ end
+ end
+ end
+end
--- /dev/null
+class PuppetLint::Plugins::CheckVariables < PuppetLint::CheckPlugin
+ check 'variable_contains_dash' do
+ tokens.each_index do |token_idx|
+ token = tokens[token_idx]
+
+ if token.first == :VARIABLE
+ variable = token.last[:value]
+ line_no = token.last[:line]
+ if variable.match(/-/)
+ notify :warning, :message => "variable contains a dash", :linenumber => line_no
+ end
+ end
+
+ if token.first == :DQPRE
+ end_of_string_idx = tokens[token_idx..-1].index { |r| r.first == :DQPOST }
+ tokens[token_idx..end_of_string_idx].each do |t|
+ if t.first == :VARIABLE and t.last[:value].match(/-/)
+ notify :warning, :message => "variable contains a dash", :linenumber => t.last[:line]
+ end
+ end
+ end
+ end
+ end
+end
--- /dev/null
+# Spacing, Identation & Whitespace
+# http://docs.puppetlabs.com/guides/style_guide.html#spacing-indentation--whitespace
+
+class PuppetLint::Plugins::CheckWhitespace < PuppetLint::CheckPlugin
+ check 'hard_tabs' do
+ line_no = 0
+ manifest_lines.each do |line|
+ line_no += 1
+
+ # MUST NOT use literal tab characters
+ notify :error, :message => "tab character found", :linenumber => line_no if line.include? "\t"
+ end
+ end
+
+ check 'trailing_whitespace' do
+ line_no = 0
+ manifest_lines.each do |line|
+ line_no += 1
+
+ # MUST NOT contain trailing white space
+ notify :error, :message => "trailing whitespace found", :linenumber => line_no if line.end_with? " "
+ end
+ end
+
+ check '80chars' do
+ line_no = 0
+ manifest_lines.each do |line|
+ line_no += 1
+
+ # SHOULD NOT exceed an 80 character line width
+ unless line =~ /puppet:\/\//
+ notify :warning, :message => "line has more than 80 characters", :linenumber => line_no if line.length > 80
+ end
+ end
+ end
+
+ check '2sp_soft_tabs' do
+ line_no = 0
+ manifest_lines.each do |line|
+ line_no += 1
+
+ # MUST use two-space soft tabs
+ line.scan(/^ +/) do |prefix|
+ unless prefix.length % 2 == 0
+ notify :error, :message => "two-space soft tabs not used", :linenumber => line_no
+ end
+ end
+ end
+ end
+
+ check 'arrow_alignment' do
+ line_no = 0
+ in_resource = false
+ selectors = []
+ resource_indent_length = 0
+ manifest_lines.each do |line|
+ line_no += 1
+
+ # SHOULD align fat comma arrows (=>) within blocks of attributes
+ if line =~ /^( +.+? +)=>/
+ line_indent = $1
+ if in_resource
+ if selectors.count > 0
+ if selectors.last == 0
+ selectors[-1] = line_indent.length
+ end
+
+ # check for length first
+ unless line_indent.length == selectors.last
+ notify :warning, :message => "=> on line isn't properly aligned for selector", :linenumber => line_no
+ end
+
+ # then for a new selector or selector finish
+ if line.strip.end_with? "{"
+ selectors.push(0)
+ elsif line.strip =~ /\}[,;]?$/
+ selectors.pop
+ end
+ else
+ unless line_indent.length == resource_indent_length
+ notify :warning, :message => "=> on line isn't properly aligned for resource", :linenumber => line_no
+ end
+
+ if line.strip.end_with? "{"
+ selectors.push(0)
+ end
+ end
+ else
+ resource_indent_length = line_indent.length
+ in_resource = true
+ if line.strip.end_with? "{"
+ selectors.push(0)
+ end
+ end
+ elsif line.strip =~ /\}[,;]?$/ and selectors.count > 0
+ selectors.pop
+ else
+ in_resource = false
+ resource_indent_length = 0
+ end
+ end
+ end
+end
--- /dev/null
+require 'puppet-lint'
+require 'rake'
+require 'rake/tasklib'
+
+class PuppetLint
+ class RakeTask < ::Rake::TaskLib
+ def initialize(*args)
+ desc 'Run puppet-lint'
+
+ task :lint do
+ RakeFileUtils.send(:verbose, true) do
+ linter = PuppetLint.new
+ Dir.glob('**/*.pp').each do |puppet_file|
+ puts "Evaluating #{puppet_file}"
+ linter.file = puppet_file
+ linter.run
+ end
+ fail if linter.errors?
+ end
+ end
+ end
+ end
+end
+
+PuppetLint::RakeTask.new