openvox-lint 1.0.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,21 @@
3
3
  # Resource reference titles must start with a capital letter.
4
4
  # e.g. File['/tmp/foo'] not file['/tmp/foo']
5
5
  OpenvoxLint.new_check(:resource_reference_without_title_capital) do
6
+ # Puppet built-in functions and common stdlib functions that accept
7
+ # bracket/index syntax (name[...]). These are NOT resource
8
+ # references and must not be flagged.
9
+ FUNCTION_ALLOWLIST = %w[
10
+ split join include contain require
11
+ each map filter reduce select reject
12
+ slice flatten any all empty
13
+ assert_type create_resources defined dig
14
+ ensure_packages ensure_resource epp fail
15
+ fqdn_rand generate hiera hiera_array hiera_hash hiera_include
16
+ inline_epp inline_template lookup match md5 notice
17
+ realize regsubst sha1 sprintf tag template unique versioncmp
18
+ with
19
+ ].freeze
20
+
6
21
  def check
7
22
  sem = semantic_tokens
8
23
  sem.each_with_index do |tok, i|
@@ -11,8 +26,8 @@ OpenvoxLint.new_check(:resource_reference_without_title_capital) do
11
26
  next unless sem[i + 1].type == :LBRACK
12
27
  # This is name[...] which should be Name[...] for a resource reference
13
28
  next if tok.value =~ /\A[A-Z]/
14
- # Skip function calls and normal array access
15
- next if %w[split join include contain require].include?(tok.value)
29
+ # Skip function calls, method calls, and normal array access
30
+ next if FUNCTION_ALLOWLIST.include?(tok.value)
16
31
  notify :warning,
17
32
  message: "resource reference '#{tok.value}' must start with a capital letter",
18
33
  line: tok.line, column: tok.column
@@ -13,4 +13,20 @@ OpenvoxLint.new_check(:single_quote_string_with_variables) do
13
13
  line: tok.line, column: tok.column
14
14
  end
15
15
  end
16
+
17
+ # --fix support: swap outer single quotes to double (may require manual
18
+ # review for literal $ or inner quotes per style exceptions).
19
+ def fix(problem)
20
+ idx = problem[:line] - 1
21
+ line = @manifest_lines[idx].dup
22
+ col = (problem[:column] || 1) - 1
23
+ if line[col] == "'"
24
+ end_col = line.index("'", col + 1)
25
+ if end_col
26
+ inner = line[(col + 1)...end_col]
27
+ line[col..end_col] = '"' + inner + '"'
28
+ @manifest_lines[idx] = line
29
+ end
30
+ end
31
+ end
16
32
  end
@@ -6,7 +6,9 @@ OpenvoxLint.new_check(:top_scope_facts) do
6
6
  def check
7
7
  tokens.each do |tok|
8
8
  next unless tok.type == :VARIABLE
9
- name = tok.value.sub(/^\$/, '')
9
+ # Strip leading $ and any trailing : left by the lexer when a
10
+ # variable is used as a resource title ($::fact:).
11
+ name = tok.value.sub(/^\$/, '').chomp(':')
10
12
  next unless name.start_with?('::')
11
13
  fact_name = name.sub(/\A::/, '')
12
14
  # Skip module-qualified variables (e.g. ::mymodule::param)
@@ -1,24 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Resource bodies and parameter lists should end with a trailing comma.
4
+ #
5
+ # Only fires inside resource bodies (NAME { ... }) — not inside
6
+ # conditionals (if/unless/case), class bodies, or other brace contexts
7
+ # where a trailing comma is not expected.
4
8
  OpenvoxLint.new_check(:trailing_comma) do
5
9
  def check
6
- tokens.each_with_index do |tok, i|
7
- next unless tok.type == :RBRACE || tok.type == :RPAREN
8
- # Find the previous non-whitespace token
9
- j = i - 1
10
- j -= 1 while j >= 0 && tokens[j].formatting?
11
- next if j < 0
12
- prev = tokens[j]
13
- # Skip if previous is opening brace/paren (empty block) or already a comma
14
- next if %i[LBRACE LPAREN COMMA RBRACE].include?(prev.type)
15
- # Only flag inside parameter lists and resource bodies
16
- next unless prev.type == :SSTRING || prev.type == :STRING || prev.type == :NAME ||
17
- prev.type == :VARIABLE || prev.type == :NUMBER || prev.type == :TRUE ||
18
- prev.type == :FALSE || prev.type == :CLASSREF || prev.type == :RBRACK
10
+ resource_indexes.each do |res|
11
+ params = res[:param_tokens]
12
+ next if params.empty?
13
+
14
+ # Find the last non-formatting token in the resource body
15
+ last = params.reverse_each.find { |t| !t.formatting? }
16
+ next unless last
17
+
18
+ # Already has a trailing comma — nothing to do
19
+ next if last.type == :COMMA
20
+
21
+ # Only flag after value-like tokens (not structural tokens)
22
+ next unless %i[SSTRING STRING NAME VARIABLE NUMBER TRUE FALSE
23
+ CLASSREF RBRACK DQSTRING].include?(last.type)
24
+
19
25
  notify :warning,
20
26
  message: 'missing trailing comma after last attribute',
21
- line: prev.line, column: prev.column
27
+ line: last.line, column: last.column
22
28
  end
23
29
  end
24
30
  end
@@ -11,4 +11,10 @@ OpenvoxLint.new_check(:trailing_whitespace) do
11
11
  column: line.rstrip.length + 1
12
12
  end
13
13
  end
14
+
15
+ # --fix support: strip trailing whitespace from the offending line.
16
+ def fix(problem)
17
+ idx = problem[:line] - 1
18
+ @manifest_lines[idx] = @manifest_lines[idx].rstrip
19
+ end
14
20
  end
@@ -2,18 +2,24 @@
2
2
 
3
3
  # Variables in double-quoted strings should be enclosed in braces.
4
4
  # e.g. "$foo" should be "${foo}"
5
+ #
6
+ # Correctly handles strings with a mix of enclosed and unenclosed
7
+ # variables — e.g. "$foo and ${bar}" flags the unenclosed $foo even
8
+ # though ${bar} is properly enclosed.
5
9
  OpenvoxLint.new_check(:variables_not_enclosed) do
6
10
  def check
7
11
  tokens.each do |tok|
8
12
  next unless tok.type == :DQSTRING || tok.type == :STRING
9
13
  val = tok.value
10
- # Look for $var not followed by { inside double-quoted strings
11
- next unless val =~ /\$([a-zA-Z_][a-zA-Z0-9_:]*)/
12
- next if val =~ /\$\{/ # already enclosed
13
- notify :warning,
14
- message: 'variable not enclosed in braces within string',
15
- line: tok.line,
16
- column: tok.column
14
+ # Scan for any $var that is NOT preceded by ${ (already enclosed).
15
+ # We use a negative lookbehind to skip ${...} patterns and only
16
+ # match bare $varname references.
17
+ val.scan(/(?<!\$)\$(?!\{)([a-zA-Z_][a-zA-Z0-9_:]*)/) do
18
+ notify :warning,
19
+ message: 'variable not enclosed in braces within string',
20
+ line: tok.line,
21
+ column: tok.column
22
+ end
17
23
  end
18
24
  end
19
25
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenvoxLint
4
- VERSION = '1.0.7'
4
+ VERSION = '1.3.0'
5
5
  end
data/lib/openvox-lint.rb CHANGED
@@ -29,11 +29,21 @@ module OpenvoxLint
29
29
  yield(configuration)
30
30
  end
31
31
 
32
+ # Reset the global configuration singleton. Called at the start of
33
+ # every CLI run so that repeated invocations in the same Ruby
34
+ # process (Vim plugins, guard, Rake loops) start clean.
35
+ def reset_configuration!
36
+ @configuration = Configuration.new
37
+ end
38
+
32
39
  def checks
33
40
  @checks ||= {}
34
41
  end
35
42
 
36
43
  def new_check(name, &block)
44
+ if checks.key?(name)
45
+ $stderr.puts "openvox-lint: warning: check '#{name}' is already registered — overwriting" if ENV['OPENVOX_LINT_DEBUG']
46
+ end
37
47
  klass = Class.new(CheckPlugin, &block)
38
48
  klass.instance_variable_set(:@check_name, name)
39
49
  checks[name] = klass
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openvox-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerald Sheets
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-27 00:00:00.000000000 Z
11
+ date: 2026-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -53,10 +53,15 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.50'
55
55
  description: |
56
- openvox-lint is a linter for OpenVox and Puppet manifests. It checks your
57
- code against the Puppet style guide and catches common errors including
58
- deprecated patterns, legacy facts, strict mode violations, and Puppet 8+
59
- language issues. Fully compatible with OpenVox 8.x and Puppet 8.x.
56
+ openvox-lint is a modern style-guide linter for OpenVox and Puppet manifests.
57
+ It checks your .pp files against the OpenVox Language Style Guide (the
58
+ current canonical reference) and catches common errors, deprecated patterns
59
+ (legacy facts, Hiera 3, import, etc.), strict-mode issues, and Puppet 8+ /
60
+ OpenVox 8.x problems. Includes real --fix support for many checks.
61
+
62
+ Fully compatible with OpenVox 8.x and Puppet 8.x. Drop-in replacement for
63
+ the archived puppet-lint with better Ruby 2.5+ support and OpenVox-specific
64
+ checks.
60
65
  email:
61
66
  - jsheets@xai.com
62
67
  executables:
@@ -99,7 +104,6 @@ files:
99
104
  - lib/openvox-lint/plugins/checks/parameter_order.rb
100
105
  - lib/openvox-lint/plugins/checks/puppet_url_without_modules.rb
101
106
  - lib/openvox-lint/plugins/checks/quoted_booleans.rb
102
- - lib/openvox-lint/plugins/checks/relative_classname_inclusion.rb
103
107
  - lib/openvox-lint/plugins/checks/resource_reference_without_title_capital.rb
104
108
  - lib/openvox-lint/plugins/checks/selector_inside_resource.rb
105
109
  - lib/openvox-lint/plugins/checks/single_quote_string_with_variables.rb
@@ -124,9 +128,9 @@ metadata:
124
128
  homepage_uri: https://github.com/cvquesty/openvox-lint
125
129
  source_code_uri: https://github.com/cvquesty/openvox-lint
126
130
  bug_tracker_uri: https://github.com/cvquesty/openvox-lint/issues
127
- changelog_uri: https://github.com/cvquesty/openvox-lint/blob/main/CHANGELOG.md
131
+ changelog_uri: https://github.com/cvquesty/openvox-lint/blob/development/CHANGELOG.md
128
132
  rubygems_mfa_required: 'true'
129
- post_install_message:
133
+ post_install_message:
130
134
  rdoc_options: []
131
135
  require_paths:
132
136
  - lib
@@ -142,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
146
  version: '0'
143
147
  requirements: []
144
148
  rubygems_version: 3.0.3.1
145
- signing_key:
149
+ signing_key:
146
150
  specification_version: 4
147
- summary: Check OpenVox/Puppet manifests against the style guide
151
+ summary: Check OpenVox/Puppet manifests against the OpenVox Language Style Guide
148
152
  test_files: []
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Use absolute class names in include/require/contain statements.
4
- OpenvoxLint.new_check(:relative_classname_inclusion) do
5
- INCLUDE_FUNCS = %w[include require contain].freeze
6
-
7
- def check
8
- sem = semantic_tokens
9
- sem.each_with_index do |tok, i|
10
- next unless tok.type == :NAME && INCLUDE_FUNCS.include?(tok.value)
11
- j = i + 1
12
- next if j >= sem.length
13
- name_tok = sem[j]
14
- next unless name_tok.type == :NAME || name_tok.type == :SSTRING || name_tok.type == :STRING
15
- class_name = name_tok.value.gsub(/['"]/, '')
16
- next if class_name.start_with?('::') || class_name.empty?
17
- # Single-segment names are OK (they're unambiguous)
18
- next unless class_name.include?('::')
19
- notify :warning,
20
- message: "class name '#{class_name}' should be fully qualified (start with ::)",
21
- line: name_tok.line, column: name_tok.column
22
- end
23
- end
24
- end