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 +4 -4
- data/CHANGELOG.md +7 -1
- data/FEATURE_SUMMARY.md +6 -4
- data/Gemfile.lock +2 -2
- data/README.md +46 -12
- data/exe/wheneverd +16 -1
- data/lib/wheneverd/cli/linger.rb +67 -0
- data/lib/wheneverd/cli.rb +2 -0
- data/lib/wheneverd/dsl/context.rb +2 -3
- data/lib/wheneverd/entry.rb +2 -4
- data/lib/wheneverd/systemd/errors.rb +3 -0
- data/lib/wheneverd/systemd/loginctl.rb +59 -0
- data/lib/wheneverd/systemd/renderer.rb +1 -3
- data/lib/wheneverd/version.rb +1 -1
- data/lib/wheneverd.rb +1 -0
- data/test/cli_linger_test.rb +125 -0
- data/wheneverd.gemspec +1 -2
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7ef1858fab03a38b36da00bf0093d4025762263ca868d05a78c1876d8aeb6441
|
|
4
|
+
data.tar.gz: 539fbeec1db7023059767501172d7337da50a966e0727c29879f88cd81770303
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
|
|
15
|
+
## 0.2.1
|
|
16
16
|
|
|
17
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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
|
-
|
|
7
|
+
Pre-1.0, but working end-to-end for user systemd timers:
|
|
10
8
|
|
|
11
|
-
|
|
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
|
|
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
|
|
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 [--
|
|
183
|
-
- `wheneverd delete [--
|
|
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
|
-
|
|
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,
|
|
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
|
|
36
|
+
entry = Wheneverd::Entry.new(trigger: trigger)
|
|
38
37
|
|
|
39
38
|
schedule.add_entry(entry)
|
|
40
39
|
|
data/lib/wheneverd/entry.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
@@ -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,
|
|
86
|
+
contents: timer_contents(path_basename, timer_lines_for(trigger))
|
|
89
87
|
)
|
|
90
88
|
end
|
|
91
89
|
private_class_method :build_timer_unit
|
data/lib/wheneverd/version.rb
CHANGED
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 = ["
|
|
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
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
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
|