diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-01 18:38:49 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-01 18:38:49 +0200 |
| commit | 08129b43792eeec92eacb9f47ce3f879489aa03c (patch) | |
| tree | f527ba7c71c58536fa68b0acaef41dcd56a3cfc6 | |
| parent | 861e526d9ad9228f2b740dbcf00cf39d8fe652f2 (diff) | |
make rcm usable as a gem from any directory, fix bugs and tests
- Add lib/rcm.rb entry point and bin/rcm CLI executable
- Update gemspec: v0.1.0, proper files list, executables, runtime deps
- Support standalone arg parsing and --hosts filtering in options
- Fix inverted logic and typo in FileBackup#different? (== vs !=, cecksum_b)
- Fix unqualified File.directory? resolving to RCM::File in Directory
- Fix test_chown assertions running before evaluate! creates files
- Add setup to file tests to prevent order-dependent failures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| -rwxr-xr-x | bin/rcm | 18 | ||||
| -rw-r--r-- | lib/dslkeywords/file.rb | 4 | ||||
| -rw-r--r-- | lib/dslkeywords/given.rb | 4 | ||||
| -rw-r--r-- | lib/options.rb | 33 | ||||
| -rw-r--r-- | lib/rcm.rb | 1 | ||||
| -rw-r--r-- | rcm.gemspec | 8 | ||||
| -rw-r--r-- | test/lib/dslkeywords/file_test.rb | 6 | ||||
| -rw-r--r-- | test/lib/dslkeywords/mode_test.rb | 24 |
8 files changed, 74 insertions, 24 deletions
@@ -0,0 +1,18 @@ +#!/usr/bin/env ruby +# CLI entry point for RCM configuration management. +# Usage: rcm <config.rb> [--dry] [--debug] [--hosts host1,host2] + +if ARGV.empty? || ARGV.first.start_with?('-') + warn "Usage: rcm <config.rb> [--dry] [--debug] [--hosts host1,host2]" + exit 1 +end + +config_file = ARGV.shift + +unless File.exist?(config_file) + warn "Error: file not found: #{config_file}" + exit 1 +end + +require 'rcm' +load config_file diff --git a/lib/dslkeywords/file.rb b/lib/dslkeywords/file.rb index aa1b725..9a44f72 100644 --- a/lib/dslkeywords/file.rb +++ b/lib/dslkeywords/file.rb @@ -23,7 +23,7 @@ module RCM def different?(file_a, file_b) checksum_a = Digest::SHA256.file(file_a).hexdigest checksum_b = Digest::SHA256.file(file_b).hexdigest - [checksum_a == checksum_b, checksum_a, cecksum_b] + [checksum_a != checksum_b, checksum_a, checksum_b] end private @@ -320,7 +320,7 @@ module RCM end do? "Copying #{source_path} -> #{@file_path} resursively" do - if File.directory?(@file_path) + if ::File.directory?(@file_path) Dir["#{source_path}/*"].each { FileUtils.cp_r(_1, @file_path) } else FileUtils.cp_r(source_path, @file_path) diff --git a/lib/dslkeywords/given.rb b/lib/dslkeywords/given.rb index fa14e68..ba61703 100644 --- a/lib/dslkeywords/given.rb +++ b/lib/dslkeywords/given.rb @@ -17,6 +17,10 @@ module RCM def met? return false if @conds.key?(:hostname) && Socket.gethostname != @conds[:hostname].to_s + # When --hosts is specified, only run on the listed hosts + hosts = option(:hosts) + return false if hosts.any? && !hosts.include?(Socket.gethostname) + true end end diff --git a/lib/options.rb b/lib/options.rb index 401d135..6ce2479 100644 --- a/lib/options.rb +++ b/lib/options.rb @@ -1,16 +1,33 @@ require 'optparse' module RCM - # Command line options + # Command line options, supports both Rake mode (args after --) + # and standalone mode (direct args). Unknown options are ignored + # so that test runners and other tools can pass their own flags. module Options - @@options = { debug: false, dry: false } + @@options = { debug: false, dry: false, hosts: [] } - if (after_double_dash = ARGV.slice_before('--').to_a.last&.drop(1)) - OptionParser.new do |opts| - opts.banner = 'Usage: rake [task] -- [options]' - opts.on('-v', '--[no-]debug', 'debug output') { |v| @@options[:debug] = v } - opts.on('-d', '--dry', 'dry mode') { |v| @@options[:dry] = v } - end.parse!(after_double_dash) + parser = OptionParser.new do |opts| + opts.banner = 'Usage: rake [task] -- [options] OR ruby config.rb [options]' + opts.on('-v', '--[no-]debug', 'debug output') { |v| @@options[:debug] = v } + opts.on('-d', '--dry', 'dry mode') { |v| @@options[:dry] = v } + opts.on('--hosts HOSTS', 'comma-separated list of target hostnames') do |v| + @@options[:hosts] = v.split(',').map(&:strip) + end + end + + # Rake passes args after '--'; standalone scripts pass args directly. + args = if ARGV.include?('--') + ARGV.slice_before('--').to_a.last.drop(1) + else + ARGV.dup + end + + # Ignore unknown options (e.g. from test runners or other tools) + begin + parser.parse!(args) + rescue OptionParser::InvalidOption + retry end def option(key) diff --git a/lib/rcm.rb b/lib/rcm.rb new file mode 100644 index 0000000..641b02d --- /dev/null +++ b/lib/rcm.rb @@ -0,0 +1 @@ +require_relative 'dsl' diff --git a/rcm.gemspec b/rcm.gemspec index 0c771df..a24b5dd 100644 --- a/rcm.gemspec +++ b/rcm.gemspec @@ -1,13 +1,17 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 3.3.0' s.name = 'rcm' - s.version = '0.0.0' + s.version = '0.1.0' s.licenses = ['BSD3'] s.summary = "Ruby Configuration Management system" s.description = "To configure my stuff" s.authors = ["Paul Buetow"] s.email = 'rcm@dev.buetow.org' - s.files = ["lib/dsl.rb"] + s.files = Dir['lib/**/*.rb'] + s.executables = ['rcm'] s.homepage = 'https://codeberg.org/snonux/rcm' s.metadata = { "source_code_uri" => "https://codeberg.org/snonux/rcm" } + + s.add_runtime_dependency 'toml', '~> 0.3' + s.add_runtime_dependency 'erb' end diff --git a/test/lib/dslkeywords/file_test.rb b/test/lib/dslkeywords/file_test.rb index 35a0f21..a290627 100644 --- a/test/lib/dslkeywords/file_test.rb +++ b/test/lib/dslkeywords/file_test.rb @@ -12,6 +12,12 @@ class RCMFileTest < Minitest::Test FileUtils.rm_r(DIR_PATH) if File.directory?(DIR_PATH) end + # Clean up shared temp file between tests to prevent order-dependent failures + def setup + File.unlink(FILE_PATH) if File.file?(FILE_PATH) + FileUtils.rm_r(DIR_PATH) if File.directory?(DIR_PATH) + end + def test_create_file_from_string text = 'Hello World!' configure_from_scratch do diff --git a/test/lib/dslkeywords/mode_test.rb b/test/lib/dslkeywords/mode_test.rb index a4bf099..6e93bde 100644 --- a/test/lib/dslkeywords/mode_test.rb +++ b/test/lib/dslkeywords/mode_test.rb @@ -48,12 +48,12 @@ class RCMModeTest < Minitest::Test end def test_chown - configure_from_scratch do - # Well, test only makes sense that it doesn't throw any exception, as test - # can't change files to other owners as test will likely run as non-root. - user_name = Etc.getlogin - group_name = Etc.getgrgid(Process.gid).name + # Test only verifies no exception is thrown, as test runs as non-root + # and can't change files to other owners. + user_name = Etc.getlogin + group_name = Etc.getgrgid(Process.gid).name + configure_from_scratch do touch FILE1_PATH do owner user_name group group_name @@ -62,14 +62,14 @@ class RCMModeTest < Minitest::Test owner user_name group group_name end + end - stat = File.stat(FILE1_PATH) - assert_equal user_name, Etc.getpwuid(stat.uid) - assert_equal group_name, Etc.getgrgid(stat.gid) + stat = File.stat(FILE1_PATH) + assert_equal user_name, Etc.getpwuid(stat.uid).name + assert_equal group_name, Etc.getgrgid(stat.gid).name - stat = File.stat(DIR_PATH) - assert_equal user_name, Etc.getpwuid(stat.uid) - assert_equal group_name, Etc.getgrgid(stat.gid) - end + stat = File.stat(DIR_PATH) + assert_equal user_name, Etc.getpwuid(stat.uid).name + assert_equal group_name, Etc.getgrgid(stat.gid).name end end |
