wheneverd 0.3.0 → 0.5.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +38 -33
- data/README.md +64 -7
- data/lib/wheneverd/cli/activate.rb +5 -5
- data/lib/wheneverd/cli/deactivate.rb +6 -6
- data/lib/wheneverd/cli/reload.rb +8 -8
- data/lib/wheneverd/cli/status.rb +13 -6
- data/lib/wheneverd/cli.rb +10 -8
- data/lib/wheneverd/dsl/context.rb +46 -0
- data/lib/wheneverd/dsl/period_parser.rb +8 -107
- data/lib/wheneverd/dsl/period_strategy/array_strategy.rb +29 -0
- data/lib/wheneverd/dsl/period_strategy/base.rb +65 -0
- data/lib/wheneverd/dsl/period_strategy/duration_strategy.rb +33 -0
- data/lib/wheneverd/dsl/period_strategy/string_strategy.rb +51 -0
- data/lib/wheneverd/dsl/period_strategy/symbol_strategy.rb +31 -0
- data/lib/wheneverd/dsl/period_strategy.rb +43 -0
- data/lib/wheneverd/duration.rb +1 -7
- data/lib/wheneverd/errors.rb +3 -0
- data/lib/wheneverd/interval.rb +22 -7
- data/lib/wheneverd/schedule.rb +15 -1
- data/lib/wheneverd/service.rb +105 -0
- data/lib/wheneverd/systemd/cron_parser/dow_parser.rb +208 -0
- data/lib/wheneverd/systemd/cron_parser/field_parser.rb +163 -0
- data/lib/wheneverd/systemd/cron_parser.rb +56 -303
- data/lib/wheneverd/systemd/renderer.rb +31 -66
- data/lib/wheneverd/systemd/unit_content_builder.rb +99 -0
- data/lib/wheneverd/systemd/unit_deleter.rb +2 -28
- data/lib/wheneverd/systemd/unit_lister.rb +2 -28
- data/lib/wheneverd/systemd/unit_namer.rb +6 -15
- data/lib/wheneverd/systemd/unit_path_utils.rb +54 -0
- data/lib/wheneverd/systemd/unit_writer.rb +2 -28
- data/lib/wheneverd/trigger/base.rb +22 -0
- data/lib/wheneverd/trigger/boot.rb +8 -6
- data/lib/wheneverd/trigger/calendar.rb +7 -0
- data/lib/wheneverd/trigger/interval.rb +8 -6
- data/lib/wheneverd/validation.rb +89 -0
- data/lib/wheneverd/version.rb +1 -1
- data/lib/wheneverd.rb +5 -1
- data/test/cli_activate_test.rb +27 -0
- data/test/cli_reload_test.rb +23 -0
- data/test/cli_status_test.rb +14 -4
- data/test/domain_model_test.rb +105 -0
- data/test/dsl_context_shell_test.rb +31 -0
- data/test/systemd_cron_parser_test.rb +41 -25
- data/test/systemd_renderer_errors_test.rb +1 -1
- data/test/systemd_renderer_test.rb +73 -0
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9469e813aa6390a8e7eb911ce672a9fdaf7ab861e435d71d2d8887d551f8b8f8
|
|
4
|
+
data.tar.gz: dcf0211fe96fe0faff489a82ab3d0f12f36c3703083a949606eed4ab8eb9fa28
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a448263264d7322c6622d111f5bf2f51f916ee0dc22193388e4b8b15a4336c3d136c475102db7c772b5be35fd53fa3800fcb733ef3850d620e982baeb9fa9a9
|
|
7
|
+
data.tar.gz: 62aad81a000a60510e6b230660321ec40951ecdcaed1317517f33fa10688fa319aa2afc43f8b003704b364d0f0f68a1d12e95482dce408208269a400ebee9a87
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.0.3
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ On release, entries are moved into `## x.y.z` sections that match the gem versio
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 0.5.0
|
|
9
|
+
|
|
10
|
+
- Adds top-level `service` DSL entries for long-running systemd user services.
|
|
11
|
+
- `activate`, `deactivate`, `reload`, and `status` now manage standalone services alongside timers.
|
|
12
|
+
- Standalone services render as `Type=simple` units with configurable `Restart=`, `RestartSec=`, and extra `[Service]` settings.
|
|
13
|
+
|
|
14
|
+
## 0.4.0
|
|
15
|
+
|
|
16
|
+
- Docs: adds a copy/paste "deploy a simple schedule" example and refines README status section.
|
|
17
|
+
- Refactor: extracts `UnitPathUtils` module for shared identifier/path utilities across `UnitWriter`, `UnitDeleter`, `UnitLister`, and `Renderer`.
|
|
18
|
+
- Refactor: adds polymorphic `Trigger::Base` interface with `#systemd_timer_lines` and `#signature` methods for all trigger types.
|
|
19
|
+
- Refactor: splits `CronParser` into focused `FieldParser` and `DowParser` submodules for maintainability.
|
|
20
|
+
- Refactor: implements strategy pattern for `PeriodParser` with dedicated strategies for Duration, String, Symbol, and Array inputs.
|
|
21
|
+
- Refactor: extracts `UnitContentBuilder` from `Renderer` for cleaner separation of unit content generation.
|
|
22
|
+
- Refactor: adds `Validation` module with composable validators (`type`, `positive_integer`, `non_empty_string`, `non_empty_array`, `in_range`).
|
|
23
|
+
|
|
8
24
|
## 0.3.0
|
|
9
25
|
|
|
10
26
|
- Schedule DSL: `command` accepts argv arrays, adds a `shell` helper for `/bin/bash -lc`, and `wheneverd init` includes examples.
|
data/Gemfile.lock
CHANGED
|
@@ -1,61 +1,64 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
wheneverd (0.
|
|
4
|
+
wheneverd (0.5.0)
|
|
5
5
|
clamp (~> 1.3)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
10
|
ast (2.4.3)
|
|
11
|
-
clamp (1.
|
|
11
|
+
clamp (1.5.2)
|
|
12
12
|
date (3.5.1)
|
|
13
13
|
docile (1.4.1)
|
|
14
|
-
|
|
14
|
+
drb (2.2.3)
|
|
15
|
+
erb (6.0.4)
|
|
15
16
|
io-console (0.8.2)
|
|
16
|
-
irb (1.
|
|
17
|
+
irb (1.18.0)
|
|
17
18
|
pp (>= 0.6.0)
|
|
19
|
+
prism (>= 1.3.0)
|
|
18
20
|
rdoc (>= 4.0.0)
|
|
19
21
|
reline (>= 0.4.2)
|
|
20
|
-
json (2.
|
|
22
|
+
json (2.19.4)
|
|
21
23
|
language_server-protocol (3.17.0.5)
|
|
22
24
|
lint_roller (1.1.0)
|
|
23
|
-
minitest (6.0.
|
|
25
|
+
minitest (6.0.5)
|
|
26
|
+
drb (~> 2.0)
|
|
24
27
|
prism (~> 1.5)
|
|
25
|
-
parallel (1.
|
|
26
|
-
parser (3.3.
|
|
28
|
+
parallel (2.1.0)
|
|
29
|
+
parser (3.3.11.1)
|
|
27
30
|
ast (~> 2.4.1)
|
|
28
31
|
racc
|
|
29
32
|
pp (0.6.3)
|
|
30
33
|
prettyprint
|
|
31
34
|
prettyprint (0.2.0)
|
|
32
|
-
prism (1.
|
|
35
|
+
prism (1.9.0)
|
|
33
36
|
psych (5.3.1)
|
|
34
37
|
date
|
|
35
38
|
stringio
|
|
36
39
|
racc (1.8.1)
|
|
37
40
|
rainbow (3.1.1)
|
|
38
|
-
rake (13.
|
|
39
|
-
rdoc (7.
|
|
41
|
+
rake (13.4.2)
|
|
42
|
+
rdoc (7.2.0)
|
|
40
43
|
erb
|
|
41
44
|
psych (>= 4.0.0)
|
|
42
45
|
tsort
|
|
43
46
|
redcarpet (3.6.1)
|
|
44
|
-
regexp_parser (2.
|
|
47
|
+
regexp_parser (2.12.0)
|
|
45
48
|
reline (0.6.3)
|
|
46
49
|
io-console (~> 0.5)
|
|
47
|
-
rubocop (1.
|
|
50
|
+
rubocop (1.86.1)
|
|
48
51
|
json (~> 2.3)
|
|
49
52
|
language_server-protocol (~> 3.17.0.2)
|
|
50
53
|
lint_roller (~> 1.1.0)
|
|
51
|
-
parallel (
|
|
54
|
+
parallel (>= 1.10)
|
|
52
55
|
parser (>= 3.3.0.2)
|
|
53
56
|
rainbow (>= 2.2.2, < 4.0)
|
|
54
57
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
55
|
-
rubocop-ast (>= 1.
|
|
58
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
56
59
|
ruby-progressbar (~> 1.7)
|
|
57
60
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
58
|
-
rubocop-ast (1.49.
|
|
61
|
+
rubocop-ast (1.49.1)
|
|
59
62
|
parser (>= 3.3.7.2)
|
|
60
63
|
prism (~> 1.7)
|
|
61
64
|
ruby-progressbar (1.13.0)
|
|
@@ -70,7 +73,7 @@ GEM
|
|
|
70
73
|
unicode-display_width (3.2.0)
|
|
71
74
|
unicode-emoji (~> 4.1)
|
|
72
75
|
unicode-emoji (4.2.0)
|
|
73
|
-
yard (0.9.
|
|
76
|
+
yard (0.9.43)
|
|
74
77
|
|
|
75
78
|
PLATFORMS
|
|
76
79
|
ruby
|
|
@@ -89,31 +92,33 @@ DEPENDENCIES
|
|
|
89
92
|
|
|
90
93
|
CHECKSUMS
|
|
91
94
|
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
92
|
-
|
|
95
|
+
bundler (4.0.11) sha256=5bcec0fb78302e48d02ee46f10ee6e6942be647ba5b44a6d1ddfda9a240ce785
|
|
96
|
+
clamp (1.5.2) sha256=2fed212e5c9b60447eb5097af39d4c12c3c7e6788e8d791e9c436e0e755f4adc
|
|
93
97
|
date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
|
|
94
98
|
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
|
|
95
|
-
|
|
99
|
+
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
|
100
|
+
erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
|
|
96
101
|
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
97
|
-
irb (1.
|
|
98
|
-
json (2.
|
|
102
|
+
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
103
|
+
json (2.19.4) sha256=670a7d333fb3b18ca5b29cb255eb7bef099e40d88c02c80bd42a3f30fe5239ac
|
|
99
104
|
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
100
105
|
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
101
|
-
minitest (6.0.
|
|
102
|
-
parallel (1.
|
|
103
|
-
parser (3.3.
|
|
106
|
+
minitest (6.0.5) sha256=f007d7246bf4feea549502842cd7c6aba8851cdc9c90ba06de9c476c0d01155c
|
|
107
|
+
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
|
108
|
+
parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
|
|
104
109
|
pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
|
|
105
110
|
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
|
106
|
-
prism (1.
|
|
111
|
+
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
107
112
|
psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
|
|
108
113
|
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
109
114
|
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
110
|
-
rake (13.
|
|
111
|
-
rdoc (7.
|
|
115
|
+
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
116
|
+
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
|
|
112
117
|
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
|
|
113
|
-
regexp_parser (2.
|
|
118
|
+
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
|
114
119
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
115
|
-
rubocop (1.
|
|
116
|
-
rubocop-ast (1.49.
|
|
120
|
+
rubocop (1.86.1) sha256=44415f3f01d01a21e01132248d2fd0867572475b566ca188a0a42133a08d4531
|
|
121
|
+
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
|
|
117
122
|
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
118
123
|
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
|
|
119
124
|
simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
|
|
@@ -122,8 +127,8 @@ CHECKSUMS
|
|
|
122
127
|
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
123
128
|
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
|
|
124
129
|
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
|
|
125
|
-
wheneverd (0.
|
|
126
|
-
yard (0.9.
|
|
130
|
+
wheneverd (0.5.0)
|
|
131
|
+
yard (0.9.43) sha256=cf8733a8f0485df2a162927e9b5f182215a61f6d22de096b8f402c726a1c5821
|
|
127
132
|
|
|
128
133
|
BUNDLED WITH
|
|
129
|
-
4.0.
|
|
134
|
+
4.0.11
|
data/README.md
CHANGED
|
@@ -4,14 +4,23 @@ Wheneverd is to systemd timers what the [`whenever` gem](https://github.com/java
|
|
|
4
4
|
|
|
5
5
|
## Status
|
|
6
6
|
|
|
7
|
-
Pre-1.0, but working end-to-end for user
|
|
7
|
+
Pre-1.0, but working end-to-end for systemd user timers on Linux:
|
|
8
8
|
|
|
9
9
|
- Loads a Ruby schedule DSL file (default: `config/schedule.rb`).
|
|
10
10
|
- Renders systemd `.service`/`.timer` units (interval, calendar, and 5-field cron schedules).
|
|
11
|
-
- Writes,
|
|
11
|
+
- Writes, diffs, shows, and deletes generated unit files (default: `~/.config/systemd/user`).
|
|
12
12
|
- Enables/starts/stops/disables/restarts timers via `systemctl --user`.
|
|
13
|
+
- Validates `OnCalendar=` values with `systemd-analyze` (optional unit verification).
|
|
13
14
|
- Manages lingering via `loginctl` (so timers can run while logged out).
|
|
14
15
|
|
|
16
|
+
Non-goals / not yet implemented:
|
|
17
|
+
|
|
18
|
+
- System-level units (`/etc/systemd/system`) / `systemctl` without `--user`.
|
|
19
|
+
- Non-systemd schedulers (cron, launchd, etc).
|
|
20
|
+
- Non-Linux platforms (no Windows/macOS support).
|
|
21
|
+
|
|
22
|
+
Expect the CLI and generated unit details to change until 1.0.
|
|
23
|
+
|
|
15
24
|
See `FEATURE_SUMMARY.md` for user-visible behavior, and `CHANGELOG.md` for release notes.
|
|
16
25
|
|
|
17
26
|
## Installation
|
|
@@ -62,6 +71,40 @@ every 1.day, at: "4:30 am" do
|
|
|
62
71
|
end
|
|
63
72
|
```
|
|
64
73
|
|
|
74
|
+
### Deploy a simple schedule (copy/paste)
|
|
75
|
+
|
|
76
|
+
From your project root (the default identifier is the current directory name):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Install (skip if already in your Gemfile)
|
|
80
|
+
bundle add wheneverd
|
|
81
|
+
bundle install
|
|
82
|
+
|
|
83
|
+
# Write a schedule that appends a timestamp to ~/.cache/wheneverd-demo.log every minute
|
|
84
|
+
mkdir -p config
|
|
85
|
+
cat > config/schedule.rb <<'RUBY'
|
|
86
|
+
# frozen_string_literal: true
|
|
87
|
+
|
|
88
|
+
every "1m" do
|
|
89
|
+
shell "mkdir -p ~/.cache && date >> ~/.cache/wheneverd-demo.log"
|
|
90
|
+
end
|
|
91
|
+
RUBY
|
|
92
|
+
|
|
93
|
+
# Preview, write units, and enable/start the timer(s)
|
|
94
|
+
bundle exec wheneverd show
|
|
95
|
+
bundle exec wheneverd validate
|
|
96
|
+
bundle exec wheneverd write
|
|
97
|
+
bundle exec wheneverd activate
|
|
98
|
+
|
|
99
|
+
# Verify it’s installed and running
|
|
100
|
+
bundle exec wheneverd status
|
|
101
|
+
tail -n 5 ~/.cache/wheneverd-demo.log
|
|
102
|
+
|
|
103
|
+
# Stop/disable timers and remove generated unit files
|
|
104
|
+
bundle exec wheneverd deactivate
|
|
105
|
+
bundle exec wheneverd delete
|
|
106
|
+
```
|
|
107
|
+
|
|
65
108
|
Preview the generated units:
|
|
66
109
|
|
|
67
110
|
```bash
|
|
@@ -124,7 +167,7 @@ Schedules are defined in a Ruby file (default: `config/schedule.rb`) and evaluat
|
|
|
124
167
|
|
|
125
168
|
Note: schedule files are executed as Ruby. Do not run untrusted schedule code.
|
|
126
169
|
|
|
127
|
-
The core shape is:
|
|
170
|
+
The core timer shape is:
|
|
128
171
|
|
|
129
172
|
```ruby
|
|
130
173
|
every(period, at: nil) do
|
|
@@ -132,6 +175,20 @@ every(period, at: nil) do
|
|
|
132
175
|
end
|
|
133
176
|
```
|
|
134
177
|
|
|
178
|
+
Long-running services can be managed from the same schedule with top-level
|
|
179
|
+
`service` entries:
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
service "worker",
|
|
183
|
+
shell: "bundle exec bin/worker",
|
|
184
|
+
restart: "always",
|
|
185
|
+
restart_sec: "5s",
|
|
186
|
+
service: {
|
|
187
|
+
"WorkingDirectory" => "/srv/apps/myapp/current",
|
|
188
|
+
"Environment" => "RAILS_ENV=production"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
135
192
|
For calendar schedules, you can also pass multiple period symbols (or an array) to run the same jobs on multiple days:
|
|
136
193
|
|
|
137
194
|
```ruby
|
|
@@ -227,14 +284,14 @@ Commands:
|
|
|
227
284
|
|
|
228
285
|
- `wheneverd init [--schedule PATH] [--force]` writes a template schedule file.
|
|
229
286
|
- `wheneverd show [--schedule PATH] [--identifier NAME]` prints rendered units to stdout.
|
|
230
|
-
- `wheneverd status [--identifier NAME] [--unit-dir PATH]` prints `systemctl --user list-timers` and `systemctl --user status` for installed timers.
|
|
287
|
+
- `wheneverd status [--identifier NAME] [--unit-dir PATH]` prints `systemctl --user list-timers` and `systemctl --user status` for installed timers/services.
|
|
231
288
|
- `wheneverd diff [--schedule PATH] [--identifier NAME] [--unit-dir PATH]` diffs rendered units vs unit files on disk.
|
|
232
289
|
- `wheneverd validate [--schedule PATH] [--identifier NAME] [--verify]` validates rendered `OnCalendar=` values via `systemd-analyze calendar` (and with `--verify`, runs `systemd-analyze --user verify` on temporary unit files).
|
|
233
290
|
- `wheneverd write [--schedule PATH] [--identifier NAME] [--unit-dir PATH] [--dry-run] [--[no-]prune]` writes units to disk (or prints paths in `--dry-run` mode).
|
|
234
291
|
- `wheneverd delete [--identifier NAME] [--unit-dir PATH] [--dry-run]` deletes previously generated units for the identifier.
|
|
235
|
-
- `wheneverd activate [--schedule PATH] [--identifier NAME]` runs `systemctl --user daemon-reload` and enables/starts the timers.
|
|
236
|
-
- `wheneverd deactivate [--schedule PATH] [--identifier NAME]` stops and disables the timers.
|
|
237
|
-
- `wheneverd reload [--schedule PATH] [--identifier NAME] [--unit-dir PATH] [--[no-]prune]` writes units, reloads systemd, and restarts timers.
|
|
292
|
+
- `wheneverd activate [--schedule PATH] [--identifier NAME]` runs `systemctl --user daemon-reload` and enables/starts the timers/services.
|
|
293
|
+
- `wheneverd deactivate [--schedule PATH] [--identifier NAME]` stops and disables the timers/services.
|
|
294
|
+
- `wheneverd reload [--schedule PATH] [--identifier NAME] [--unit-dir PATH] [--[no-]prune]` writes units, reloads systemd, and restarts timers/services.
|
|
238
295
|
- `wheneverd current [--identifier NAME] [--unit-dir PATH]` prints the currently installed unit file contents from disk.
|
|
239
296
|
- `wheneverd linger [--user NAME] [enable|disable|status]` manages lingering via `loginctl` (`status` is the default).
|
|
240
297
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Wheneverd
|
|
4
|
-
# Implements `wheneverd activate` (enable + start timers via `systemctl --user`).
|
|
4
|
+
# Implements `wheneverd activate` (enable + start timers/services via `systemctl --user`).
|
|
5
5
|
class CLI::Activate < CLI
|
|
6
6
|
def execute
|
|
7
|
-
|
|
8
|
-
return 0 if
|
|
7
|
+
units = activatable_unit_basenames
|
|
8
|
+
return 0 if units.empty?
|
|
9
9
|
|
|
10
10
|
Wheneverd::Systemd::Systemctl.run("daemon-reload")
|
|
11
|
-
Wheneverd::Systemd::Systemctl.run("enable", "--now", *
|
|
11
|
+
Wheneverd::Systemd::Systemctl.run("enable", "--now", *units)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
units.each { |unit| puts unit }
|
|
14
14
|
0
|
|
15
15
|
rescue StandardError => e
|
|
16
16
|
handle_error(e)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Wheneverd
|
|
4
|
-
# Implements `wheneverd deactivate` (stop + disable timers via `systemctl --user`).
|
|
4
|
+
# Implements `wheneverd deactivate` (stop + disable timers/services via `systemctl --user`).
|
|
5
5
|
class CLI::Deactivate < CLI
|
|
6
6
|
def execute
|
|
7
|
-
|
|
8
|
-
return 0 if
|
|
7
|
+
units = activatable_unit_basenames
|
|
8
|
+
return 0 if units.empty?
|
|
9
9
|
|
|
10
|
-
Wheneverd::Systemd::Systemctl.run("stop", *
|
|
11
|
-
Wheneverd::Systemd::Systemctl.run("disable", *
|
|
10
|
+
Wheneverd::Systemd::Systemctl.run("stop", *units)
|
|
11
|
+
Wheneverd::Systemd::Systemctl.run("disable", *units)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
units.each { |unit| puts unit }
|
|
14
14
|
0
|
|
15
15
|
rescue StandardError => e
|
|
16
16
|
handle_error(e)
|
data/lib/wheneverd/cli/reload.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Wheneverd
|
|
4
|
-
# Implements `wheneverd reload` (write units, reload daemon, restart timers).
|
|
4
|
+
# Implements `wheneverd reload` (write units, reload daemon, restart timers/services).
|
|
5
5
|
class CLI::Reload < CLI
|
|
6
6
|
option "--[no-]prune", :flag,
|
|
7
7
|
"Prune previously generated units for the identifier (default: enabled)",
|
|
8
8
|
default: true
|
|
9
9
|
|
|
10
10
|
def execute
|
|
11
|
-
paths,
|
|
12
|
-
return 0 if
|
|
11
|
+
paths, units = write_units_and_activatable_basenames
|
|
12
|
+
return 0 if units.empty?
|
|
13
13
|
|
|
14
|
-
reload_systemd(
|
|
14
|
+
reload_systemd(units)
|
|
15
15
|
|
|
16
16
|
paths.each { |path| puts path }
|
|
17
17
|
0
|
|
@@ -21,7 +21,7 @@ module Wheneverd
|
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
-
def
|
|
24
|
+
def write_units_and_activatable_basenames
|
|
25
25
|
units = render_units
|
|
26
26
|
paths = Wheneverd::Systemd::UnitWriter.write(
|
|
27
27
|
units,
|
|
@@ -29,12 +29,12 @@ module Wheneverd
|
|
|
29
29
|
prune: prune?,
|
|
30
30
|
identifier: identifier_value
|
|
31
31
|
)
|
|
32
|
-
[paths,
|
|
32
|
+
[paths, activatable_unit_basenames(units)]
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def reload_systemd(
|
|
35
|
+
def reload_systemd(units)
|
|
36
36
|
Wheneverd::Systemd::Systemctl.run("daemon-reload")
|
|
37
|
-
Wheneverd::Systemd::Systemctl.run("restart", *
|
|
37
|
+
Wheneverd::Systemd::Systemctl.run("restart", *units)
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
end
|
data/lib/wheneverd/cli/status.rb
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Wheneverd
|
|
4
|
-
# Implements `wheneverd status` (show installed timer status via `systemctl --user`).
|
|
4
|
+
# Implements `wheneverd status` (show installed timer/service status via `systemctl --user`).
|
|
5
5
|
class CLI::Status < CLI
|
|
6
6
|
def execute
|
|
7
|
-
timer_units =
|
|
8
|
-
|
|
7
|
+
timer_units, service_units = installed_unit_basenames
|
|
8
|
+
units = timer_units + service_units
|
|
9
|
+
return 0 if units.empty?
|
|
9
10
|
|
|
10
11
|
print_list_timers(timer_units)
|
|
11
|
-
print_status(
|
|
12
|
+
print_status(units)
|
|
12
13
|
0
|
|
13
14
|
rescue StandardError => e
|
|
14
15
|
handle_error(e)
|
|
@@ -17,12 +18,18 @@ module Wheneverd
|
|
|
17
18
|
private
|
|
18
19
|
|
|
19
20
|
# @return [Array<String>]
|
|
20
|
-
def
|
|
21
|
+
def installed_unit_basenames
|
|
21
22
|
paths = Wheneverd::Systemd::UnitLister.list(identifier: identifier_value, unit_dir: unit_dir)
|
|
22
|
-
paths.map { |path| File.basename(path) }
|
|
23
|
+
basenames = paths.map { |path| File.basename(path) }
|
|
24
|
+
timers = basenames.grep(/\.timer\z/).uniq
|
|
25
|
+
timer_managed_services = timers.map { |timer| timer.sub(/\.timer\z/, ".service") }
|
|
26
|
+
services = basenames.grep(/\.service\z/).uniq - timer_managed_services
|
|
27
|
+
[timers, services]
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
def print_list_timers(timer_units)
|
|
31
|
+
return if timer_units.empty?
|
|
32
|
+
|
|
26
33
|
stdout, = Wheneverd::Systemd::Systemctl.run("list-timers", "--all", *timer_units)
|
|
27
34
|
print stdout
|
|
28
35
|
end
|
data/lib/wheneverd/cli.rb
CHANGED
|
@@ -51,12 +51,14 @@ module Wheneverd
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# @param units [Array<Wheneverd::Systemd::Unit>]
|
|
54
|
-
# @return [Array<String>] timer unit basenames
|
|
55
|
-
def
|
|
56
|
-
units.select { |unit|
|
|
54
|
+
# @return [Array<String>] timer and standalone service unit basenames
|
|
55
|
+
def activatable_unit_basenames(units = render_units)
|
|
56
|
+
units.select { |unit| %i[timer service].include?(unit.activation) }
|
|
57
|
+
.map(&:path_basename)
|
|
58
|
+
.uniq
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
private :render_units, :
|
|
61
|
+
private :render_units, :activatable_unit_basenames
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
|
|
@@ -81,14 +83,14 @@ module Wheneverd
|
|
|
81
83
|
subcommand "help", "Show help", Wheneverd::CLI::Help
|
|
82
84
|
subcommand "init", "Create a schedule template", Wheneverd::CLI::Init
|
|
83
85
|
subcommand "show", "Render units to stdout", Wheneverd::CLI::Show
|
|
84
|
-
subcommand "status", "Show systemctl
|
|
86
|
+
subcommand "status", "Show systemctl status for this identifier", Wheneverd::CLI::Status
|
|
85
87
|
subcommand "diff", "Diff rendered units vs files on disk", Wheneverd::CLI::Diff
|
|
86
88
|
subcommand "validate", "Validate schedule via systemd-analyze", Wheneverd::CLI::Validate
|
|
87
89
|
subcommand "write", "Write units to disk", Wheneverd::CLI::Write
|
|
88
90
|
subcommand "delete", "Delete units from disk", Wheneverd::CLI::Delete
|
|
89
|
-
subcommand "activate", "Enable and start timers via systemctl --user", Wheneverd::CLI::Activate
|
|
90
|
-
subcommand "deactivate", "Stop and disable timers via systemctl --user", Wheneverd::CLI::Deactivate
|
|
91
|
-
subcommand "reload", "Write units, reload daemon, restart timers", Wheneverd::CLI::Reload
|
|
91
|
+
subcommand "activate", "Enable and start timers/services via systemctl --user", Wheneverd::CLI::Activate
|
|
92
|
+
subcommand "deactivate", "Stop and disable timers/services via systemctl --user", Wheneverd::CLI::Deactivate
|
|
93
|
+
subcommand "reload", "Write units, reload daemon, restart timers/services", Wheneverd::CLI::Reload
|
|
92
94
|
subcommand "current", "Show installed units from disk", Wheneverd::CLI::Current
|
|
93
95
|
subcommand "linger", "Manage systemd user lingering via loginctl", Wheneverd::CLI::Linger
|
|
94
96
|
end
|
|
@@ -75,6 +75,37 @@ module Wheneverd
|
|
|
75
75
|
command([shell_executable, "-lc", script_stripped])
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
# Add a long-running systemd user service to the schedule.
|
|
79
|
+
#
|
|
80
|
+
# @example Shell service
|
|
81
|
+
# service "worker", shell: "bundle exec bin/worker"
|
|
82
|
+
#
|
|
83
|
+
# @example Argv service
|
|
84
|
+
# service "worker", command: ["bundle", "exec", "bin/worker"]
|
|
85
|
+
#
|
|
86
|
+
# @param name [String] stable service name within the schedule
|
|
87
|
+
# @param command [String, Array<String>, nil]
|
|
88
|
+
# @param shell [String, nil] shell script to run via /bin/bash -lc
|
|
89
|
+
# @param restart [String]
|
|
90
|
+
# @param restart_sec [String]
|
|
91
|
+
# @param service [Hash, Array<String>] extra [Service] lines
|
|
92
|
+
# @return [Wheneverd::Service]
|
|
93
|
+
def service(name, command: nil, shell: nil, restart: "always", restart_sec: "5s",
|
|
94
|
+
service: {})
|
|
95
|
+
command_value = normalize_service_command(command: command, shell: shell)
|
|
96
|
+
service_obj = Wheneverd::Service.new(
|
|
97
|
+
name: name,
|
|
98
|
+
command: command_value,
|
|
99
|
+
restart: restart,
|
|
100
|
+
restart_sec: restart_sec,
|
|
101
|
+
service: service
|
|
102
|
+
)
|
|
103
|
+
schedule.add_service(service_obj)
|
|
104
|
+
service_obj
|
|
105
|
+
rescue Wheneverd::InvalidCommandError => e
|
|
106
|
+
raise LoadError.new(e.message, path: path)
|
|
107
|
+
end
|
|
108
|
+
|
|
78
109
|
private
|
|
79
110
|
|
|
80
111
|
def ensure_in_every_block!(name)
|
|
@@ -102,6 +133,21 @@ module Wheneverd
|
|
|
102
133
|
stripped
|
|
103
134
|
end
|
|
104
135
|
|
|
136
|
+
def normalize_service_command(command:, shell:)
|
|
137
|
+
if command && shell
|
|
138
|
+
raise LoadError.new("service() accepts command: or shell:, not both", path: path)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
return command if command
|
|
142
|
+
|
|
143
|
+
if shell
|
|
144
|
+
script = normalize_shell_script(shell)
|
|
145
|
+
return ["/bin/bash", "-lc", script]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
raise LoadError.new("service() requires command: or shell:", path: path)
|
|
149
|
+
end
|
|
150
|
+
|
|
105
151
|
def with_current_entry(entry)
|
|
106
152
|
previous_entry = @current_entry
|
|
107
153
|
@current_entry = entry
|