openvox-lint 1.0.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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +52 -0
  3. data/DOCUMENTATION.md +481 -0
  4. data/LICENSE +83 -0
  5. data/README.md +497 -0
  6. data/bin/openvox-lint +7 -0
  7. data/lib/openvox-lint/check_plugin.rb +147 -0
  8. data/lib/openvox-lint/checks.rb +46 -0
  9. data/lib/openvox-lint/cli.rb +87 -0
  10. data/lib/openvox-lint/configuration.rb +59 -0
  11. data/lib/openvox-lint/lexer.rb +342 -0
  12. data/lib/openvox-lint/linter.rb +72 -0
  13. data/lib/openvox-lint/plugins/checks/arrow_alignment.rb +42 -0
  14. data/lib/openvox-lint/plugins/checks/autoloader_layout.rb +31 -0
  15. data/lib/openvox-lint/plugins/checks/case_without_default.rb +28 -0
  16. data/lib/openvox-lint/plugins/checks/class_inherits_params.rb +13 -0
  17. data/lib/openvox-lint/plugins/checks/documentation.rb +26 -0
  18. data/lib/openvox-lint/plugins/checks/double_quoted_strings.rb +19 -0
  19. data/lib/openvox-lint/plugins/checks/duplicate_params.rb +24 -0
  20. data/lib/openvox-lint/plugins/checks/ensure_first_param.rb +28 -0
  21. data/lib/openvox-lint/plugins/checks/ensure_not_symlink_target.rb +29 -0
  22. data/lib/openvox-lint/plugins/checks/file_mode.rb +33 -0
  23. data/lib/openvox-lint/plugins/checks/hard_tabs.rb +15 -0
  24. data/lib/openvox-lint/plugins/checks/hiera3_function.rb +16 -0
  25. data/lib/openvox-lint/plugins/checks/import_statement.rb +13 -0
  26. data/lib/openvox-lint/plugins/checks/inherits_across_namespaces.rb +27 -0
  27. data/lib/openvox-lint/plugins/checks/leading_zero.rb +22 -0
  28. data/lib/openvox-lint/plugins/checks/legacy_facts.rb +47 -0
  29. data/lib/openvox-lint/plugins/checks/line_length.rb +18 -0
  30. data/lib/openvox-lint/plugins/checks/nested_classes_or_defines.rb +26 -0
  31. data/lib/openvox-lint/plugins/checks/node_name_unquoted.rb +18 -0
  32. data/lib/openvox-lint/plugins/checks/only_variable_string.rb +18 -0
  33. data/lib/openvox-lint/plugins/checks/parameter_order.rb +25 -0
  34. data/lib/openvox-lint/plugins/checks/puppet_url_without_modules.rb +17 -0
  35. data/lib/openvox-lint/plugins/checks/quoted_booleans.rb +16 -0
  36. data/lib/openvox-lint/plugins/checks/relative_classname_inclusion.rb +24 -0
  37. data/lib/openvox-lint/plugins/checks/resource_reference_without_title_capital.rb +21 -0
  38. data/lib/openvox-lint/plugins/checks/selector_inside_resource.rb +15 -0
  39. data/lib/openvox-lint/plugins/checks/single_quote_string_with_variables.rb +16 -0
  40. data/lib/openvox-lint/plugins/checks/space_before_arrow.rb +20 -0
  41. data/lib/openvox-lint/plugins/checks/star_comments.rb +13 -0
  42. data/lib/openvox-lint/plugins/checks/strict_indent.rb +16 -0
  43. data/lib/openvox-lint/plugins/checks/top_scope_facts.rb +19 -0
  44. data/lib/openvox-lint/plugins/checks/trailing_comma.rb +24 -0
  45. data/lib/openvox-lint/plugins/checks/trailing_whitespace.rb +14 -0
  46. data/lib/openvox-lint/plugins/checks/unquoted_file_mode.rb +24 -0
  47. data/lib/openvox-lint/plugins/checks/unquoted_resource_title.rb +13 -0
  48. data/lib/openvox-lint/plugins/checks/variable_contains_dash.rb +15 -0
  49. data/lib/openvox-lint/plugins/checks/variable_is_lowercase.rb +16 -0
  50. data/lib/openvox-lint/plugins/checks/variables_not_enclosed.rb +19 -0
  51. data/lib/openvox-lint/report.rb +86 -0
  52. data/lib/openvox-lint/token.rb +38 -0
  53. data/lib/openvox-lint/version.rb +5 -0
  54. data/lib/openvox-lint.rb +47 -0
  55. metadata +145 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fd99ac3fc7637de4bda009ad83ca4b89fbf861316db186f039a8b0405c062cbc
4
+ data.tar.gz: 4747a1300a899ede28ddaea30a3f5488ae7ee5e294cad55ad1f4721fc55110fa
5
+ SHA512:
6
+ metadata.gz: e260e0c5a26c9da084e03b835da830981d79fc6af3e32f5fd6644e80da03c4e01d095e4a6badeb581ff8e47467d21802efe6ad3ab1197be0c8046154c6cf37e8
7
+ data.tar.gz: 6ef7e4f2244b3247012fc5e446505f1d373418e9c226c3c62e003aae26ef3d970f3a84cd63b1d66237a6176a3af85a3a41b9c8368036e2e34f8ff8d220404c9c
data/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to openvox-lint will be documented in this file.
4
+
5
+ ## [1.0.0] - 2025-02-09
6
+
7
+ ### Added
8
+
9
+ - **Core Engine**
10
+ - Complete Puppet / OpenVox manifest lexer/tokenizer
11
+ - Token-based check plugin architecture with `OpenvoxLint.new_check` DSL
12
+ - Doubly-linked token list for efficient navigation
13
+ - Support for all Puppet 8 / OpenVox 8.x language constructs
14
+ - Heredoc, EPP tag, regex, and string interpolation tokenization
15
+
16
+ - **38 Built-in Checks**
17
+ - **Whitespace**: `trailing_whitespace`, `hard_tabs`, `line_length`, `space_before_arrow`, `strict_indent`
18
+ - **Alignment**: `arrow_alignment`
19
+ - **Strings**: `double_quoted_strings`, `only_variable_string`, `single_quote_string_with_variables`, `variables_not_enclosed`, `quoted_booleans`
20
+ - **Variables**: `variable_is_lowercase`, `variable_contains_dash`
21
+ - **Resources**: `ensure_first_param`, `ensure_not_symlink_target`, `file_mode`, `unquoted_file_mode`, `unquoted_resource_title`, `duplicate_params`, `trailing_comma`
22
+ - **Classes**: `documentation`, `nested_classes_or_defines`, `parameter_order`, `class_inherits_params`, `inherits_across_namespaces`
23
+ - **Conditionals**: `case_without_default`, `selector_inside_resource`
24
+ - **References**: `leading_zero`, `resource_reference_without_title_capital`, `relative_classname_inclusion`, `autoloader_layout`
25
+ - **Comments**: `star_comments`
26
+ - **URLs**: `puppet_url_without_modules`
27
+ - **Nodes**: `node_name_unquoted`
28
+ - **Puppet 8 / OpenVox 8** ⭐: `legacy_facts`, `top_scope_facts`, `hiera3_function`, `import_statement`
29
+
30
+ - **Output Formats**
31
+ - Text (default), JSON, CSV, GitHub Actions, Code Climate, Custom
32
+
33
+ - **CLI Features**
34
+ - `--fix` auto-fix support
35
+ - `--fail-on-warnings` for strict CI
36
+ - `--only-checks` and `--no-<check>-check` granular control
37
+ - `--list-checks` to enumerate all checks
38
+ - `.openvox-lint.rc` configuration file support
39
+ - `# lint:ignore:check_name` inline suppression
40
+
41
+ - **Integration**
42
+ - vim-openvox plugin compatibility
43
+ - GitHub Actions workflow support
44
+ - Rake task integration
45
+ - Pre-commit hook support
46
+
47
+ ### Compatibility
48
+
49
+ - OpenVox 8.x (community fork of Puppet)
50
+ - Puppet 8.x
51
+ - Puppet 7.x (with deprecation warnings)
52
+ - Ruby ≥ 3.1.0
data/DOCUMENTATION.md ADDED
@@ -0,0 +1,481 @@
1
+ # openvox-lint Technical Documentation
2
+
3
+ ## Overview
4
+
5
+ openvox-lint is a Ruby gem that statically analyses OpenVox / Puppet manifest
6
+ (`.pp`) files. It tokenises each file with a purpose-built lexer, then runs
7
+ a configurable set of check plugins against the token stream. Problems are
8
+ reported in multiple output formats suitable for humans, CI systems, and IDEs.
9
+
10
+ This document covers the architecture, every public API, every built-in check,
11
+ the lexer token types, the plugin system, and integration guidance.
12
+
13
+ ---
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ ┌─────────────┐ ┌───────┐ ┌────────┐ ┌──────────┐
19
+ │ CLI / API │────▶│ Linter │────▶│ Lexer │────▶│ Tokens │
20
+ │ │ │ │ │ │ │ (array) │
21
+ └─────────────┘ └───┬────┘ └────────┘ └────┬─────┘
22
+ │ │
23
+ │ ┌────────┐ │
24
+ └────────▶│ Checks │◀──────────┘
25
+ │ │
26
+ └───┬────┘
27
+
28
+ ┌───▼────┐
29
+ │ Report │
30
+ └────────┘
31
+ ```
32
+
33
+ ### Pipeline
34
+
35
+ 1. **CLI** parses command-line arguments and loads configuration
36
+ 2. **Linter** expands file arguments, reads each `.pp` file
37
+ 3. **Lexer** tokenises the manifest into `Token` objects
38
+ 4. **Checks** runs each enabled check plugin against the token stream
39
+ 5. **Report** formats and outputs the collected problems
40
+
41
+ ---
42
+
43
+ ## Module: OpenvoxLint
44
+
45
+ ### Constants
46
+
47
+ | Constant | Value | Description |
48
+ |----------|-------|-------------|
49
+ | `VERSION` | `'1.0.0'` | Gem version |
50
+
51
+ ### Class Methods
52
+
53
+ | Method | Returns | Description |
54
+ |--------|---------|-------------|
55
+ | `.configuration` | `Configuration` | Global configuration singleton |
56
+ | `.configure { \|c\| }` | `Configuration` | Yields configuration for block-style setup |
57
+ | `.checks` | `Hash{Symbol => Class}` | Registry of loaded check classes |
58
+ | `.new_check(name, &block)` | `Class` | Register a new check plugin |
59
+
60
+ ### Exceptions
61
+
62
+ | Exception | Inherits | Usage |
63
+ |-----------|----------|-------|
64
+ | `OpenvoxLint::Error` | `StandardError` | General errors |
65
+ | `OpenvoxLint::NoFix` | `StandardError` | Raised to skip fixing a problem |
66
+
67
+ ---
68
+
69
+ ## Class: OpenvoxLint::Token
70
+
71
+ Represents a single token from the lexer.
72
+
73
+ ### Attributes
74
+
75
+ | Attribute | Type | Description |
76
+ |-----------|------|-------------|
77
+ | `type` | `Symbol` | Token type (see Token Types below) |
78
+ | `value` | `String` | Raw text value |
79
+ | `line` | `Integer` | 1-based line number |
80
+ | `column` | `Integer` | 1-based column number |
81
+ | `prev_token` | `Token\|nil` | Previous token in doubly-linked list |
82
+ | `next_token` | `Token\|nil` | Next token in doubly-linked list |
83
+
84
+ ### Methods
85
+
86
+ | Method | Returns | Description |
87
+ |--------|---------|-------------|
88
+ | `#formatting?` | `Boolean` | True if whitespace/comment/indent/newline |
89
+ | `#to_s` | `String` | Human-readable representation |
90
+
91
+ ### Token Types
92
+
93
+ #### Keywords
94
+
95
+ | Type | Puppet Keyword |
96
+ |------|---------------|
97
+ | `:AND` | `and` |
98
+ | `:APPLICATION` | `application` |
99
+ | `:ATTR` | `attr` |
100
+ | `:CASE` | `case` |
101
+ | `:CLASS` | `class` |
102
+ | `:CONSUMES` | `consumes` |
103
+ | `:DEFAULT` | `default` |
104
+ | `:DEFINE` | `define` |
105
+ | `:ELSE` | `else` |
106
+ | `:ELSIF` | `elsif` |
107
+ | `:FALSE` | `false` |
108
+ | `:FUNCTION` | `function` |
109
+ | `:IF` | `if` |
110
+ | `:IMPORT` | `import` |
111
+ | `:IN` | `in` |
112
+ | `:INHERITS` | `inherits` |
113
+ | `:NODE` | `node` |
114
+ | `:NOT` | `not` |
115
+ | `:OR` | `or` |
116
+ | `:PRIVATE` | `private` |
117
+ | `:PRODUCES` | `produces` |
118
+ | `:SITE` | `site` |
119
+ | `:TRUE` | `true` |
120
+ | `:TYPE` | `type` |
121
+ | `:UNDEF` | `undef` |
122
+ | `:UNLESS` | `unless` |
123
+
124
+ #### Identifiers & Literals
125
+
126
+ | Type | Description | Example |
127
+ |------|-------------|---------|
128
+ | `:NAME` | Identifier / bare word | `ensure`, `myclass` |
129
+ | `:CLASSREF` | Capitalised reference | `File`, `String`, `Stdlib::Absolutepath` |
130
+ | `:VARIABLE` | Variable | `$foo`, `$::bar::baz` |
131
+ | `:NUMBER` | Numeric literal | `42`, `0xFF`, `3.14` |
132
+ | `:SSTRING` | Single-quoted string | `'hello'` |
133
+ | `:STRING` | Double-quoted string (no interpolation) | `"hello"` |
134
+ | `:DQSTRING` | Double-quoted string (with interpolation) | `"hello ${name}"` |
135
+ | `:REGEX` | Regular expression | `/^foo/` |
136
+ | `:HEREDOC_OPEN` | Heredoc opening tag | `@("END")` |
137
+ | `:HEREDOC` | Heredoc body | content |
138
+
139
+ #### Operators
140
+
141
+ | Type | Operator | Type | Operator |
142
+ |------|----------|------|----------|
143
+ | `:FARROW` | `=>` | `:PARROW` | `+>` |
144
+ | `:ISEQUAL` | `==` | `:NOTEQUAL` | `!=` |
145
+ | `:MATCH` | `=~` | `:NOMATCH` | `!~` |
146
+ | `:LESSEQUAL` | `<=` | `:GREATEREQUAL` | `>=` |
147
+ | `:LESSTHAN` | `<` | `:GREATERTHAN` | `>` |
148
+ | `:LSHIFT` | `<<` | `:RSHIFT` | `>>` |
149
+ | `:IN_EDGE` | `->` | `:OUT_EDGE` | `<-` |
150
+ | `:IN_EDGE_SUB` | `~>` | `:OUT_EDGE_SUB` | `<~` |
151
+ | `:APPENDS` | `+=` | `:EQUALS` | `=` |
152
+ | `:LCOLLECT` | `<\|` | `:RCOLLECT` | `\|>` |
153
+ | `:LLCOLLECT` | `<<\|` | `:RRCOLLECT` | `\|>>` |
154
+
155
+ #### Punctuation
156
+
157
+ | Type | Character | Type | Character |
158
+ |------|-----------|------|-----------|
159
+ | `:LBRACE` | `{` | `:RBRACE` | `}` |
160
+ | `:LPAREN` | `(` | `:RPAREN` | `)` |
161
+ | `:LBRACK` | `[` | `:RBRACK` | `]` |
162
+ | `:COMMA` | `,` | `:SEMIC` | `;` |
163
+ | `:DOT` | `.` | `:COLON` | `:` |
164
+ | `:PIPE` | `\|` | `:AT` | `@` |
165
+ | `:QMARK` | `?` | `:BACKSLASH` | `\\` |
166
+ | `:PLUS` | `+` | `:MINUS` | `-` |
167
+ | `:TIMES` | `*` | `:MODULO` | `%` |
168
+ | `:DIV` | `/` | `:NOT` | `!` |
169
+
170
+ #### Formatting
171
+
172
+ | Type | Description |
173
+ |------|-------------|
174
+ | `:WHITESPACE` | Spaces/tabs (not at line start) |
175
+ | `:INDENT` | Spaces/tabs at line start |
176
+ | `:NEWLINE` | Line break |
177
+ | `:COMMENT` | `#` comment |
178
+ | `:MLCOMMENT` | `/* */` comment |
179
+ | `:SLASH_COMMENT` | `//` comment |
180
+
181
+ ---
182
+
183
+ ## Class: OpenvoxLint::Lexer
184
+
185
+ ### Constructor
186
+
187
+ ```ruby
188
+ lexer = OpenvoxLint::Lexer.new(code_string)
189
+ ```
190
+
191
+ ### Attributes
192
+
193
+ | Attribute | Type | Description |
194
+ |-----------|------|-------------|
195
+ | `tokens` | `Array<Token>` | All tokens (doubly-linked) |
196
+ | `manifest_lines` | `Array<String>` | Source lines (for line-based checks) |
197
+
198
+ ---
199
+
200
+ ## Class: OpenvoxLint::CheckPlugin
201
+
202
+ Base class for all checks. Created via `OpenvoxLint.new_check`.
203
+
204
+ ### Subclass Interface
205
+
206
+ | Method | Required | Description |
207
+ |--------|----------|-------------|
208
+ | `#check` | **Yes** | Main check logic; call `notify` to report |
209
+ | `#fix(problem)` | No | Auto-fix a problem; raise `NoFix` to skip |
210
+
211
+ ### Helper Methods Available in Checks
212
+
213
+ | Method | Returns | Description |
214
+ |--------|---------|-------------|
215
+ | `tokens` | `Array<Token>` | Full token stream |
216
+ | `manifest_lines` | `Array<String>` | Source lines |
217
+ | `semantic_tokens` | `Array<Token>` | Non-formatting tokens only |
218
+ | `resource_indexes` | `Array<Hash>` | Resource body locations |
219
+ | `class_indexes` | `Array<Hash>` | Class definition locations |
220
+ | `defined_type_indexes` | `Array<Hash>` | Defined type locations |
221
+ | `node_indexes` | `Array<Hash>` | Node definition locations |
222
+ | `title_tokens` | `Array<Token>` | Resource title tokens |
223
+ | `fullpath` | `String` | Full file path |
224
+ | `filename` | `String` | Base filename |
225
+ | `notify(kind, details)` | — | Report a problem |
226
+
227
+ ### `notify` Parameters
228
+
229
+ ```ruby
230
+ notify :warning, # or :error
231
+ message: 'description of problem',
232
+ line: 42,
233
+ column: 5
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Class: OpenvoxLint::Configuration
239
+
240
+ ### Attributes
241
+
242
+ | Attribute | Type | Default | Description |
243
+ |-----------|------|---------|-------------|
244
+ | `log_format` | `String` | `'text'` | Output format |
245
+ | `with_filename` | `Boolean` | `true` | Show filename in output |
246
+ | `fail_on_warnings` | `Boolean` | `false` | Exit 1 on warnings |
247
+ | `fix` | `Boolean` | `false` | Auto-fix mode |
248
+ | `only_checks` | `Array<Symbol>` | `[]` | Run only these checks |
249
+ | `disabled_checks` | `Array<Symbol>` | `[]` | Skip these checks |
250
+ | `ignore_paths` | `Array<String>` | vendor, pkg, spec | Glob patterns to ignore |
251
+ | `config_file` | `String` | `.openvox-lint.rc` | RC file path |
252
+ | `relative` | `Boolean` | `false` | Use relative paths |
253
+ | `column` | `Boolean` | `true` | Show column numbers |
254
+ | `custom_log_format` | `String\|nil` | `nil` | Custom format string |
255
+
256
+ ### Methods
257
+
258
+ | Method | Description |
259
+ |--------|-------------|
260
+ | `#load_from_rc(path)` | Load config from an RC file |
261
+ | `#check_enabled?(name)` | Is a check enabled? |
262
+
263
+ ---
264
+
265
+ ## Class: OpenvoxLint::Linter
266
+
267
+ ### Usage
268
+
269
+ ```ruby
270
+ linter = OpenvoxLint::Linter.new
271
+ linter.run('manifests/')
272
+ linter.problems # => Array of problem hashes
273
+ linter.errors? # => true/false
274
+ linter.exit_code # => 0 or 1
275
+ ```
276
+
277
+ ### Methods
278
+
279
+ | Method | Returns | Description |
280
+ |--------|---------|-------------|
281
+ | `#run(*paths)` | — | Lint files/directories |
282
+ | `#problems` | `Array<Hash>` | All detected problems |
283
+ | `#file_count` | `Integer` | Number of files checked |
284
+ | `#errors?` | `Boolean` | Any errors found? |
285
+ | `#warnings?` | `Boolean` | Any warnings found? |
286
+ | `#exit_code` | `Integer` | 0=clean, 1=problems |
287
+
288
+ ---
289
+
290
+ ## Class: OpenvoxLint::Report
291
+
292
+ ### Usage
293
+
294
+ ```ruby
295
+ report = OpenvoxLint::Report.new(config)
296
+ report.format(problems) # to $stdout
297
+ report.format(problems, io: file) # to file
298
+ ```
299
+
300
+ ### Supported Formats
301
+
302
+ | Format | Flag | Description |
303
+ |--------|------|-------------|
304
+ | `text` | `-f text` (default) | `path:line:col: KIND: check: message` |
305
+ | `json` | `-f json` | JSON array |
306
+ | `csv` | `-f csv` | CSV with headers |
307
+ | `github` | `-f github` | GitHub Actions annotations |
308
+ | `codeclimate` | `-f codeclimate` | Code Climate JSON |
309
+ | `custom` | `--log-format` | User-defined format string |
310
+
311
+ ---
312
+
313
+ ## Complete Check Reference
314
+
315
+ ### Puppet 8 / OpenVox 8 Migration Checks
316
+
317
+ These are the most important checks for users upgrading from Puppet 7 or
318
+ migrating to OpenVox.
319
+
320
+ #### `legacy_facts` (WARNING)
321
+
322
+ Legacy (unstructured) top-scope facts are excluded by default in Puppet 8 /
323
+ OpenVox 8. Variables like `$osfamily`, `$fqdn`, `$ipaddress`, and
324
+ `$operatingsystem` must be replaced with structured facts.
325
+
326
+ **Bad:**
327
+ ```puppet
328
+ if $osfamily == 'RedHat' { }
329
+ ```
330
+
331
+ **Good:**
332
+ ```puppet
333
+ if $facts['os']['family'] == 'RedHat' { }
334
+ ```
335
+
336
+ Covers 80+ legacy fact names.
337
+
338
+ #### `top_scope_facts` (WARNING)
339
+
340
+ Top-scope fact variables (`$::hostname`) should use the `$facts` hash.
341
+
342
+ **Bad:**
343
+ ```puppet
344
+ $hostname = $::hostname
345
+ ```
346
+
347
+ **Good:**
348
+ ```puppet
349
+ $hostname = $facts['networking']['hostname']
350
+ ```
351
+
352
+ #### `hiera3_function` (ERROR)
353
+
354
+ Hiera 3 functions are removed in Puppet 8. This is an error, not a warning.
355
+
356
+ **Bad:**
357
+ ```puppet
358
+ $val = hiera('mykey')
359
+ $hash = hiera_hash('myhash')
360
+ ```
361
+
362
+ **Good:**
363
+ ```puppet
364
+ $val = lookup('mykey')
365
+ $hash = lookup('myhash', Hash, 'hash')
366
+ ```
367
+
368
+ #### `import_statement` (ERROR)
369
+
370
+ The `import` keyword was removed in Puppet 4.
371
+
372
+ **Bad:**
373
+ ```puppet
374
+ import 'foo'
375
+ ```
376
+
377
+ **Good:**
378
+ Use module autoloading.
379
+
380
+ ---
381
+
382
+ ## Puppet 8 / OpenVox 8 Language Context
383
+
384
+ openvox-lint is designed with full awareness of the Puppet 8 / OpenVox 8
385
+ language changes:
386
+
387
+ ### Changes from Puppet 7
388
+
389
+ | Change | Impact | openvox-lint Check |
390
+ |--------|--------|-------------------|
391
+ | Strict mode enabled by default | Undefined vars → errors | (runtime) |
392
+ | Legacy facts excluded | `$osfamily` etc. unavailable | `legacy_facts` |
393
+ | Top-scope facts deprecated | `$::fact` pattern obsolete | `top_scope_facts` |
394
+ | Hiera 3 removed | `hiera()` functions gone | `hiera3_function` |
395
+ | PSON removed | Binary serialization changed | (runtime) |
396
+ | String literals frozen | Immutable strings | (runtime) |
397
+ | Ruby 3.2 required | API changes | (gemspec) |
398
+ | `Deferred` lazy evaluation | Resource ordering matters | (runtime) |
399
+ | `Sensitive` auto-protection | Deferred functions protected | (runtime) |
400
+
401
+ ### OpenVox Compatibility
402
+
403
+ OpenVox 8.x is a **fully compatible fork** of Puppet 8.x:
404
+ - **Identical language syntax** — no changes to the Puppet DSL
405
+ - **Identical module compatibility** — all Puppet Forge modules work
406
+ - **Different package names** — `openvox-agent` replaces `puppet-agent`
407
+ - **Same configuration paths** — `/etc/puppetlabs/`
408
+ - **Community maintained** by Vox Pupuli
409
+
410
+ openvox-lint works identically with both OpenVox and Puppet manifests.
411
+
412
+ ---
413
+
414
+ ## Exit Codes
415
+
416
+ | Code | Meaning |
417
+ |------|---------|
418
+ | `0` | No errors (warnings allowed unless `--fail-on-warnings`) |
419
+ | `1` | Errors found, or warnings with `--fail-on-warnings` |
420
+
421
+ ---
422
+
423
+ ## Plugin Development
424
+
425
+ ### Creating a Check Plugin
426
+
427
+ ```ruby
428
+ # my_check.rb
429
+ OpenvoxLint.new_check(:my_check) do
430
+ def check
431
+ tokens.each do |tok|
432
+ if tok.type == :NAME && tok.value == 'bad_thing'
433
+ notify :warning,
434
+ message: 'found bad_thing',
435
+ line: tok.line,
436
+ column: tok.column
437
+ end
438
+ end
439
+ end
440
+
441
+ # Optional: auto-fix
442
+ def fix(problem)
443
+ # Modify tokens in-place
444
+ raise OpenvoxLint::NoFix # if can't fix
445
+ end
446
+ end
447
+ ```
448
+
449
+ ### Distributing as a Gem
450
+
451
+ ```ruby
452
+ # my-openvox-lint-check.gemspec
453
+ Gem::Specification.new do |s|
454
+ s.name = 'openvox-lint-my_check'
455
+ s.add_runtime_dependency 'openvox-lint', '~> 1.0'
456
+ end
457
+ ```
458
+
459
+ Place the check file in `lib/openvox-lint/plugins/checks/my_check.rb`.
460
+
461
+ ---
462
+
463
+ ## File Inventory
464
+
465
+ | File | Lines | Description |
466
+ |------|-------|-------------|
467
+ | `bin/openvox-lint` | 7 | CLI entry point |
468
+ | `lib/openvox-lint.rb` | 47 | Main module, auto-loader |
469
+ | `lib/openvox-lint/version.rb` | 5 | Version constant |
470
+ | `lib/openvox-lint/configuration.rb` | 59 | Configuration management |
471
+ | `lib/openvox-lint/token.rb` | 38 | Token data structure |
472
+ | `lib/openvox-lint/lexer.rb` | 342 | Puppet/OpenVox lexer |
473
+ | `lib/openvox-lint/check_plugin.rb` | 147 | Base check class |
474
+ | `lib/openvox-lint/checks.rb` | 46 | Check runner |
475
+ | `lib/openvox-lint/report.rb` | 86 | Output formatters |
476
+ | `lib/openvox-lint/linter.rb` | 72 | File orchestrator |
477
+ | `lib/openvox-lint/cli.rb` | 87 | CLI parser |
478
+ | `lib/openvox-lint/plugins/checks/*.rb` | 38 files | Check plugins |
479
+ | `spec/spec_helper.rb` | 41 | Test helper |
480
+ | `spec/unit/lexer_spec.rb` | 85 | Lexer tests |
481
+ | `spec/unit/checks_spec.rb` | 142 | Check tests |
data/LICENSE ADDED
@@ -0,0 +1,83 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "You" (or "Your") shall mean an individual or Legal Entity
16
+ exercising permissions granted by this License.
17
+
18
+ "Source" form shall mean the preferred form for making modifications.
19
+
20
+ "Object" form shall mean any form resulting from mechanical
21
+ transformation or translation of a Source form.
22
+
23
+ "Work" shall mean the work of authorship made available under
24
+ the License.
25
+
26
+ "Contribution" shall mean any work of authorship submitted to the
27
+ Licensor for inclusion in the Work.
28
+
29
+ "Contributor" shall mean Licensor and any individual or Legal Entity
30
+ on behalf of whom a Contribution has been received by the Licensor.
31
+
32
+ 2. Grant of Copyright License.
33
+ Subject to the terms of this License, each Contributor grants to You
34
+ a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
35
+ irrevocable copyright license to reproduce, prepare Derivative Works
36
+ of, publicly display, publicly perform, sublicense, and distribute
37
+ the Work and such Derivative Works in Source or Object form.
38
+
39
+ 3. Grant of Patent License.
40
+ Subject to the terms of this License, each Contributor grants to You
41
+ a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
42
+ irrevocable patent license to make, have made, use, offer to sell,
43
+ sell, import, and otherwise transfer the Work.
44
+
45
+ 4. Redistribution.
46
+ You may reproduce and distribute copies of the Work or Derivative
47
+ Works thereof in any medium, provided that You meet the conditions
48
+ of this License.
49
+
50
+ 5. Submission of Contributions.
51
+ Unless You explicitly state otherwise, any Contribution submitted
52
+ for inclusion in the Work shall be under the terms of this License.
53
+
54
+ 6. Trademarks.
55
+ This License does not grant permission to use the trade names,
56
+ trademarks, service marks, or product names of the Licensor.
57
+
58
+ 7. Disclaimer of Warranty.
59
+ The Work is provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR
60
+ CONDITIONS OF ANY KIND.
61
+
62
+ 8. Limitation of Liability.
63
+ In no event shall any Contributor be liable to You for damages.
64
+
65
+ 9. Accepting Warranty or Additional Liability.
66
+ You may choose to offer acceptance of support, warranty, indemnity,
67
+ or other liability obligations consistent with this License.
68
+
69
+ END OF TERMS AND CONDITIONS
70
+
71
+ Copyright 2025 Johnny Sheets
72
+
73
+ Licensed under the Apache License, Version 2.0 (the "License");
74
+ you may not use this file except in compliance with the License.
75
+ You may obtain a copy of the License at
76
+
77
+ http://www.apache.org/licenses/LICENSE-2.0
78
+
79
+ Unless required by applicable law or agreed to in writing, software
80
+ distributed under the License is distributed on an "AS IS" BASIS,
81
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
82
+ See the License for the specific language governing permissions and
83
+ limitations under the License.