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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +52 -0
- data/DOCUMENTATION.md +481 -0
- data/LICENSE +83 -0
- data/README.md +497 -0
- data/bin/openvox-lint +7 -0
- data/lib/openvox-lint/check_plugin.rb +147 -0
- data/lib/openvox-lint/checks.rb +46 -0
- data/lib/openvox-lint/cli.rb +87 -0
- data/lib/openvox-lint/configuration.rb +59 -0
- data/lib/openvox-lint/lexer.rb +342 -0
- data/lib/openvox-lint/linter.rb +72 -0
- data/lib/openvox-lint/plugins/checks/arrow_alignment.rb +42 -0
- data/lib/openvox-lint/plugins/checks/autoloader_layout.rb +31 -0
- data/lib/openvox-lint/plugins/checks/case_without_default.rb +28 -0
- data/lib/openvox-lint/plugins/checks/class_inherits_params.rb +13 -0
- data/lib/openvox-lint/plugins/checks/documentation.rb +26 -0
- data/lib/openvox-lint/plugins/checks/double_quoted_strings.rb +19 -0
- data/lib/openvox-lint/plugins/checks/duplicate_params.rb +24 -0
- data/lib/openvox-lint/plugins/checks/ensure_first_param.rb +28 -0
- data/lib/openvox-lint/plugins/checks/ensure_not_symlink_target.rb +29 -0
- data/lib/openvox-lint/plugins/checks/file_mode.rb +33 -0
- data/lib/openvox-lint/plugins/checks/hard_tabs.rb +15 -0
- data/lib/openvox-lint/plugins/checks/hiera3_function.rb +16 -0
- data/lib/openvox-lint/plugins/checks/import_statement.rb +13 -0
- data/lib/openvox-lint/plugins/checks/inherits_across_namespaces.rb +27 -0
- data/lib/openvox-lint/plugins/checks/leading_zero.rb +22 -0
- data/lib/openvox-lint/plugins/checks/legacy_facts.rb +47 -0
- data/lib/openvox-lint/plugins/checks/line_length.rb +18 -0
- data/lib/openvox-lint/plugins/checks/nested_classes_or_defines.rb +26 -0
- data/lib/openvox-lint/plugins/checks/node_name_unquoted.rb +18 -0
- data/lib/openvox-lint/plugins/checks/only_variable_string.rb +18 -0
- data/lib/openvox-lint/plugins/checks/parameter_order.rb +25 -0
- data/lib/openvox-lint/plugins/checks/puppet_url_without_modules.rb +17 -0
- data/lib/openvox-lint/plugins/checks/quoted_booleans.rb +16 -0
- data/lib/openvox-lint/plugins/checks/relative_classname_inclusion.rb +24 -0
- data/lib/openvox-lint/plugins/checks/resource_reference_without_title_capital.rb +21 -0
- data/lib/openvox-lint/plugins/checks/selector_inside_resource.rb +15 -0
- data/lib/openvox-lint/plugins/checks/single_quote_string_with_variables.rb +16 -0
- data/lib/openvox-lint/plugins/checks/space_before_arrow.rb +20 -0
- data/lib/openvox-lint/plugins/checks/star_comments.rb +13 -0
- data/lib/openvox-lint/plugins/checks/strict_indent.rb +16 -0
- data/lib/openvox-lint/plugins/checks/top_scope_facts.rb +19 -0
- data/lib/openvox-lint/plugins/checks/trailing_comma.rb +24 -0
- data/lib/openvox-lint/plugins/checks/trailing_whitespace.rb +14 -0
- data/lib/openvox-lint/plugins/checks/unquoted_file_mode.rb +24 -0
- data/lib/openvox-lint/plugins/checks/unquoted_resource_title.rb +13 -0
- data/lib/openvox-lint/plugins/checks/variable_contains_dash.rb +15 -0
- data/lib/openvox-lint/plugins/checks/variable_is_lowercase.rb +16 -0
- data/lib/openvox-lint/plugins/checks/variables_not_enclosed.rb +19 -0
- data/lib/openvox-lint/report.rb +86 -0
- data/lib/openvox-lint/token.rb +38 -0
- data/lib/openvox-lint/version.rb +5 -0
- data/lib/openvox-lint.rb +47 -0
- 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.
|