Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 0.6.0 / 2026-03-11
* Add `.scelint` defaults file support (rspec-style CLI argument defaults)
* Add `--allow-reserved-words` option to skip reserved word checks on parameter names

### 0.5.0 / 2026-03-11
* Validate parameter names
* Fix command-line argument processing
Expand Down
5 changes: 4 additions & 1 deletion lib/scelint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ class Lint
#
# @param paths [Array<String>] Paths to look for SCE data in. Defaults to ['.']
# @param logger [Logger] A logger to send messages to. Defaults to an instance of Logger with the log level set to INFO.
def initialize(paths = ['.'], logger: Logger.new(STDOUT, level: Logger::INFO))
def initialize(paths = ['.'], logger: Logger.new(STDOUT, level: Logger::INFO), allow_reserved_words: false)
@log = logger
@allow_reserved_words = allow_reserved_words
@errors = []
@warnings = []
@notes = []
Expand Down Expand Up @@ -418,6 +419,8 @@ def check_parameter(file, check, parameter)
return
end

return if @allow_reserved_words

parameter.split('::').each do |part|
if reserved_words.include?(part)
errors << "#{file} (check '#{check}'): parameter name '#{parameter}' contains reserved word '#{part}'"
Expand Down
16 changes: 14 additions & 2 deletions lib/scelint/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,21 @@ def self.exit_on_failure?
true
end

DEFAULTS_FILE = '.scelint'

# Load default arguments from .scelint in the current directory.
# Each non-blank, non-comment line may contain one or more arguments.
def self.load_defaults
return [] unless File.exist?(DEFAULTS_FILE)
File.readlines(DEFAULTS_FILE, chomp: true)
.reject { |line| line.strip.empty? || line.strip.start_with?('#') }
.flat_map(&:split)
end

# When the first argument is not a known subcommand or an option flag,
# treat all arguments as paths for the default `lint` command.
def self.start(given_args = ARGV, config = {})
args = given_args
args = load_defaults + given_args
if args.first && !args.first.start_with?('-') && !all_commands.key?(args.first)
args = ['lint'] + args
end
Expand All @@ -26,9 +37,10 @@ def self.start(given_args = ARGV, config = {})

desc 'lint PATH', 'Lint all files in PATH'
option :strict, type: :boolean, aliases: '-s', default: false
option :allow_reserved_words, type: :boolean, default: false
def lint(*paths)
paths = ['.'] if paths.nil? || paths.empty?
lint = Scelint::Lint.new(paths, logger: logger)
lint = Scelint::Lint.new(paths, logger: logger, allow_reserved_words: options[:allow_reserved_words])

count = lint.files.count

Expand Down
2 changes: 1 addition & 1 deletion lib/scelint/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Scelint
VERSION = '0.5.0'
VERSION = '0.6.0'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
version: 2.0.0
ce:
12_ce1:
controls:
12_control1: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
version: 2.0.0
checks:
12_settings_check:
type: puppet-class-parameter
settings:
parameter: test_module_12::settings
value: true
ces:
- 12_ce1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
version: 2.0.0
profiles:
12_profile_test:
controls:
12_control1: true
84 changes: 84 additions & 0 deletions spec/unit/scelint/cli_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
let(:fixtures_dir) { File.expand_path('../../fixtures/modules', __dir__) }
let(:clean_module_path) { File.join(fixtures_dir, 'test_module_01') }
let(:warning_module_path) { File.join(fixtures_dir, 'test_module_04') }
let(:reserved_word_module_path) { File.join(fixtures_dir, 'test_module_12') }

# Runs the CLI via .start (as a user would invoke from the command line),
# captures the SystemExit status, and suppresses stdout noise from the logger.
Expand All @@ -19,6 +20,8 @@ def run_cli(args)
before(:each) do
# Silence logger output to keep spec output clean.
allow(Logger).to receive(:new).and_return(Logger.new(File::NULL))
# Prevent any real .scelint file in the working directory from affecting tests.
allow(described_class).to receive(:load_defaults).and_return([])
end

describe 'invocation without an explicit subcommand' do
Expand Down Expand Up @@ -85,4 +88,85 @@ def run_cli(args)
expect(run_cli(['--debug', clean_module_path])).to eq(0)
end
end

describe '.scelint defaults file' do
describe '.load_defaults' do
before(:each) do
allow(described_class).to receive(:load_defaults).and_call_original
end

it 'returns an empty array when no .scelint file exists' do
allow(File).to receive(:exist?).with(described_class::DEFAULTS_FILE).and_return(false)
expect(described_class.load_defaults).to eq([])
end

it 'returns args parsed from the file' do
allow(File).to receive(:exist?).with(described_class::DEFAULTS_FILE).and_return(true)
allow(File).to receive(:readlines).with(described_class::DEFAULTS_FILE, chomp: true)
.and_return(['--allow-reserved-words', '--strict'])
expect(described_class.load_defaults).to eq(['--allow-reserved-words', '--strict'])
end

it 'splits multiple args on a single line' do
allow(File).to receive(:exist?).with(described_class::DEFAULTS_FILE).and_return(true)
allow(File).to receive(:readlines).with(described_class::DEFAULTS_FILE, chomp: true)
.and_return(['--allow-reserved-words --strict'])
expect(described_class.load_defaults).to eq(['--allow-reserved-words', '--strict'])
end

it 'ignores blank lines' do
allow(File).to receive(:exist?).with(described_class::DEFAULTS_FILE).and_return(true)
allow(File).to receive(:readlines).with(described_class::DEFAULTS_FILE, chomp: true)
.and_return(['', '--strict', ''])
expect(described_class.load_defaults).to eq(['--strict'])
end

it 'ignores comment lines' do
allow(File).to receive(:exist?).with(described_class::DEFAULTS_FILE).and_return(true)
allow(File).to receive(:readlines).with(described_class::DEFAULTS_FILE, chomp: true)
.and_return(['# this is a comment', '--strict'])
expect(described_class.load_defaults).to eq(['--strict'])
end
end

context 'when .scelint contains --allow-reserved-words' do
before(:each) do
allow(described_class).to receive(:load_defaults).and_return(['--allow-reserved-words'])
end

it 'applies the flag without passing it on the command line' do
expect(run_cli([reserved_word_module_path])).to eq(0)
end

it 'can be overridden by --no-allow-reserved-words on the command line' do
expect(run_cli(['--no-allow-reserved-words', reserved_word_module_path])).to eq(1)
end
end

context 'when .scelint contains --strict' do
before(:each) do
allow(described_class).to receive(:load_defaults).and_return(['--strict'])
end

it 'applies --strict without passing it on the command line' do
expect(run_cli([warning_module_path])).to eq(1)
end
end
end

describe '--allow-reserved-words flag' do
context 'when a parameter name contains a reserved word' do
it 'exits 1 without --allow-reserved-words' do
expect(run_cli([reserved_word_module_path])).to eq(1)
end

it 'exits 0 with --allow-reserved-words' do
expect(run_cli(['--allow-reserved-words', reserved_word_module_path])).to eq(0)
end

it 'exits 0 with --allow-reserved-words and --strict' do
expect(run_cli(['--allow-reserved-words', '--strict', reserved_word_module_path])).to eq(0)
end
end
end
end
2 changes: 1 addition & 1 deletion spec/unit/scelint/lint_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Each test assumes 3 files, no errors, no warnings, no notes.
# Exceptions are listed below.
let(:lint_files) { { '04' => 37, '11' => 2 } }
let(:lint_errors) { {} }
let(:lint_errors) { { '12' => 2 } }
let(:lint_warnings) { { '04' => 17 } }
let(:lint_notes) { { '11' => 1 } }

Expand Down
Loading