openvox-lint 1.0.8 → 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.
data/README.md CHANGED
@@ -4,12 +4,12 @@
4
4
  [![Gem Downloads](https://img.shields.io/gem/dt/openvox-lint)](https://rubygems.org/gems/openvox-lint)
5
5
  ![Ruby](https://img.shields.io/badge/ruby-%E2%89%A5%202.5-red)
6
6
  ![License](https://img.shields.io/badge/license-Apache%202.0-blue)
7
- ![Checks](https://img.shields.io/badge/built--in%20checks-38-brightgreen)
7
+ ![Checks](https://img.shields.io/badge/built--in%20checks-37-brightgreen)
8
8
  ![Status](https://img.shields.io/badge/status-stable-brightgreen)
9
9
 
10
10
  **A style-guide linter for OpenVox and Puppet manifests.**
11
11
 
12
- openvox-lint checks your `.pp` manifest files against the [Puppet Style Guide](https://puppet.com/docs/puppet/latest/style_guide.html) and catches common errors, deprecated patterns, legacy facts, strict-mode violations, and Puppet 8+ / OpenVox 8.x language issues.
12
+ openvox-lint checks your `.pp` manifest files against the [OpenVox Language Style Guide](https://docs.openvoxproject.org/openvox/latest/style_guide.html) (and Puppet equivalent) and catches common errors, deprecated patterns, legacy facts, strict-mode violations, and Puppet 8+ / OpenVox 8.x language issues.
13
13
 
14
14
  Fully compatible with:
15
15
  - **OpenVox 8.x** (the community-maintained open-source fork of Puppet)
@@ -25,7 +25,7 @@ Fully compatible with:
25
25
  - [Quick Start](#quick-start)
26
26
  - [Usage](#usage)
27
27
  - [Enabling and Disabling Checks](#enabling-and-disabling-checks)
28
- - [Built-in Checks (38)](#built-in-checks)
28
+ - [Built-in Checks (37)](#built-in-checks)
29
29
  - [Configuration](#configuration)
30
30
  - [Output Formats](#output-formats)
31
31
  - [Integration](#integration)
@@ -33,6 +33,7 @@ Fully compatible with:
33
33
  - [Updating](#updating)
34
34
  - [Uninstalling](#uninstalling)
35
35
  - [Development](#development)
36
+ - [Contributors](#contributors)
36
37
  - [License](#license)
37
38
 
38
39
  ---
@@ -159,6 +160,27 @@ openvox-lint -f github manifests/
159
160
  openvox-lint --fail-on-warnings manifests/
160
161
  ```
161
162
 
163
+ ### Fix mode (`--fix`)
164
+
165
+ `--fix` automatically corrects problems for the checks that implement fix logic. Fixes are applied in-place and are safe for the supported cases.
166
+
167
+ Currently supported checks:
168
+ - `trailing_whitespace` — removes trailing spaces/tabs
169
+ - `hard_tabs` — converts tabs to two spaces
170
+ - `quoted_booleans` — changes `'true'` / `'false'` (strings) to unquoted booleans
171
+ - `double_quoted_strings` — converts unnecessary double-quoted strings (no interpolation or escapes) to single quotes
172
+ - `single_quote_string_with_variables` — converts single-quoted strings containing `$variables` to double quotes so interpolation works
173
+
174
+ Example:
175
+
176
+ ```bash
177
+ openvox-lint --fix manifests/
178
+ # or
179
+ openvox-lint --fix .
180
+ ```
181
+
182
+ After running, re-lint to confirm. Complex structural fixes (e.g. arrow alignment) are not yet implemented.
183
+
162
184
  ---
163
185
 
164
186
  ## Enabling and Disabling Checks
@@ -182,7 +204,7 @@ openvox-lint --no-line_length-check --no-arrow_alignment-check --no-strict_inden
182
204
  Use `--only-checks` to run *only* the checks you specify (everything else is skipped):
183
205
 
184
206
  ```bash
185
- # Only check for legacy facts and hiera3 usage
207
+ # Only check for legacy facts and deprecated Hiera 3 functions
186
208
  openvox-lint --only-checks legacy_facts,hiera3_function,top_scope_facts manifests/
187
209
 
188
210
  # Only check for errors (skip all warnings)
@@ -232,13 +254,13 @@ To see every check that openvox-lint can run:
232
254
  openvox-lint --list-checks
233
255
  ```
234
256
 
235
- This shows the check name, severity (warning or error), and description for all 38 built-in checks.
257
+ This shows the check name, severity (warning or error), and description for all 37 built-in checks.
236
258
 
237
259
  ---
238
260
 
239
261
  ## Built-in Checks
240
262
 
241
- openvox-lint ships with **38 built-in checks** organized into categories:
263
+ openvox-lint ships with **37 built-in checks** organized into categories:
242
264
 
243
265
  ### Whitespace & Formatting (5 checks)
244
266
 
@@ -302,13 +324,12 @@ openvox-lint ships with **38 built-in checks** organized into categories:
302
324
  | `case_without_default` | warning | Case statements must have a `default` case |
303
325
  | `selector_inside_resource` | warning | Don't use selectors inside resource bodies |
304
326
 
305
- ### References & Syntax (4 checks)
327
+ ### References & Syntax (3 checks)
306
328
 
307
329
  | Check | Severity | Description |
308
330
  |-------|----------|-------------|
309
331
  | `leading_zero` | warning | No leading zeros in numbers (except file modes) |
310
332
  | `resource_reference_without_title_capital` | warning | Capitalise resource reference types |
311
- | `relative_classname_inclusion` | warning | Use fully qualified class names |
312
333
  | `autoloader_layout` | warning | Class/define name must match file path |
313
334
 
314
335
  ### Comments (1 check)
@@ -335,7 +356,7 @@ openvox-lint ships with **38 built-in checks** organized into categories:
335
356
  |-------|----------|-------------|
336
357
  | `legacy_facts` | warning | Legacy facts removed in Puppet 8; use `$facts['...']` |
337
358
  | `top_scope_facts` | warning | `$::fact` references; use `$facts['fact']` |
338
- | `hiera3_function` | **error** | `hiera()` / `hiera_hash()` removed; use `lookup()` |
359
+ | `hiera3_function` | **error** | Deprecated Hiera 3 functions removed; use Hiera 5 `lookup()` |
339
360
  | `import_statement` | **error** | `import` removed in Puppet 4+ |
340
361
 
341
362
  ---
@@ -382,7 +403,7 @@ class foo { # lint:ignore:documentation
382
403
 
383
404
  ```
384
405
  manifests/init.pp:5:15: WARNING: unquoted_file_mode: unquoted file mode
385
- manifests/init.pp:9:3: ERROR: hiera3_function: 'hiera()' is removed in Puppet 8
406
+ manifests/init.pp:9:3: ERROR: hiera3_function: deprecated Hiera 3 function 'hiera()' — use Hiera 5 lookup() instead
386
407
  ```
387
408
 
388
409
  ### JSON (`-f json`)
@@ -617,14 +638,14 @@ openvox-lint/
617
638
 
618
639
  ## Comparison with puppet-lint
619
640
 
620
- | Feature | puppet-lint 5.x | openvox-lint 1.0 |
641
+ | Feature | puppet-lint 5.x | openvox-lint 1.3.0 |
621
642
  |---------|-----------------|------------------|
622
643
  | Ruby requirement | ≥ 3.1 | ≥ 2.5 (RHEL 8, macOS, all modern platforms) |
623
644
  | Runtime dependencies | None | None |
624
- | Built-in checks | ~25 | 38 |
645
+ | Built-in checks | ~25 | 37 |
625
646
  | Legacy facts detection | Via plugin | Built-in |
626
647
  | Top-scope facts detection | Via plugin | Built-in |
627
- | Hiera 3 detection | No | Built-in (error) |
648
+ | Deprecated Hiera 3 function detection | No | Built-in (error) |
628
649
  | Import statement detection | No | Built-in (error) |
629
650
  | Strict indent check | Via plugin | Built-in |
630
651
  | GitHub Actions output | No | Built-in (`-f github`) |
@@ -633,7 +654,7 @@ openvox-lint/
633
654
  | Custom log format | Yes | Yes (compatible) |
634
655
  | OpenVox awareness | No | Yes |
635
656
  | Plugin system | Yes | Yes (compatible) |
636
- | `--fix` support | Yes | Yes |
657
+ | `--fix` support | Yes | Yes (real implementations for trailing_whitespace, hard_tabs, quoted_booleans, double_quoted_strings, single_quote_string_with_variables + line-based) |
637
658
  | vim-openvox integration | No | Native |
638
659
 
639
660
  ---
@@ -650,6 +671,16 @@ Apache License, Version 2.0. See [LICENSE](LICENSE) for details.
650
671
 
651
672
  ---
652
673
 
674
+ ## Contributors
675
+
676
+ Thanks to the following people for their contributions to openvox-lint:
677
+
678
+ | Contributor | Contribution |
679
+ |-------------|--------------|
680
+ | **[@hbro](https://github.com/hbro)** (Hans Broeckx) | Fixed `legacy_facts` false positives on local variables ([PR #1](https://github.com/cvquesty/openvox-lint/pull/1)) — *First community contribution!* 🎉 |
681
+
682
+ Want to contribute? See [CONTRIBUTING.md](CONTRIBUTING.md) to get started!
683
+
653
684
  ## Author
654
685
 
655
686
  Jerald Sheets ([@cvquesty](https://github.com/cvquesty))
@@ -42,6 +42,24 @@ module OpenvoxLint
42
42
  fullpath: filepath, configuration: @config,
43
43
  )
44
44
  @problems.concat(checker.run)
45
+
46
+ # Apply --fix: checks' #fix methods (if implemented) mutate the shared
47
+ # manifest_lines array in place. After all checks, write the (possibly
48
+ # fixed) content back if it differs. This makes --fix functional for
49
+ # supported checks. Ensures final newline per style guide.
50
+ if @config.fix
51
+ begin
52
+ fixed_code = lexer.manifest_lines.join("\n") + "\n"
53
+ if fixed_code != code
54
+ File.write(filepath, fixed_code)
55
+ end
56
+ rescue StandardError => write_err
57
+ @problems << {
58
+ path: filepath, line: 0, column: 0, kind: :error,
59
+ check: :fix, message: "Failed to write fixes: #{write_err.message}",
60
+ }
61
+ end
62
+ end
45
63
  rescue StandardError => e
46
64
  @problems << {
47
65
  path: filepath, line: 0, column: 0, kind: :error,
@@ -24,4 +24,20 @@ OpenvoxLint.new_check(:double_quoted_strings) do
24
24
  column: tok.column
25
25
  end
26
26
  end
27
+
28
+ # --fix support: convert the double-quoted string (no interp) to single
29
+ # quotes by swapping the outer delimiters at the reported column.
30
+ def fix(problem)
31
+ idx = problem[:line] - 1
32
+ line = @manifest_lines[idx].dup
33
+ col = (problem[:column] || 1) - 1
34
+ if line[col] == '"'
35
+ end_col = line.index('"', col + 1)
36
+ if end_col
37
+ inner = line[(col + 1)...end_col]
38
+ line[col..end_col] = "'" + inner + "'"
39
+ @manifest_lines[idx] = line
40
+ end
41
+ end
42
+ end
27
43
  end
@@ -12,4 +12,11 @@ OpenvoxLint.new_check(:hard_tabs) do
12
12
  column: col + 1
13
13
  end
14
14
  end
15
+
16
+ # --fix support: replace all hard tabs with two spaces (simple but effective
17
+ # for consistent soft-tab style; more sophisticated indent fixers exist).
18
+ def fix(problem)
19
+ idx = problem[:line] - 1
20
+ @manifest_lines[idx] = @manifest_lines[idx].gsub("\t", ' ')
21
+ end
15
22
  end
@@ -1,7 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Hiera 3 functions (hiera, hiera_array, hiera_hash, hiera_include)
4
- # are removed in Puppet 8 / OpenVox 8. Use lookup() instead.
3
+ # Detects deprecated Hiera 3 functions that are removed in Puppet 8 / OpenVox 8.
4
+ #
5
+ # Hiera 3 is fully deprecated. Only Hiera 5 is supported in Puppet 8+.
6
+ # The legacy hiera() functions were compatibility shims that have been removed.
7
+ #
8
+ # Deprecated functions:
9
+ # - hiera() -> lookup()
10
+ # - hiera_array() -> lookup(..., Array, 'unique')
11
+ # - hiera_hash() -> lookup(..., Hash, 'hash')
12
+ # - hiera_include() -> lookup(...).include
13
+ #
14
+ # Use the Hiera 5 lookup() function instead.
5
15
  OpenvoxLint.new_check(:hiera3_function) do
6
16
  HIERA3_FUNCS = %w[hiera hiera_array hiera_hash hiera_include].freeze
7
17
 
@@ -9,7 +19,7 @@ OpenvoxLint.new_check(:hiera3_function) do
9
19
  tokens.each do |tok|
10
20
  next unless tok.type == :NAME && HIERA3_FUNCS.include?(tok.value)
11
21
  notify :error,
12
- message: "'#{tok.value}()' is removed in Puppet 8 / OpenVox 8 — use lookup() instead",
22
+ message: "deprecated Hiera 3 function '#{tok.value}()' — use Hiera 5 lookup() instead",
13
23
  line: tok.line, column: tok.column
14
24
  end
15
25
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  # Legacy (unstructured) top-scope facts are removed in Puppet 8 / OpenVox 8.
4
6
  # Use structured facts via $facts hash instead.
5
7
  #
@@ -35,15 +37,57 @@ OpenvoxLint.new_check(:legacy_facts) do
35
37
  ].freeze
36
38
 
37
39
  def check
40
+ local_vars = collect_local_vars
38
41
  tokens.each do |tok|
39
42
  next unless tok.type == :VARIABLE
40
43
  # Strip leading $ and :: prefix, and any trailing : left by the
41
44
  # lexer when a variable is used as a resource title ($fact:).
42
45
  name = tok.value.sub(/^\$/, '').sub(/\A::/, '').chomp(':')
43
46
  next unless LEGACY_FACTS.include?(name)
47
+ next if local_vars.include?(name)
44
48
  notify :warning,
45
49
  message: "legacy fact '#{name}' — use $facts['...'] structured fact instead (Puppet 8 / OpenVox 8)",
46
50
  line: tok.line, column: tok.column
47
51
  end
48
52
  end
53
+
54
+ private
55
+
56
+ # Collect variable names that are declared locally (class/define parameters
57
+ # or lambda block parameters) so they are not mistaken for legacy facts.
58
+ def collect_local_vars
59
+ local = Set.new
60
+ toks = tokens
61
+ i = 0
62
+ while i < toks.size
63
+ case toks[i].type
64
+ when :CLASS, :DEFINE
65
+ # Skip to the opening LPAREN of the parameter list (stop at LBRACE)
66
+ j = i + 1
67
+ j += 1 while j < toks.size && toks[j].type != :LPAREN && toks[j].type != :LBRACE
68
+ if j < toks.size && toks[j].type == :LPAREN
69
+ depth = 1
70
+ j += 1
71
+ while j < toks.size && depth > 0
72
+ case toks[j].type
73
+ when :LPAREN then depth += 1
74
+ when :RPAREN then depth -= 1
75
+ when :VARIABLE then local << toks[j].value.sub(/^\$/, '').sub(/\A::/, '') if depth == 1
76
+ end
77
+ j += 1
78
+ end
79
+ end
80
+ when :PIPE
81
+ # Lambda parameter list: |$var1, $var2|
82
+ j = i + 1
83
+ while j < toks.size && toks[j].type != :PIPE
84
+ local << toks[j].value.sub(/^\$/, '').sub(/\A::/, '') if toks[j].type == :VARIABLE
85
+ j += 1
86
+ end
87
+ i = j # skip past closing PIPE
88
+ end
89
+ i += 1
90
+ end
91
+ local
92
+ end
49
93
  end
@@ -13,4 +13,25 @@ OpenvoxLint.new_check(:quoted_booleans) do
13
13
  line: tok.line, column: tok.column
14
14
  end
15
15
  end
16
+
17
+ # --fix support: unquote the boolean at the reported column (or fallback
18
+ # gsub). Assumes the problem points to the start of the quoted value.
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] == "'" || line[col] == '"'
24
+ quote = line[col]
25
+ # Find matching closing quote for this simple boolean token
26
+ end_col = line.index(quote, col + 1)
27
+ if end_col && %w[true false].include?(line[(col + 1)...end_col])
28
+ val = line[(col + 1)...end_col]
29
+ line[col..end_col] = val
30
+ @manifest_lines[idx] = line
31
+ return
32
+ end
33
+ end
34
+ # Fallback (safe for most manifests)
35
+ @manifest_lines[idx] = line.gsub(/(['"])(true|false)\1/, '\2')
36
+ end
16
37
  end
@@ -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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenvoxLint
4
- VERSION = '1.0.8'
4
+ VERSION = '1.3.0'
5
5
  end
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.8
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerald Sheets
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-04 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,7 +128,7 @@ 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
133
  post_install_message:
130
134
  rdoc_options: []
@@ -141,8 +145,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
145
  - !ruby/object:Gem::Version
142
146
  version: '0'
143
147
  requirements: []
144
- rubygems_version: 3.4.19
148
+ rubygems_version: 3.0.3.1
145
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