wheneverd 0.2.0 → 0.3.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.
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Wheneverd
6
+ module Systemd
7
+ # Thin wrapper around `systemd-analyze`.
8
+ class Analyze
9
+ DEFAULT_SYSTEMD_ANALYZE = "systemd-analyze"
10
+
11
+ # Run `systemd-analyze calendar <value>`.
12
+ #
13
+ # @param value [String] an `OnCalendar=` value
14
+ # @param systemd_analyze [String] path to the `systemd-analyze` executable
15
+ # @return [Array(String, String)] stdout and stderr
16
+ # @raise [Wheneverd::Systemd::SystemdAnalyzeError]
17
+ def self.calendar(value, systemd_analyze: DEFAULT_SYSTEMD_ANALYZE)
18
+ run(systemd_analyze, "calendar", value.to_s)
19
+ end
20
+
21
+ # Run `systemd-analyze verify` for unit files.
22
+ #
23
+ # @param paths [Array<String>] unit file paths to verify
24
+ # @param user [Boolean] verify user units with `--user` (default: true)
25
+ # @param systemd_analyze [String] path to the `systemd-analyze` executable
26
+ # @return [Array(String, String)] stdout and stderr
27
+ # @raise [Wheneverd::Systemd::SystemdAnalyzeError]
28
+ def self.verify(paths, user: true, systemd_analyze: DEFAULT_SYSTEMD_ANALYZE)
29
+ run(systemd_analyze, "verify", *Array(paths).map(&:to_s), user: user)
30
+ end
31
+
32
+ def self.run(systemd_analyze, *args, user: false)
33
+ cmd = [systemd_analyze.to_s]
34
+ cmd << "--user" if user
35
+ cmd.concat(args.flatten.map(&:to_s))
36
+
37
+ stdout, stderr, status = Open3.capture3(*cmd)
38
+ raise SystemdAnalyzeError, format_error(cmd, status, stdout, stderr) unless status.success?
39
+
40
+ [stdout, stderr]
41
+ rescue Errno::ENOENT
42
+ raise SystemdAnalyzeError, "systemd-analyze not found (tried: #{systemd_analyze})"
43
+ end
44
+
45
+ def self.format_error(cmd, status, stdout, stderr)
46
+ details = []
47
+ details << "command: #{cmd.join(' ')}"
48
+ details << "status: #{status.exitstatus}"
49
+ details << "stdout: #{stdout.strip}" unless stdout.to_s.strip.empty?
50
+ details << "stderr: #{stderr.strip}" unless stderr.to_s.strip.empty?
51
+ "systemd-analyze failed (#{details.join(', ')})"
52
+ end
53
+ private_class_method :format_error
54
+ end
55
+ end
56
+ end
@@ -22,5 +22,8 @@ module Wheneverd
22
22
 
23
23
  # Raised when a `loginctl` invocation fails.
24
24
  class LoginctlError < Error; end
25
+
26
+ # Raised when a `systemd-analyze` invocation fails (or `systemd-analyze` is not available).
27
+ class SystemdAnalyzeError < Error; end
25
28
  end
26
29
  end
@@ -53,7 +53,7 @@ module Wheneverd
53
53
  def self.job_signature(job)
54
54
  case job
55
55
  when Wheneverd::Job::Command
56
- "command:#{job.command}"
56
+ job.signature
57
57
  else
58
58
  raise ArgumentError, "Unsupported job type: #{job.class}"
59
59
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Wheneverd
4
4
  # Gem version.
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
data/lib/wheneverd.rb CHANGED
@@ -35,6 +35,7 @@ require_relative "wheneverd/systemd/cron_parser"
35
35
  require_relative "wheneverd/systemd/calendar_spec"
36
36
  require_relative "wheneverd/systemd/unit_namer"
37
37
  require_relative "wheneverd/systemd/renderer"
38
+ require_relative "wheneverd/systemd/analyze"
38
39
  require_relative "wheneverd/systemd/systemctl"
39
40
  require_relative "wheneverd/systemd/loginctl"
40
41
  require_relative "wheneverd/systemd/unit_writer"
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+ require_relative "support/cli_test_helpers"
5
+
6
+ class CLIDiffTest < Minitest::Test
7
+ include CLITestHelpers
8
+
9
+ def test_exits_one_and_shows_added_diff_when_units_are_not_installed
10
+ with_inited_unit_dir("missing_units") do |unit_dir|
11
+ timer = first_timer
12
+ status, out, err = run_diff(unit_dir)
13
+ assert_diff_added(status, out, err, unit_dir, timer)
14
+ end
15
+ end
16
+
17
+ def test_exits_zero_when_no_differences
18
+ with_installed_units do |unit_dir|
19
+ status, out, err = run_diff(unit_dir)
20
+ assert_equal 0, status
21
+ assert_equal "", err
22
+ assert_equal "", out
23
+ end
24
+ end
25
+
26
+ def test_exits_one_and_shows_removed_lines_when_installed_unit_was_modified
27
+ with_installed_units do |unit_dir|
28
+ timer = first_timer
29
+ File.open(File.join(unit_dir, timer), "a") { |f| f.puts "# local edit" }
30
+ status, out, err = run_diff(unit_dir)
31
+ assert_diff_contains(status, out, err, timer, "-# local edit")
32
+ end
33
+ end
34
+
35
+ def test_exits_one_and_shows_added_lines_when_installed_unit_is_missing_lines
36
+ with_installed_units do |unit_dir|
37
+ timer = first_timer
38
+ remove_exact_line(File.join(unit_dir, timer), "Persistent=true\n")
39
+ status, out, err = run_diff(unit_dir)
40
+ assert_diff_contains(status, out, err, timer, "+Persistent=true")
41
+ end
42
+ end
43
+
44
+ def test_exits_one_and_shows_diff_for_stale_units_on_disk
45
+ with_installed_units do |unit_dir|
46
+ stale_timer = "wheneverd-demo-000000000000.timer"
47
+ stale_path = write_stale_timer(unit_dir, stale_timer)
48
+ status, out, err = run_diff(unit_dir)
49
+ assert_diff_removed(status, out, err, stale_timer, stale_path)
50
+ end
51
+ end
52
+
53
+ def test_returns_two_on_error
54
+ with_project_dir do
55
+ status, out, err = run_cli(["diff", "--schedule", "missing.rb"])
56
+ assert_equal 2, status
57
+ assert_equal "", out
58
+ assert_includes err, "Schedule file not found"
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def with_inited_unit_dir(name)
65
+ with_inited_project_dir do |project_dir|
66
+ yield File.join(project_dir, name)
67
+ end
68
+ end
69
+
70
+ def with_installed_units
71
+ with_inited_unit_dir("tmp_units") do |unit_dir|
72
+ assert_equal 0, run_cli(["write", "--identifier", "demo", "--unit-dir", unit_dir]).first
73
+ yield unit_dir
74
+ end
75
+ end
76
+
77
+ def run_diff(unit_dir)
78
+ run_cli(["diff", "--identifier", "demo", "--unit-dir", unit_dir])
79
+ end
80
+
81
+ def first_timer
82
+ expected_timer_basenames(identifier: "demo").fetch(0)
83
+ end
84
+
85
+ def remove_exact_line(path, line)
86
+ File.write(path, File.read(path).lines.reject { |l| l == line }.join)
87
+ end
88
+
89
+ def write_stale_timer(unit_dir, basename)
90
+ path = File.join(unit_dir, basename)
91
+ File.write(path, "#{Wheneverd::Systemd::Renderer::MARKER_PREFIX} test\n# stale\n")
92
+ path
93
+ end
94
+
95
+ def assert_diff_added(status, out, err, unit_dir, timer)
96
+ assert_equal 1, status
97
+ assert_equal "", err
98
+ assert_includes out, "diff --wheneverd #{timer}"
99
+ assert_includes out, "--- /dev/null"
100
+ assert_includes out, "+++ #{File.join(File.expand_path(unit_dir), timer)}"
101
+ end
102
+
103
+ def assert_diff_contains(status, out, err, timer, expected_line)
104
+ assert_equal 1, status
105
+ assert_equal "", err
106
+ assert_includes out, "diff --wheneverd #{timer}"
107
+ assert_includes out, expected_line
108
+ end
109
+
110
+ def assert_diff_removed(status, out, err, timer, path)
111
+ assert_equal 1, status
112
+ assert_equal "", err
113
+ assert_includes out, "diff --wheneverd #{timer}"
114
+ assert_includes out, "--- #{path}"
115
+ assert_includes out, "+++ /dev/null"
116
+ assert_includes out, "-# stale"
117
+ end
118
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+ require_relative "support/cli_test_helpers"
5
+
6
+ class CLIInitTest < Minitest::Test
7
+ include CLITestHelpers
8
+
9
+ def test_writes_template_with_shell_and_argv_examples
10
+ with_project_dir do |project_dir|
11
+ status, out, err = run_cli(["init"])
12
+ assert_equal 0, status
13
+ assert_equal "", err
14
+ assert_includes out, "Wrote schedule template to"
15
+
16
+ schedule_path = File.join(project_dir, "config", "schedule.rb")
17
+ schedule = File.read(schedule_path)
18
+ assert_includes schedule, "command [\"echo\", \"hello world\"]"
19
+ assert_includes schedule, "shell \"echo hello | sed -e s/hello/hi/\""
20
+ end
21
+ end
22
+
23
+ def test_refuses_to_overwrite_without_force
24
+ with_project_dir do
25
+ assert_equal 0, run_cli(["init"]).first
26
+
27
+ status, out, err = run_cli(["init"])
28
+ assert_equal 1, status
29
+ assert_equal "", out
30
+ assert_includes err, "already exists"
31
+ assert_includes err, "--force"
32
+ end
33
+ end
34
+
35
+ def test_overwrites_with_force
36
+ with_project_dir do |project_dir|
37
+ assert_equal 0, run_cli(["init"]).first
38
+
39
+ schedule_path = File.join(project_dir, "config", "schedule.rb")
40
+ File.write(schedule_path, "# custom\n")
41
+
42
+ status, out, err = run_cli(["init", "--force"])
43
+ assert_equal 0, status
44
+ assert_equal "", err
45
+ assert_includes out, "Overwrote schedule template to"
46
+ assert_includes File.read(schedule_path), "Supported `every` period forms:"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+ require_relative "support/cli_test_helpers"
5
+
6
+ class CLIStatusTest < Minitest::Test
7
+ include CLITestHelpers
8
+
9
+ def test_runs_list_timers_and_status_for_each_installed_timer
10
+ with_installed_units do |unit_dir|
11
+ status, _out, err, calls = run_status(unit_dir)
12
+ assert_cli_success(status, err)
13
+ assert_status_calls(calls, expected_timer_units)
14
+ end
15
+ end
16
+
17
+ def test_returns_nonzero_when_systemctl_fails
18
+ with_installed_units do |unit_dir|
19
+ status, _out, err, calls = run_status(unit_dir, exitstatus: 1, stderr: "boom\n")
20
+ assert_equal 1, status
21
+ assert_includes err, "systemctl failed"
22
+ assert_includes err, "boom"
23
+ assert_includes calls.fetch(0).fetch(0), "list-timers"
24
+ end
25
+ end
26
+
27
+ def test_exits_zero_and_does_not_call_systemctl_when_no_timers_installed
28
+ with_project_dir do |project_dir|
29
+ unit_dir = File.join(project_dir, "empty_units")
30
+ FileUtils.mkdir_p(unit_dir)
31
+ status, out, err, calls = run_status(unit_dir)
32
+ assert_cli_success(status, err)
33
+ assert_equal "", out
34
+ assert_equal [], calls
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def with_installed_units
41
+ with_inited_project_dir do |project_dir|
42
+ unit_dir = File.join(project_dir, "tmp_units")
43
+ assert_equal 0, run_cli(["write", "--identifier", "demo", "--unit-dir", unit_dir]).first
44
+ yield unit_dir
45
+ end
46
+ end
47
+
48
+ def run_status(unit_dir, **kwargs)
49
+ run_cli_with_capture3_stub(["status", "--identifier", "demo", "--unit-dir", unit_dir], **kwargs)
50
+ end
51
+
52
+ def expected_timer_units
53
+ expected_timer_basenames(identifier: "demo").sort
54
+ end
55
+
56
+ def assert_status_calls(calls, expected_timers)
57
+ assert_list_timers_call(calls, expected_timers)
58
+ assert_status_unit_calls(calls, expected_timers)
59
+ end
60
+
61
+ def assert_list_timers_call(calls, expected_timers)
62
+ assert_systemctl_call_starts_with(
63
+ calls,
64
+ 0,
65
+ SYSTEMCTL_USER_PREFIX + ["list-timers", "--all"],
66
+ includes: expected_timers
67
+ )
68
+ end
69
+
70
+ def assert_status_unit_calls(calls, expected_timers)
71
+ status_calls = calls.drop(1).map(&:first)
72
+ assert_equal expected_timers.length, status_calls.length
73
+ assert_equal expected_timers, status_calls.map(&:last).sort
74
+ status_calls.each { |args| assert_equal SYSTEMCTL_USER_PREFIX + ["status", args.last], args }
75
+ end
76
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+ require_relative "support/cli_test_helpers"
5
+
6
+ class CLIValidateTest < Minitest::Test
7
+ include CLITestHelpers
8
+
9
+ DUP_HOURLY_SCHEDULE = <<~RUBY
10
+ # frozen_string_literal: true
11
+
12
+ every :hour do
13
+ command "echo a"
14
+ end
15
+
16
+ every :hour do
17
+ command "echo b"
18
+ end
19
+ RUBY
20
+
21
+ HOURLY_SCHEDULE = <<~RUBY
22
+ # frozen_string_literal: true
23
+
24
+ every :hour do
25
+ command "echo a"
26
+ end
27
+ RUBY
28
+
29
+ def test_validate_runs_systemd_analyze_calendar_for_each_unique_on_calendar
30
+ with_project_dir do
31
+ write_schedule(DUP_HOURLY_SCHEDULE)
32
+ status, out, err, calls = run_validate("--verbose")
33
+ assert_cli_success(status, err)
34
+ assert_includes out, "OK OnCalendar=hourly"
35
+ assert_equal 1, calls.length
36
+ assert_equal %w[systemd-analyze calendar hourly], calls.fetch(0).fetch(0)
37
+ end
38
+ end
39
+
40
+ def test_validate_prints_message_when_no_on_calendar_and_verbose
41
+ with_project_dir do
42
+ write_empty_schedule
43
+ status, out, err, calls = run_validate("--verbose")
44
+ assert_cli_success(status, err)
45
+ assert_includes out, "No OnCalendar= values found"
46
+ assert_equal [], calls
47
+ end
48
+ end
49
+
50
+ def test_validate_verify_runs_systemd_analyze_verify
51
+ with_project_dir do
52
+ write_schedule(HOURLY_SCHEDULE)
53
+ status, out, err, calls = run_validate("--verify", "--verbose")
54
+ assert_cli_success(status, err)
55
+ assert_includes out, "OK systemd-analyze --user verify"
56
+ assert_equal %w[systemd-analyze --user verify], calls.fetch(1).fetch(0).take(3)
57
+ end
58
+ end
59
+
60
+ def test_validate_returns_nonzero_when_systemd_analyze_calendar_fails
61
+ with_project_dir do
62
+ write_schedule(HOURLY_SCHEDULE)
63
+ status, _out, err, calls = run_validate(exitstatus: 1, stderr: "boom\n")
64
+ assert_equal 1, status
65
+ assert_includes err, "systemd-analyze failed"
66
+ assert_includes err, "boom"
67
+ assert_equal %w[systemd-analyze calendar hourly], calls.fetch(0).fetch(0)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def run_validate(*args, **kwargs)
74
+ run_cli_with_capture3_stub(["validate", *args], **kwargs)
75
+ end
76
+
77
+ def write_schedule(contents)
78
+ FileUtils.mkdir_p("config")
79
+ File.write(File.join("config", "schedule.rb"), contents)
80
+ end
81
+ end
@@ -61,7 +61,7 @@ class DomainModelTest < Minitest::Test
61
61
  assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: " ") }
62
62
 
63
63
  error = assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: 123) }
64
- assert_includes error.message, "String"
64
+ assert_includes error.message, "String or an Array"
65
65
  end
66
66
 
67
67
  def test_triggers_render_timer_lines
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class DSLContextShellTest < Minitest::Test
6
+ def test_shell_requires_every_block
7
+ ctx = Wheneverd::DSL::Context.new(path: "/tmp/config/schedule.rb")
8
+ error = assert_raises(Wheneverd::DSL::LoadError) { ctx.shell("echo hi") }
9
+ assert_includes error.message, "shell() must be called inside every() block"
10
+ end
11
+
12
+ def test_shell_requires_string_script
13
+ ctx = Wheneverd::DSL::Context.new(path: "/tmp/config/schedule.rb")
14
+ error = assert_raises(Wheneverd::DSL::LoadError) { ctx.every("1m") { shell(123) } }
15
+ assert_includes error.message, "shell() script must be a String"
16
+ end
17
+
18
+ def test_shell_rejects_empty_script
19
+ ctx = Wheneverd::DSL::Context.new(path: "/tmp/config/schedule.rb")
20
+ error = assert_raises(Wheneverd::DSL::LoadError) { ctx.every("1m") { shell(" ") } }
21
+ assert_includes error.message, "shell() script must not be empty"
22
+ end
23
+ end
@@ -45,6 +45,29 @@ class DSLLoaderIntervalAndDurationTest < Minitest::Test
45
45
  assert_equal ["echo hello"], entry.jobs.map(&:command)
46
46
  end
47
47
 
48
+ def test_loads_argv_command
49
+ schedule = load_schedule(<<~RUBY)
50
+ every "5m" do
51
+ command ["echo", "hello world"]
52
+ end
53
+ RUBY
54
+
55
+ job = schedule.entries.fetch(0).jobs.fetch(0)
56
+ assert_equal ["echo", "hello world"], job.argv
57
+ assert_equal "echo \"hello world\"", job.command
58
+ end
59
+
60
+ def test_loads_shell_helper
61
+ schedule = load_schedule(<<~RUBY)
62
+ every "5m" do
63
+ shell "echo hello | sed -e s/hello/hi/"
64
+ end
65
+ RUBY
66
+
67
+ job = schedule.entries.fetch(0).jobs.fetch(0)
68
+ assert_equal ["/bin/bash", "-lc", "echo hello | sed -e s/hello/hi/"], job.argv
69
+ end
70
+
48
71
  def test_loads_duration_with_at_as_calendar
49
72
  schedule = load_schedule(<<~RUBY)
50
73
  every 1.day, at: "4:30 am" do
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class JobCommandTest < Minitest::Test
6
+ def test_accepts_argv_and_formats_execstart
7
+ command = Wheneverd::Job::Command.new(command: ["echo", "hello world"])
8
+ assert_equal ["echo", "hello world"], command.argv
9
+ assert_equal "echo \"hello world\"", command.command
10
+ end
11
+
12
+ def test_accepts_empty_argv_argument
13
+ command = Wheneverd::Job::Command.new(command: ["printf", "%s", ""])
14
+ assert_equal "printf %s \"\"", command.command
15
+ end
16
+
17
+ def test_rejects_invalid_argv
18
+ assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: []) }
19
+ assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: [" "]) }
20
+ assert_raises(Wheneverd::InvalidCommandError) { Wheneverd::Job::Command.new(command: ["echo", 1]) }
21
+ end
22
+
23
+ def test_rejects_argv_with_newlines
24
+ error = assert_raises(Wheneverd::InvalidCommandError) do
25
+ Wheneverd::Job::Command.new(command: %W[echo hi\nthere])
26
+ end
27
+ assert_includes error.message, "must not include NUL or newlines"
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class SystemdAnalyzeTest < Minitest::Test
6
+ def with_capture3_stub(exitstatus:, stdout: "", stderr: "")
7
+ calls = []
8
+ Thread.current[:open3_capture3_stub] = {
9
+ calls: calls,
10
+ stdout: stdout,
11
+ stderr: stderr,
12
+ exitstatus: exitstatus
13
+ }
14
+ yield calls
15
+ ensure
16
+ Thread.current[:open3_capture3_stub] = nil
17
+ end
18
+
19
+ def test_calendar_builds_command_and_returns_output
20
+ with_capture3_stub(exitstatus: 0, stdout: "ok\n", stderr: "") do |calls|
21
+ out, err = Wheneverd::Systemd::Analyze.calendar("hourly")
22
+ assert_equal "ok\n", out
23
+ assert_equal "", err
24
+ assert_equal [%w[systemd-analyze calendar hourly], {}], calls.fetch(0)
25
+ end
26
+ end
27
+
28
+ def test_verify_builds_user_verify_command
29
+ with_capture3_stub(exitstatus: 0) do |calls|
30
+ Wheneverd::Systemd::Analyze.verify(["/tmp/a.timer", "/tmp/a.service"], user: true)
31
+ assert_equal(
32
+ [["systemd-analyze", "--user", "verify", "/tmp/a.timer", "/tmp/a.service"], {}],
33
+ calls.fetch(0)
34
+ )
35
+ end
36
+ end
37
+
38
+ def test_calendar_raises_systemd_analyze_error_on_failure_with_details
39
+ with_capture3_stub(exitstatus: 1, stdout: "oops\n", stderr: "nope\n") do
40
+ error = assert_raises(Wheneverd::Systemd::SystemdAnalyzeError) do
41
+ Wheneverd::Systemd::Analyze.calendar("hourly")
42
+ end
43
+ assert_includes error.message, "status: 1"
44
+ assert_includes error.message, "stdout: oops"
45
+ assert_includes error.message, "stderr: nope"
46
+ end
47
+ end
48
+
49
+ def test_calendar_raises_systemd_analyze_error_when_missing
50
+ error = assert_raises(Wheneverd::Systemd::SystemdAnalyzeError) do
51
+ Wheneverd::Systemd::Analyze.calendar("hourly", systemd_analyze: "/no/such/systemd-analyze")
52
+ end
53
+ assert_includes error.message, "systemd-analyze not found"
54
+ end
55
+ end
@@ -62,6 +62,12 @@ class SystemdRendererIntervalTest < Minitest::Test
62
62
  assert_includes service.contents, "Type=oneshot"
63
63
  assert_includes service.contents, "ExecStart=echo hello"
64
64
  end
65
+
66
+ def test_interval_service_contains_execstart_for_argv
67
+ entry = interval_entry(seconds: 60, command: ["echo", "hello world"])
68
+ service = service_for(entry)
69
+ assert_includes service.contents, "ExecStart=echo \"hello world\""
70
+ end
65
71
  end
66
72
 
67
73
  class SystemdRendererCalendarTest < Minitest::Test
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wheneverd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bigcurl
@@ -50,11 +50,14 @@ files:
50
50
  - lib/wheneverd/cli/current.rb
51
51
  - lib/wheneverd/cli/deactivate.rb
52
52
  - lib/wheneverd/cli/delete.rb
53
+ - lib/wheneverd/cli/diff.rb
53
54
  - lib/wheneverd/cli/help.rb
54
55
  - lib/wheneverd/cli/init.rb
55
56
  - lib/wheneverd/cli/linger.rb
56
57
  - lib/wheneverd/cli/reload.rb
57
58
  - lib/wheneverd/cli/show.rb
59
+ - lib/wheneverd/cli/status.rb
60
+ - lib/wheneverd/cli/validate.rb
58
61
  - lib/wheneverd/cli/write.rb
59
62
  - lib/wheneverd/core_ext/numeric_duration.rb
60
63
  - lib/wheneverd/dsl/at_normalizer.rb
@@ -69,6 +72,7 @@ files:
69
72
  - lib/wheneverd/interval.rb
70
73
  - lib/wheneverd/job/command.rb
71
74
  - lib/wheneverd/schedule.rb
75
+ - lib/wheneverd/systemd/analyze.rb
72
76
  - lib/wheneverd/systemd/calendar_spec.rb
73
77
  - lib/wheneverd/systemd/cron_parser.rb
74
78
  - lib/wheneverd/systemd/errors.rb
@@ -87,17 +91,24 @@ files:
87
91
  - test/cli_activate_test.rb
88
92
  - test/cli_current_test.rb
89
93
  - test/cli_deactivate_test.rb
94
+ - test/cli_diff_test.rb
90
95
  - test/cli_end_to_end_test.rb
96
+ - test/cli_init_test.rb
91
97
  - test/cli_linger_test.rb
92
98
  - test/cli_reload_test.rb
99
+ - test/cli_status_test.rb
93
100
  - test/cli_systemctl_integration_test.rb
94
101
  - test/cli_systemd_analyze_test.rb
95
102
  - test/cli_test.rb
103
+ - test/cli_validate_test.rb
96
104
  - test/domain_model_test.rb
97
105
  - test/dsl_calendar_symbol_period_list_test.rb
106
+ - test/dsl_context_shell_test.rb
98
107
  - test/dsl_loader_test.rb
108
+ - test/job_command_test.rb
99
109
  - test/support/cli_subprocess_test_helpers.rb
100
110
  - test/support/cli_test_helpers.rb
111
+ - test/systemd_analyze_test.rb
101
112
  - test/systemd_calendar_spec_test.rb
102
113
  - test/systemd_cron_parser_test.rb
103
114
  - test/systemd_renderer_errors_test.rb
@@ -131,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
131
142
  - !ruby/object:Gem::Version
132
143
  version: '0'
133
144
  requirements: []
134
- rubygems_version: 4.0.3
145
+ rubygems_version: 4.0.4
135
146
  specification_version: 4
136
147
  summary: Wheneverd is to systemd timers what whenever is to cron.
137
148
  test_files: []