wheneverd 0.1.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9af22418de7ea3db50ee8a05d5614f172154a7d6b1b750017594b6202aa5298
4
- data.tar.gz: 77c113c6dc437c94f59ef019cd0c274ecd94c9af2463fcfea712bbd87b109387
3
+ metadata.gz: 7ef1858fab03a38b36da00bf0093d4025762263ca868d05a78c1876d8aeb6441
4
+ data.tar.gz: 539fbeec1db7023059767501172d7337da50a966e0727c29879f88cd81770303
5
5
  SHA512:
6
- metadata.gz: 2f27bd8f52c7d9e111d8b3cdcb1170b1f584f30db2e601c3938b67db29d2d1ca433e419c3020dbe01636e7e62d1798a987173deb7d1d8c3f14be91f5abf0f203
7
- data.tar.gz: 6cbacc2cc72b97b1dee8dd196c1bb332c47c8a0da0cd9e01473abf8cb799b96a6a9d2c3484d7294ae5b70fc392a6287c1ba55a6ad364547ce59387d7224c82a1
6
+ metadata.gz: 2656962166f254b2d48f94d23329db78a16964eda9c164457c5586b4dabfa095077b75372b94da9f9cad38862d5548be15ba7b2567cef5d2fb2cdc3cb94d4984
7
+ data.tar.gz: a06b649a079b18fd3f321bc818e322d3284c98d96bfbabaf43244b4d604193b395186d31548efdf2296fe12509f47e665f1cdf96f47e2af25512b3eae53ba20d
data/CHANGELOG.md CHANGED
@@ -5,7 +5,13 @@ On release, entries are moved into `## x.y.z` sections that match the gem versio
5
5
 
6
6
  ## Unreleased
7
7
 
8
- - No changes yet.
8
+ ## 0.2.1
9
+
10
+ - Removes an unused filtering metadata keyword argument from the schedule DSL.
11
+
12
+ ## 0.2.0
13
+
14
+ - Adds `wheneverd linger enable|disable|status` for managing systemd user lingering via `loginctl`.
9
15
 
10
16
  ## 0.1.0
11
17
 
data/FEATURE_SUMMARY.md CHANGED
@@ -12,19 +12,21 @@ It complements [`CHANGELOG.md`](CHANGELOG.md) by staying high-level and focusing
12
12
 
13
13
  ## Unreleased
14
14
 
15
- - No changes yet.
15
+ ## 0.2.1
16
16
 
17
- ## 0.1.0
17
+ - Adds `wheneverd linger enable|disable|status` for managing systemd user lingering via `loginctl`.
18
+ - Removes an unused filtering metadata keyword argument from the schedule DSL.
19
+
20
+ ## 0.2.0
18
21
 
19
22
  - The `wheneverd` CLI is implemented using Clamp (`--help`, usage errors in `ERROR: ...` format, `--verbose` for details).
20
23
  - The gem includes a small “whenever-like” domain model (interval parsing, durations, triggers, schedules).
21
24
  - The gem can load a Ruby schedule DSL file via `Wheneverd::DSL::Loader.load_file`.
22
- - Schedule DSL supports `every(period, at: ..., roles: ...) { command("...") }` entries (multiple `command` calls per entry).
25
+ - Schedule DSL supports `every(period, at: ...) { command("...") }` entries (multiple `command` calls per entry).
23
26
  - Schedule DSL supports multiple calendar period symbols per `every` block (e.g. `every :tuesday, :wednesday`).
24
27
  - Supported `every` periods include interval strings/durations, calendar shortcuts (`:hour`, `:day`, `:month`, `:year`),
25
28
  day selectors (`:monday..:sunday`, `:weekday`, `:weekend`), and standard 5-field cron strings.
26
29
  - `at:` supports a string or an array of strings (for calendar schedules), like `"4:30 am"` or `"00:15"`.
27
- - `roles:` is accepted and stored on entries, but is not used for filtering yet.
28
30
  - The gem can render systemd `.service` and `.timer` units via `Wheneverd::Systemd::Renderer.render`.
29
31
  - Generated unit basenames include a stable ID derived from the job’s trigger + command (reordering schedule blocks won’t rename units).
30
32
  - Interval timers include both `OnActiveSec=` and `OnUnitActiveSec=` to ensure a newly started timer has a next run scheduled.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- wheneverd (0.1.0)
4
+ wheneverd (0.2.0)
5
5
  clamp (~> 1.3)
6
6
 
7
7
  GEM
@@ -122,7 +122,7 @@ CHECKSUMS
122
122
  tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
123
123
  unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
124
124
  unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
125
- wheneverd (0.1.0)
125
+ wheneverd (0.2.0)
126
126
  yard (0.9.38) sha256=721fb82afb10532aa49860655f6cc2eaa7130889df291b052e1e6b268283010f
127
127
 
128
128
  BUNDLED WITH
data/README.md CHANGED
@@ -2,15 +2,17 @@
2
2
 
3
3
  Wheneverd is to systemd timers what the [`whenever` gem](https://github.com/javan/whenever) is to cron.
4
4
 
5
- Tagline / repo: `git@github.com:bigcurl/wheneverd.git`
6
-
7
5
  ## Status
8
6
 
9
- Working end-to-end: schedule DSL loading, systemd unit rendering, and safe unit write/list/delete are implemented, along with a CLI for `init`, `show`, `write`, `delete`, `activate`, `deactivate`, `reload`, and `current`.
7
+ Pre-1.0, but working end-to-end for user systemd timers:
10
8
 
11
- Known limitations: `roles:` is accepted but not used for filtering yet.
9
+ - Loads a Ruby schedule DSL file (default: `config/schedule.rb`).
10
+ - Renders systemd `.service`/`.timer` units (interval, calendar, and 5-field cron schedules).
11
+ - Writes, lists, and deletes generated unit files (default: `~/.config/systemd/user`).
12
+ - Enables/starts/stops/disables/restarts timers via `systemctl --user`.
13
+ - Manages lingering via `loginctl` (so timers can run while logged out).
12
14
 
13
- See `FEATURE_SUMMARY.md` for high-level user-visible behavior, and `CHANGELOG.md` for release notes.
15
+ See `FEATURE_SUMMARY.md` for user-visible behavior, and `CHANGELOG.md` for release notes.
14
16
 
15
17
  ## Installation
16
18
 
@@ -38,6 +40,7 @@ wheneverd activate
38
40
  wheneverd deactivate
39
41
  wheneverd reload
40
42
  wheneverd current
43
+ wheneverd linger
41
44
  ```
42
45
 
43
46
  ### Minimal `config/schedule.rb` example
@@ -81,6 +84,35 @@ After changing your schedule, rewrite units and restart the timer(s) to pick up
81
84
  wheneverd reload
82
85
  ```
83
86
 
87
+ ### User timers and lingering (`loginctl enable-linger`)
88
+
89
+ By default, `wheneverd` uses *user* systemd units (`systemctl --user`). On many systems, the per-user systemd instance
90
+ only runs while you are logged in. If you want timers to run after logout (or on boot without an interactive login),
91
+ enable lingering for your user:
92
+
93
+ ```bash
94
+ wheneverd linger enable
95
+ ```
96
+
97
+ This runs `loginctl enable-linger "$USER"` under the hood. If you see “Access denied”, your system may require admin
98
+ privileges (polkit policy); try:
99
+
100
+ ```bash
101
+ sudo loginctl enable-linger "$USER"
102
+ ```
103
+
104
+ Check whether lingering is enabled:
105
+
106
+ ```bash
107
+ wheneverd linger status
108
+ ```
109
+
110
+ To disable it later:
111
+
112
+ ```bash
113
+ wheneverd linger disable
114
+ ```
115
+
84
116
  ## Syntax
85
117
 
86
118
  Schedules are defined in a Ruby file (default: `config/schedule.rb`) and evaluated in a dedicated DSL context.
@@ -90,7 +122,7 @@ Note: schedule files are executed as Ruby. Do not run untrusted schedule code.
90
122
  The core shape is:
91
123
 
92
124
  ```ruby
93
- every(period, at: nil, roles: nil) do
125
+ every(period, at: nil) do
94
126
  command "echo hello"
95
127
  end
96
128
  ```
@@ -155,10 +187,6 @@ Cron translation supports standard 5-field crontab strings (`minute hour day-of-
155
187
 
156
188
  Unsupported cron patterns raise an error at render time (e.g. non-5-field strings, `@daily`, `L`, `W`, `#`, `?`).
157
189
 
158
- ### `roles:`
159
-
160
- `roles:` is accepted and stored on entries, but is ignored in v1 (no role-based filtering yet).
161
-
162
190
  ## CLI
163
191
 
164
192
  Defaults:
@@ -174,22 +202,28 @@ Notes:
174
202
  - Identifiers are sanitized for use in unit file names (non-alphanumeric characters become `-`).
175
203
  - Unit basenames include a stable ID derived from the job’s trigger + command (reordering schedule blocks won’t rename units).
176
204
  - `wheneverd write` / `wheneverd reload` prune previously generated units for the identifier by default (use `--no-prune` to keep old units around).
205
+ - `--unit-dir` controls where unit files are written/read/deleted; `activate`/`deactivate` use systemd’s unit search path.
177
206
 
178
207
  Commands:
179
208
 
180
209
  - `wheneverd init [--schedule PATH] [--force]` writes a template schedule file.
181
210
  - `wheneverd show [--schedule PATH] [--identifier NAME]` prints rendered units to stdout.
182
- - `wheneverd write [--dry-run] [--unit-dir PATH] [--[no-]prune]` writes units to disk (or prints paths in `--dry-run` mode).
183
- - `wheneverd delete [--dry-run] [--unit-dir PATH]` deletes previously generated units for the identifier.
211
+ - `wheneverd write [--schedule PATH] [--identifier NAME] [--unit-dir PATH] [--dry-run] [--[no-]prune]` writes units to disk (or prints paths in `--dry-run` mode).
212
+ - `wheneverd delete [--identifier NAME] [--unit-dir PATH] [--dry-run]` deletes previously generated units for the identifier.
184
213
  - `wheneverd activate [--schedule PATH] [--identifier NAME]` runs `systemctl --user daemon-reload` and enables/starts the timers.
185
214
  - `wheneverd deactivate [--schedule PATH] [--identifier NAME]` stops and disables the timers.
186
215
  - `wheneverd reload [--schedule PATH] [--identifier NAME] [--unit-dir PATH] [--[no-]prune]` writes units, reloads systemd, and restarts timers.
187
216
  - `wheneverd current [--identifier NAME] [--unit-dir PATH]` prints the currently installed unit file contents from disk.
217
+ - `wheneverd linger [--user NAME] [enable|disable|status]` manages lingering via `loginctl` (`status` is the default).
188
218
 
189
219
  ## Development
190
220
 
191
221
  ```bash
192
222
  bundle install
223
+
224
+ # Run the CLI from this repo:
225
+ bundle exec exe/wheneverd --help
226
+
193
227
  bundle exec rake test
194
228
  bundle exec rake ci
195
229
  bundle exec rake yard
data/exe/wheneverd CHANGED
@@ -1,9 +1,24 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
+ begin
5
+ require "bundler/setup"
6
+ rescue LoadError
7
+ # Allow running without Bundler (e.g., when installed as a gem).
8
+ end
9
+
4
10
  lib = File.expand_path("../lib", __dir__)
5
11
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
12
 
7
- require "wheneverd/cli"
13
+ begin
14
+ require "wheneverd/cli"
15
+ rescue LoadError => e
16
+ if e.message.include?("clamp")
17
+ warn "ERROR: missing gem dependency `clamp`."
18
+ warn "Run `bundle install` and then `bundle exec exe/wheneverd ...`."
19
+ exit 1
20
+ end
21
+ raise
22
+ end
8
23
 
9
24
  exit(Wheneverd::CLI.run || 0)
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wheneverd
4
+ # Implements `wheneverd linger` (manage systemd user lingering via `loginctl`).
5
+ class CLI::Linger < CLI
6
+ option "--user", "NAME", "Username to manage (default: $USER)"
7
+
8
+ private
9
+
10
+ def username_value
11
+ name = user.to_s.strip
12
+ name = ENV.fetch("USER", "").strip if name.empty?
13
+ raise ArgumentError, "Could not determine username; pass --user NAME" if name.empty?
14
+
15
+ name
16
+ end
17
+ end
18
+ end
19
+
20
+ module Wheneverd
21
+ class CLI::Linger::Enable < CLI::Linger
22
+ def execute
23
+ name = username_value
24
+ Wheneverd::Systemd::Loginctl.enable_linger(name)
25
+ puts "Enabled lingering for #{name}"
26
+ 0
27
+ rescue StandardError => e
28
+ handle_error(e)
29
+ end
30
+ end
31
+ end
32
+
33
+ module Wheneverd
34
+ class CLI::Linger::Disable < CLI::Linger
35
+ def execute
36
+ name = username_value
37
+ Wheneverd::Systemd::Loginctl.disable_linger(name)
38
+ puts "Disabled lingering for #{name}"
39
+ 0
40
+ rescue StandardError => e
41
+ handle_error(e)
42
+ end
43
+ end
44
+ end
45
+
46
+ module Wheneverd
47
+ class CLI::Linger::Status < CLI::Linger
48
+ def execute
49
+ name = username_value
50
+ stdout, _stderr = Wheneverd::Systemd::Loginctl.show_linger(name)
51
+ puts stdout.strip unless stdout.to_s.strip.empty?
52
+ 0
53
+ rescue StandardError => e
54
+ handle_error(e)
55
+ end
56
+ end
57
+ end
58
+
59
+ module Wheneverd
60
+ class CLI::Linger
61
+ self.default_subcommand = "status"
62
+
63
+ subcommand "enable", "Enable lingering via loginctl", Wheneverd::CLI::Linger::Enable
64
+ subcommand "disable", "Disable lingering via loginctl", Wheneverd::CLI::Linger::Disable
65
+ subcommand "status", "Show lingering status via loginctl", Wheneverd::CLI::Linger::Status
66
+ end
67
+ end
data/lib/wheneverd/cli.rb CHANGED
@@ -69,6 +69,7 @@ require_relative "cli/activate"
69
69
  require_relative "cli/deactivate"
70
70
  require_relative "cli/reload"
71
71
  require_relative "cli/current"
72
+ require_relative "cli/linger"
72
73
 
73
74
  module Wheneverd
74
75
  class CLI
@@ -83,5 +84,6 @@ module Wheneverd
83
84
  subcommand "deactivate", "Stop and disable timers via systemctl --user", Wheneverd::CLI::Deactivate
84
85
  subcommand "reload", "Write units, reload daemon, restart timers", Wheneverd::CLI::Reload
85
86
  subcommand "current", "Show installed units from disk", Wheneverd::CLI::Current
87
+ subcommand "linger", "Manage systemd user lingering via loginctl", Wheneverd::CLI::Linger
86
88
  end
87
89
  end
@@ -25,16 +25,15 @@ module Wheneverd
25
25
  #
26
26
  # @param periods [Array<String, Symbol, Wheneverd::Duration, Array<Symbol>>]
27
27
  # @param at [String, Array<String>, nil]
28
- # @param roles [Object] stored but currently not used for filtering
29
28
  # @return [Wheneverd::Entry]
30
- def every(*periods, at: nil, roles: nil, &block)
29
+ def every(*periods, at: nil, &block)
31
30
  raise InvalidPeriodError.new("every() requires a block", path: path) unless block
32
31
 
33
32
  raise InvalidPeriodError.new("every() requires a period", path: path) if periods.empty?
34
33
 
35
34
  period = periods.length == 1 ? periods.first : periods
36
35
  trigger = @period_parser.trigger_for(period, at: at)
37
- entry = Wheneverd::Entry.new(trigger: trigger, roles: roles)
36
+ entry = Wheneverd::Entry.new(trigger: trigger)
38
37
 
39
38
  schedule.add_entry(entry)
40
39
 
@@ -6,17 +6,15 @@ module Wheneverd
6
6
  # An entry ties together a trigger (when to run) and one or more jobs (what to run).
7
7
  class Entry
8
8
  # @return [Wheneverd::Trigger::Interval, Wheneverd::Trigger::Calendar, Wheneverd::Trigger::Boot]
9
- attr_reader :trigger, :jobs, :roles
9
+ attr_reader :trigger, :jobs
10
10
 
11
11
  # @param trigger [Object] a trigger object describing when to run
12
12
  # @param jobs [Array<Object>] job objects (usually {Wheneverd::Job::Command})
13
- # @param roles [Object] stored but currently not used for filtering
14
- def initialize(trigger:, jobs: [], roles: nil)
13
+ def initialize(trigger:, jobs: [])
15
14
  raise ArgumentError, "trigger is required" if trigger.nil?
16
15
 
17
16
  @trigger = trigger
18
17
  @jobs = jobs.dup
19
- @roles = roles
20
18
  end
21
19
 
22
20
  # Append a job to the entry.
@@ -19,5 +19,8 @@ module Wheneverd
19
19
 
20
20
  # Raised when a `systemctl` invocation fails.
21
21
  class SystemctlError < Error; end
22
+
23
+ # Raised when a `loginctl` invocation fails.
24
+ class LoginctlError < Error; end
22
25
  end
23
26
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Wheneverd
6
+ module Systemd
7
+ # Thin wrapper around `loginctl` that raises on non-zero exit status.
8
+ class Loginctl
9
+ # Run `loginctl` and return stdout/stderr.
10
+ #
11
+ # @param args [Array<String>]
12
+ # @return [Array(String, String)] stdout and stderr
13
+ # @raise [Wheneverd::Systemd::LoginctlError]
14
+ def self.run(*args)
15
+ cmd = ["loginctl", "--no-pager"]
16
+ cmd.concat(args.flatten.map(&:to_s))
17
+
18
+ stdout, stderr, status = Open3.capture3(*cmd)
19
+ raise LoginctlError, format_error(cmd, status, stdout, stderr) unless status.success?
20
+
21
+ [stdout, stderr]
22
+ end
23
+
24
+ # Enable lingering for a user (so their `systemctl --user` instance can run while logged out).
25
+ #
26
+ # @param username [String]
27
+ # @return [Array(String, String)] stdout and stderr
28
+ def self.enable_linger(username)
29
+ run("enable-linger", username)
30
+ end
31
+
32
+ # Disable lingering for a user.
33
+ #
34
+ # @param username [String]
35
+ # @return [Array(String, String)] stdout and stderr
36
+ def self.disable_linger(username)
37
+ run("disable-linger", username)
38
+ end
39
+
40
+ # Show lingering state for a user (includes a `Linger=` line).
41
+ #
42
+ # @param username [String]
43
+ # @return [Array(String, String)] stdout and stderr
44
+ def self.show_linger(username)
45
+ run("show-user", username, "-p", "Linger")
46
+ end
47
+
48
+ def self.format_error(cmd, status, stdout, stderr)
49
+ details = []
50
+ details << "command: #{cmd.join(' ')}"
51
+ details << "status: #{status.exitstatus}"
52
+ details << "stdout: #{stdout.strip}" unless stdout.to_s.strip.empty?
53
+ details << "stderr: #{stderr.strip}" unless stderr.to_s.strip.empty?
54
+ "loginctl failed (#{details.join(', ')})"
55
+ end
56
+ private_class_method :format_error
57
+ end
58
+ end
59
+ end
@@ -80,12 +80,10 @@ module Wheneverd
80
80
  private_class_method :build_service_unit
81
81
 
82
82
  def self.build_timer_unit(path_basename, trigger)
83
- timer_lines = timer_lines_for(trigger)
84
-
85
83
  Unit.new(
86
84
  path_basename: path_basename,
87
85
  kind: :timer,
88
- contents: timer_contents(path_basename, timer_lines)
86
+ contents: timer_contents(path_basename, timer_lines_for(trigger))
89
87
  )
90
88
  end
91
89
  private_class_method :build_timer_unit
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Wheneverd
4
4
  # Gem version.
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.1"
6
6
  end
data/lib/wheneverd.rb CHANGED
@@ -36,6 +36,7 @@ require_relative "wheneverd/systemd/calendar_spec"
36
36
  require_relative "wheneverd/systemd/unit_namer"
37
37
  require_relative "wheneverd/systemd/renderer"
38
38
  require_relative "wheneverd/systemd/systemctl"
39
+ require_relative "wheneverd/systemd/loginctl"
39
40
  require_relative "wheneverd/systemd/unit_writer"
40
41
  require_relative "wheneverd/systemd/unit_deleter"
41
42
  require_relative "wheneverd/systemd/unit_lister"
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+ require_relative "support/cli_test_helpers"
5
+
6
+ class CLILingerEnableTest < Minitest::Test
7
+ include CLITestHelpers
8
+
9
+ def test_exits_zero
10
+ with_project_dir do
11
+ status, _out, err, _calls = run_cli_with_capture3_stub(["linger", "enable", "--user", "demo"])
12
+ assert_cli_success(status, err)
13
+ end
14
+ end
15
+
16
+ def test_calls_loginctl_enable_linger
17
+ with_project_dir do
18
+ status, _out, err, calls = run_cli_with_capture3_stub(["linger", "enable", "--user", "demo"])
19
+ assert_cli_success(status, err)
20
+ args, kwargs = calls.fetch(0)
21
+ assert_equal ["loginctl", "--no-pager", "enable-linger", "demo"], args
22
+ assert_equal({}, kwargs)
23
+ end
24
+ end
25
+
26
+ def test_prints_confirmation
27
+ with_project_dir do
28
+ status, out, err, _calls = run_cli_with_capture3_stub(["linger", "enable", "--user", "demo"])
29
+ assert_cli_success(status, err)
30
+ assert_includes out, "Enabled lingering for demo"
31
+ end
32
+ end
33
+ end
34
+
35
+ class CLILingerDisableTest < Minitest::Test
36
+ include CLITestHelpers
37
+
38
+ def test_calls_loginctl_disable_linger
39
+ with_project_dir do
40
+ status, _out, err, calls = run_cli_with_capture3_stub(["linger", "disable", "--user", "demo"])
41
+ assert_cli_success(status, err)
42
+ args, _kwargs = calls.fetch(0)
43
+ assert_equal ["loginctl", "--no-pager", "disable-linger", "demo"], args
44
+ end
45
+ end
46
+ end
47
+
48
+ class CLILingerStatusTest < Minitest::Test
49
+ include CLITestHelpers
50
+
51
+ def test_is_default_subcommand
52
+ with_project_dir do
53
+ status, _out, err, calls = run_cli_with_capture3_stub(["linger", "--user", "demo"],
54
+ stdout: "Linger=yes\n")
55
+ assert_cli_success(status, err)
56
+ args, _kwargs = calls.fetch(0)
57
+ assert_equal ["loginctl", "--no-pager", "show-user", "demo", "-p", "Linger"], args
58
+ end
59
+ end
60
+
61
+ def test_prints_linger_property_line
62
+ with_project_dir do
63
+ status, out, err, _calls = run_cli_with_capture3_stub(
64
+ ["linger", "status", "--user", "demo"],
65
+ stdout: "Linger=yes\n"
66
+ )
67
+ assert_cli_success(status, err)
68
+ assert_equal "Linger=yes\n", out
69
+ end
70
+ end
71
+ end
72
+
73
+ class CLILingerFailureTest < Minitest::Test
74
+ include CLITestHelpers
75
+
76
+ def test_exits_one_on_access_denied
77
+ with_project_dir do
78
+ status, _out, _err, _calls = run_cli_with_capture3_stub(
79
+ ["linger", "enable", "--user", "demo"],
80
+ exitstatus: 1,
81
+ stderr: "Access denied\n"
82
+ )
83
+ assert_equal 1, status
84
+ end
85
+ end
86
+
87
+ def test_prints_loginctl_failed_message
88
+ with_project_dir do
89
+ _status, out, err, _calls = run_cli_with_capture3_stub(
90
+ ["linger", "enable", "--user", "demo"],
91
+ exitstatus: 1,
92
+ stderr: "Access denied\n"
93
+ )
94
+ assert_equal "", out
95
+ assert_includes err, "loginctl failed"
96
+ assert_includes err, "Access denied"
97
+ end
98
+ end
99
+
100
+ def test_disable_failure_uses_handle_error
101
+ with_project_dir do
102
+ status, out, err, _calls = run_cli_with_capture3_stub(
103
+ ["linger", "disable", "--user", "demo"],
104
+ exitstatus: 1,
105
+ stderr: "Access denied\n"
106
+ )
107
+ assert_equal 1, status
108
+ assert_equal "", out
109
+ assert_includes err, "loginctl failed"
110
+ end
111
+ end
112
+
113
+ def test_status_failure_uses_handle_error
114
+ with_project_dir do
115
+ status, out, err, _calls = run_cli_with_capture3_stub(
116
+ ["linger", "status", "--user", "demo"],
117
+ exitstatus: 1,
118
+ stderr: "Access denied\n"
119
+ )
120
+ assert_equal 1, status
121
+ assert_equal "", out
122
+ assert_includes err, "loginctl failed"
123
+ end
124
+ end
125
+ end
data/wheneverd.gemspec CHANGED
@@ -5,8 +5,7 @@ require_relative "lib/wheneverd/version"
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "wheneverd"
7
7
  spec.version = Wheneverd::VERSION
8
- spec.authors = ["Dr. Samuel Goebert"]
9
- spec.email = ["maintainers@example.com"]
8
+ spec.authors = ["bigcurl"]
10
9
 
11
10
  spec.summary = "Wheneverd is to systemd timers what whenever is to cron."
12
11
  spec.description =
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wheneverd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
- - Dr. Samuel Goebert
7
+ - bigcurl
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
@@ -25,8 +25,6 @@ dependencies:
25
25
  version: '1.3'
26
26
  description: Generates systemd timer/service units from a Ruby DSL, similar in spirit
27
27
  to the whenever gem for cron.
28
- email:
29
- - maintainers@example.com
30
28
  executables:
31
29
  - wheneverd
32
30
  extensions: []
@@ -54,6 +52,7 @@ files:
54
52
  - lib/wheneverd/cli/delete.rb
55
53
  - lib/wheneverd/cli/help.rb
56
54
  - lib/wheneverd/cli/init.rb
55
+ - lib/wheneverd/cli/linger.rb
57
56
  - lib/wheneverd/cli/reload.rb
58
57
  - lib/wheneverd/cli/show.rb
59
58
  - lib/wheneverd/cli/write.rb
@@ -73,6 +72,7 @@ files:
73
72
  - lib/wheneverd/systemd/calendar_spec.rb
74
73
  - lib/wheneverd/systemd/cron_parser.rb
75
74
  - lib/wheneverd/systemd/errors.rb
75
+ - lib/wheneverd/systemd/loginctl.rb
76
76
  - lib/wheneverd/systemd/renderer.rb
77
77
  - lib/wheneverd/systemd/systemctl.rb
78
78
  - lib/wheneverd/systemd/time_parser.rb
@@ -88,6 +88,7 @@ files:
88
88
  - test/cli_current_test.rb
89
89
  - test/cli_deactivate_test.rb
90
90
  - test/cli_end_to_end_test.rb
91
+ - test/cli_linger_test.rb
91
92
  - test/cli_reload_test.rb
92
93
  - test/cli_systemctl_integration_test.rb
93
94
  - test/cli_systemd_analyze_test.rb