Thank you for your interest in contributing to openvox-lint! This document provides guidelines for contributing code, documentation, and bug reports.
- Code of Conduct
- Getting Started
- Development Setup
- Running Tests
- Submitting Changes
- Writing Check Plugins
- Coding Standards
- Documentation
- Releasing
This project follows the Contributor Covenant. Be respectful, inclusive, and constructive in all interactions.
- Ruby ≥ 2.5.0
- Bundler ≥ 2.0
- Git
git clone https://github.com/cvquesty/openvox-lint.git
cd openvox-lintbundle installopenvox-lint/
├── bin/
│ └── openvox-lint # CLI entry point
├── lib/
│ ├── openvox-lint.rb # Main module
│ └── openvox-lint/
│ ├── version.rb # Version constant
│ ├── configuration.rb # Configuration management
│ ├── token.rb # Token data structure
│ ├── lexer.rb # Manifest lexer
│ ├── check_plugin.rb # Base check class
│ ├── checks.rb # Check runner
│ ├── report.rb # Output formatters
│ ├── linter.rb # File orchestrator
│ ├── cli.rb # CLI parser
│ └── plugins/
│ └── checks/ # Built-in check plugins
├── spec/ # RSpec tests
├── openvox-lint.gemspec
├── Gemfile
└── Rakefile
| Class | Purpose |
|---|---|
Lexer |
Tokenises Puppet/OpenVox manifests |
Token |
Represents a single lexer token |
CheckPlugin |
Base class for lint checks |
Checks |
Runs enabled checks against tokens |
Linter |
Orchestrates file discovery and linting |
Report |
Formats output in various formats |
Configuration |
Holds runtime configuration |
CLI |
Command-line interface |
bundle exec rake specbundle exec rspec spec/unit/lexer_spec.rb
bundle exec rspec spec/unit/checks_spec.rbbundle exec ruby -Ilib bin/openvox-lint spec/fixtures/bundle exec rubocopgit checkout -b feature/my-new-check- Write code following our coding standards
- Add tests for new functionality
- Update documentation as needed
bundle exec rake spec
bundle exec rubocopUse conventional commits:
git commit -m "feat: add new_check_name check for XYZ pattern"
git commit -m "fix: correct false positive in arrow_alignment"
git commit -m "docs: add examples for legacy_facts check"Commit types:
feat:— New featurefix:— Bug fixdocs:— Documentation onlytest:— Test onlyrefactor:— Code change that neither fixes a bug nor adds a featurechore:— Maintenance tasks
git push origin feature/my-new-checkOpen a pull request on GitHub with:
- Clear description of changes
- Link to related issue (if any)
- Test results
# lib/openvox-lint/plugins/checks/my_check.rb
# frozen_string_literal: true
# Description of what this check detects and why it matters.
OpenvoxLint.new_check(:my_check) do
def check
# Implement check logic
tokens.each do |tok|
if bad_pattern?(tok)
notify :warning,
message: 'description of problem',
line: tok.line,
column: tok.column
end
end
end
private
def bad_pattern?(tok)
# Detection logic
end
end| Method | Description |
|---|---|
tokens |
Array of all tokens |
manifest_lines |
Array of source lines |
semantic_tokens |
Tokens excluding whitespace/comments |
resource_indexes |
Array of resource body info |
class_indexes |
Array of class definition info |
defined_type_indexes |
Array of defined type info |
node_indexes |
Array of node definition info |
title_tokens |
Array of resource title tokens |
fullpath |
Full path to file being checked |
filename |
Base filename |
# Warning (style issue)
notify :warning,
message: 'human-readable description',
line: token.line,
column: token.column
# Error (will cause runtime failure)
notify :error,
message: 'critical problem description',
line: token.line,
column: token.columnCreate tests in spec/unit/checks_spec.rb:
describe ':my_check' do
it 'detects the bad pattern' do
problems = lint("bad_code_here", checks: %w[my_check])
expect(problems.size).to eq(1)
expect(problems.first[:check]).to eq(:my_check)
end
it 'passes good code' do
problems = lint("good_code_here", checks: %w[my_check])
expect(problems).to be_empty
end
end- Follow Ruby Style Guide
- Use frozen string literal pragma:
# frozen_string_literal: true - 2-space indentation
- Maximum line length: 100 characters
- Run
rubocopbefore committing
| Entity | Convention | Example |
|---|---|---|
| Check names | snake_case |
:legacy_facts |
| Class names | PascalCase |
CheckPlugin |
| Methods | snake_case |
resource_indexes |
| Constants | SCREAMING_SNAKE |
LEGACY_FACTS |
- Single Responsibility — Each check detects one type of issue
- Clear Messages — Problem messages should explain what's wrong
- Accurate Location — Report the exact line and column
- No False Positives — Prefer missing problems over wrong ones
- Documentation — Include a comment explaining the check's purpose
- Use conventional commit format
- First line: imperative mood, ≤ 72 characters
- Body: explain why, not just what
- Reference issues:
Fixes #123
# Brief description of what this check detects.
#
# More detailed explanation if needed, including:
# - Why this pattern is problematic
# - What the correct pattern looks like
# - Any exceptions or edge cases
OpenvoxLint.new_check(:example_check) do
# ...
endUpdate README.md when:
- Adding new checks (update check count and table)
- Adding CLI options
- Changing default behaviour
Update DOCUMENTATION.md when:
- Adding new checks (full documentation with examples)
- Changing API
- Adding new token types
Add an entry to CHANGELOG.md for every user-visible change:
## [Unreleased]
### Added
- New `my_check` check that detects XYZ pattern
### Fixed
- False positive in `existing_check` when ABC
### Changed
- `some_check` now also flags DEF patternWe follow Semantic Versioning:
- MAJOR — Breaking changes
- MINOR — New features, backwards compatible
- PATCH — Bug fixes, backwards compatible
- Update
lib/openvox-lint/version.rb - Update
CHANGELOG.mdwith release date - Update check count in README.md and DOCUMENTATION.md
- Commit:
git commit -m "chore: release v1.3.2" - Tag:
git tag v1.3.2 - Push:
git push origin development --tags - Build gem:
gem build openvox-lint.gemspec - Publish:
gem push openvox-lint-1.3.2.gem
- Issues: github.com/cvquesty/openvox-lint/issues
- Discussions: Open an issue for questions
By contributing, you agree that your contributions will be licensed under the Apache License 2.0.