wheneverd 0.1.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 +7 -0
- data/.gitignore +26 -0
- data/.rubocop.yml +41 -0
- data/.yardopts +8 -0
- data/AGENTS.md +42 -0
- data/CHANGELOG.md +28 -0
- data/FEATURE_SUMMARY.md +38 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +129 -0
- data/LICENSE +21 -0
- data/README.md +204 -0
- data/Rakefile +196 -0
- data/bin/console +8 -0
- data/bin/setup +5 -0
- data/exe/wheneverd +9 -0
- data/lib/wheneverd/cli/activate.rb +19 -0
- data/lib/wheneverd/cli/current.rb +22 -0
- data/lib/wheneverd/cli/deactivate.rb +19 -0
- data/lib/wheneverd/cli/delete.rb +20 -0
- data/lib/wheneverd/cli/help.rb +18 -0
- data/lib/wheneverd/cli/init.rb +78 -0
- data/lib/wheneverd/cli/reload.rb +40 -0
- data/lib/wheneverd/cli/show.rb +23 -0
- data/lib/wheneverd/cli/write.rb +32 -0
- data/lib/wheneverd/cli.rb +87 -0
- data/lib/wheneverd/core_ext/numeric_duration.rb +56 -0
- data/lib/wheneverd/dsl/at_normalizer.rb +48 -0
- data/lib/wheneverd/dsl/calendar_symbol_period_list.rb +42 -0
- data/lib/wheneverd/dsl/context.rb +72 -0
- data/lib/wheneverd/dsl/errors.rb +29 -0
- data/lib/wheneverd/dsl/loader.rb +49 -0
- data/lib/wheneverd/dsl/period_parser.rb +135 -0
- data/lib/wheneverd/duration.rb +27 -0
- data/lib/wheneverd/entry.rb +31 -0
- data/lib/wheneverd/errors.rb +9 -0
- data/lib/wheneverd/interval.rb +37 -0
- data/lib/wheneverd/job/command.rb +29 -0
- data/lib/wheneverd/schedule.rb +25 -0
- data/lib/wheneverd/systemd/calendar_spec.rb +109 -0
- data/lib/wheneverd/systemd/cron_parser.rb +352 -0
- data/lib/wheneverd/systemd/errors.rb +23 -0
- data/lib/wheneverd/systemd/renderer.rb +153 -0
- data/lib/wheneverd/systemd/systemctl.rb +38 -0
- data/lib/wheneverd/systemd/time_parser.rb +75 -0
- data/lib/wheneverd/systemd/unit_deleter.rb +64 -0
- data/lib/wheneverd/systemd/unit_lister.rb +59 -0
- data/lib/wheneverd/systemd/unit_namer.rb +69 -0
- data/lib/wheneverd/systemd/unit_writer.rb +132 -0
- data/lib/wheneverd/trigger/boot.rb +26 -0
- data/lib/wheneverd/trigger/calendar.rb +26 -0
- data/lib/wheneverd/trigger/interval.rb +30 -0
- data/lib/wheneverd/version.rb +6 -0
- data/lib/wheneverd.rb +41 -0
- data/test/cli_activate_test.rb +110 -0
- data/test/cli_current_test.rb +94 -0
- data/test/cli_deactivate_test.rb +111 -0
- data/test/cli_end_to_end_test.rb +98 -0
- data/test/cli_reload_test.rb +132 -0
- data/test/cli_systemctl_integration_test.rb +76 -0
- data/test/cli_systemd_analyze_test.rb +64 -0
- data/test/cli_test.rb +332 -0
- data/test/domain_model_test.rb +108 -0
- data/test/dsl_calendar_symbol_period_list_test.rb +53 -0
- data/test/dsl_loader_test.rb +384 -0
- data/test/support/cli_subprocess_test_helpers.rb +38 -0
- data/test/support/cli_test_helpers.rb +114 -0
- data/test/systemd_calendar_spec_test.rb +45 -0
- data/test/systemd_cron_parser_test.rb +114 -0
- data/test/systemd_renderer_errors_test.rb +85 -0
- data/test/systemd_renderer_test.rb +161 -0
- data/test/systemd_systemctl_test.rb +46 -0
- data/test/systemd_time_parser_test.rb +25 -0
- data/test/systemd_unit_deleter_test.rb +83 -0
- data/test/systemd_unit_writer_prune_test.rb +85 -0
- data/test/systemd_unit_writer_test.rb +71 -0
- data/test/test_helper.rb +34 -0
- data/test/wheneverd_test.rb +9 -0
- data/wheneverd.gemspec +35 -0
- metadata +136 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f9af22418de7ea3db50ee8a05d5614f172154a7d6b1b750017594b6202aa5298
|
|
4
|
+
data.tar.gz: 77c113c6dc437c94f59ef019cd0c274ecd94c9af2463fcfea712bbd87b109387
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2f27bd8f52c7d9e111d8b3cdcb1170b1f584f30db2e601c3938b67db29d2d1ca433e419c3020dbe01636e7e62d1798a987173deb7d1d8c3f14be91f5abf0f203
|
|
7
|
+
data.tar.gz: 6cbacc2cc72b97b1dee8dd196c1bb332c47c8a0da0cd9e01473abf8cb799b96a6a9d2c3484d7294ae5b70fc392a6287c1ba55a6ad364547ce59387d7224c82a1
|
data/.gitignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.bundle/
|
|
2
|
+
.yardoc/
|
|
3
|
+
/.yardoc/
|
|
4
|
+
/coverage/
|
|
5
|
+
/doc/
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/tmp/
|
|
9
|
+
.rubocop_cache/
|
|
10
|
+
|
|
11
|
+
# Ruby
|
|
12
|
+
*.gem
|
|
13
|
+
*.rbc
|
|
14
|
+
*.bundle
|
|
15
|
+
*.so
|
|
16
|
+
*.o
|
|
17
|
+
*.a
|
|
18
|
+
*.log
|
|
19
|
+
|
|
20
|
+
# Bundler
|
|
21
|
+
/vendor/bundle
|
|
22
|
+
|
|
23
|
+
# Editors/OS
|
|
24
|
+
.DS_Store
|
|
25
|
+
.idea/
|
|
26
|
+
.vscode/
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.7
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
CacheRootDirectory: .rubocop_cache
|
|
6
|
+
Exclude:
|
|
7
|
+
- "vendor/**/*"
|
|
8
|
+
- "pkg/**/*"
|
|
9
|
+
- "tmp/**/*"
|
|
10
|
+
|
|
11
|
+
Layout/LineLength:
|
|
12
|
+
Max: 100
|
|
13
|
+
|
|
14
|
+
Style/Documentation:
|
|
15
|
+
Enabled: false
|
|
16
|
+
|
|
17
|
+
Style/StringLiterals:
|
|
18
|
+
EnforcedStyle: double_quotes
|
|
19
|
+
|
|
20
|
+
Metrics:
|
|
21
|
+
Enabled: true
|
|
22
|
+
|
|
23
|
+
Metrics/AbcSize:
|
|
24
|
+
Exclude:
|
|
25
|
+
- "lib/wheneverd/systemd/cron_parser.rb"
|
|
26
|
+
|
|
27
|
+
Metrics/CyclomaticComplexity:
|
|
28
|
+
Exclude:
|
|
29
|
+
- "lib/wheneverd/systemd/cron_parser.rb"
|
|
30
|
+
|
|
31
|
+
Metrics/MethodLength:
|
|
32
|
+
Exclude:
|
|
33
|
+
- "lib/wheneverd/systemd/cron_parser.rb"
|
|
34
|
+
|
|
35
|
+
Metrics/ModuleLength:
|
|
36
|
+
Exclude:
|
|
37
|
+
- "lib/wheneverd/systemd/cron_parser.rb"
|
|
38
|
+
|
|
39
|
+
Metrics/PerceivedComplexity:
|
|
40
|
+
Exclude:
|
|
41
|
+
- "lib/wheneverd/systemd/cron_parser.rb"
|
data/.yardopts
ADDED
data/AGENTS.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Agent Notes (wheneverd)
|
|
2
|
+
|
|
3
|
+
## Project
|
|
4
|
+
|
|
5
|
+
`wheneverd` is a Ruby gem that aims to generate `systemd` timer/service units from a Ruby DSL (analogous to the `whenever` gem for cron). The current state is an early scaffold with a placeholder CLI; systemd generation is not implemented yet.
|
|
6
|
+
|
|
7
|
+
Primary docs:
|
|
8
|
+
- `README.md` for usage and dev setup.
|
|
9
|
+
- `FEATURE_SUMMARY.md` for user-visible behavior changes.
|
|
10
|
+
- `CHANGELOG.md` for versioned release notes.
|
|
11
|
+
|
|
12
|
+
Documentation hygiene:
|
|
13
|
+
- After completing a prompt that changes user-visible behavior or developer workflow, update `README.md`, `FEATURE_SUMMARY.md`, and `CHANGELOG.md` as needed.
|
|
14
|
+
- Before committing any change, run `bundle exec rake ci`; it must complete with no errors (fix failures first).
|
|
15
|
+
- When implementing or changing behavior, add/adjust tests to tighten the implementation and prevent regressions.
|
|
16
|
+
- When committing, commit only the changes from the current session: stage explicit files (or use `git add -p`) and avoid committing unrelated modified files.
|
|
17
|
+
|
|
18
|
+
## Repo Layout
|
|
19
|
+
|
|
20
|
+
- `lib/wheneverd.rb`: gem entrypoint.
|
|
21
|
+
- `lib/wheneverd/cli.rb`: Clamp-based CLI (currently prints help / version only).
|
|
22
|
+
- `lib/wheneverd/version.rb`: gem version.
|
|
23
|
+
- `exe/wheneverd`: executable entrypoint.
|
|
24
|
+
- `test/`: Minitest suite (with SimpleCov configured in `test/test_helper.rb`).
|
|
25
|
+
- `Rakefile`: CI-like tasks for lint + tests.
|
|
26
|
+
|
|
27
|
+
## Common Commands
|
|
28
|
+
|
|
29
|
+
- Install deps: `bundle install`
|
|
30
|
+
- Run tests: `bundle exec rake test`
|
|
31
|
+
- Run RuboCop (autocorrect): `bundle exec rake ci:rubocop`
|
|
32
|
+
- Run lint + tests (default): `bundle exec rake ci`
|
|
33
|
+
|
|
34
|
+
Notes:
|
|
35
|
+
- `bundle exec rake ci` runs `rubocop -A .` (it may modify files) and then runs tests.
|
|
36
|
+
- Tests write coverage output to `coverage/`. Coverage threshold defaults to 100% and can be set via `MINIMUM_COVERAGE` (e.g. `MINIMUM_COVERAGE=95 bundle exec rake test`).
|
|
37
|
+
|
|
38
|
+
## Conventions
|
|
39
|
+
|
|
40
|
+
- Ruby target is 2.7 (`.rubocop.yml` / gemspec).
|
|
41
|
+
- Prefer double-quoted strings and keep lines <= 100 chars (RuboCop).
|
|
42
|
+
- Add/update `FEATURE_SUMMARY.md` only for user-visible changes (CLI UX, defaults, generated output, breaking changes).
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
This project keeps all changes in `## Unreleased` until they are released.
|
|
4
|
+
On release, entries are moved into `## x.y.z` sections that match the gem version.
|
|
5
|
+
|
|
6
|
+
## Unreleased
|
|
7
|
+
|
|
8
|
+
- No changes yet.
|
|
9
|
+
|
|
10
|
+
## 0.1.0
|
|
11
|
+
|
|
12
|
+
- Provides a Clamp-based `wheneverd` CLI with `--help`, `--version`, and `--verbose` (usage errors in `ERROR: ...` format).
|
|
13
|
+
- Adds core domain objects and helpers for building schedules (interval parsing, durations, triggers, entries, jobs).
|
|
14
|
+
- Adds a Ruby DSL loader (`Wheneverd::DSL::Loader.load_file`) supporting `every(...)` blocks with `command(...)` jobs.
|
|
15
|
+
- Schedule DSL: `every` accepts multiple calendar period symbols in one block (e.g. `every :tuesday, :wednesday`).
|
|
16
|
+
- Cron strings: supports standard 5-field crontab syntax (including month/day-of-week and steps); expressions that require
|
|
17
|
+
cron day-of-month vs day-of-week OR semantics may expand into multiple `OnCalendar=` lines.
|
|
18
|
+
- Adds systemd unit rendering (`Wheneverd::Systemd::Renderer.render`) for interval and calendar triggers.
|
|
19
|
+
- Systemd: unit basenames use a stable ID derived from the job’s trigger + command (reordering schedule blocks won’t rename units).
|
|
20
|
+
- Interval timers now include both `OnActiveSec=` and `OnUnitActiveSec=` so newly enabled timers have a next run scheduled.
|
|
21
|
+
- Adds helpers to write and delete generated unit files (`Wheneverd::Systemd::UnitWriter`/`UnitDeleter`).
|
|
22
|
+
- Adds CLI subcommands: `init`, `show`, `write`, `delete`, `activate`, `deactivate`, `reload`, and `current`.
|
|
23
|
+
- `wheneverd init` prints whether it wrote or overwrote the schedule template.
|
|
24
|
+
- Schedule DSL supports `every :reboot` as a boot trigger (rendered as `OnBootSec=1`).
|
|
25
|
+
- Developer: `rake ci` runs with Bundler setup (so it works without `bundle exec` after `bundle install`).
|
|
26
|
+
- Developer: adds YARD tasks (`rake yard` / `rake doc`) and inline API documentation.
|
|
27
|
+
- CLI: `delete` / `current` only operate on units matching the identifier and generated marker.
|
|
28
|
+
- CLI: `write` / `reload` prune previously generated units for the identifier by default (use `--no-prune` to disable pruning).
|
data/FEATURE_SUMMARY.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Feature Summary
|
|
2
|
+
|
|
3
|
+
This file tracks the most important *user-visible* behavior changes in `wheneverd`.
|
|
4
|
+
It complements [`CHANGELOG.md`](CHANGELOG.md) by staying high-level and focusing on what users will notice.
|
|
5
|
+
|
|
6
|
+
## How to update
|
|
7
|
+
|
|
8
|
+
- Update `## Unreleased` in commits that change user-facing behavior (CLI UX, defaults, generated output, breaking changes).
|
|
9
|
+
- Skip internal refactors, tests, and docs-only tweaks unless they affect users.
|
|
10
|
+
- Keep entries short, written from the user’s point of view.
|
|
11
|
+
- On release, move items from `## Unreleased` into a new `## x.y.z` section that matches the gem version.
|
|
12
|
+
|
|
13
|
+
## Unreleased
|
|
14
|
+
|
|
15
|
+
- No changes yet.
|
|
16
|
+
|
|
17
|
+
## 0.1.0
|
|
18
|
+
|
|
19
|
+
- The `wheneverd` CLI is implemented using Clamp (`--help`, usage errors in `ERROR: ...` format, `--verbose` for details).
|
|
20
|
+
- The gem includes a small “whenever-like” domain model (interval parsing, durations, triggers, schedules).
|
|
21
|
+
- 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).
|
|
23
|
+
- Schedule DSL supports multiple calendar period symbols per `every` block (e.g. `every :tuesday, :wednesday`).
|
|
24
|
+
- Supported `every` periods include interval strings/durations, calendar shortcuts (`:hour`, `:day`, `:month`, `:year`),
|
|
25
|
+
day selectors (`:monday..:sunday`, `:weekday`, `:weekend`), and standard 5-field cron strings.
|
|
26
|
+
- `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
|
+
- The gem can render systemd `.service` and `.timer` units via `Wheneverd::Systemd::Renderer.render`.
|
|
29
|
+
- Generated unit basenames include a stable ID derived from the job’s trigger + command (reordering schedule blocks won’t rename units).
|
|
30
|
+
- Interval timers include both `OnActiveSec=` and `OnUnitActiveSec=` to ensure a newly started timer has a next run scheduled.
|
|
31
|
+
- The gem can write/delete generated unit files via `Wheneverd::Systemd::UnitWriter` and `Wheneverd::Systemd::UnitDeleter`.
|
|
32
|
+
- The `wheneverd` CLI supports `init`, `show`, `write`, `delete`, `activate`, `deactivate`, and `reload` for working with
|
|
33
|
+
schedule files, unit directories, and managing user timers via `systemctl --user`.
|
|
34
|
+
- `wheneverd write` / `wheneverd reload` prune previously generated units for the identifier by default (use `--no-prune` to disable pruning).
|
|
35
|
+
- The `wheneverd current` command prints the currently installed unit file contents from disk for an identifier.
|
|
36
|
+
- The `wheneverd delete` / `wheneverd current` commands only operate on units matching the identifier and generated marker.
|
|
37
|
+
- `wheneverd init` prints whether it wrote or overwrote the schedule template.
|
|
38
|
+
- Schedule DSL supports `every :reboot` as a boot trigger (rendered as `OnBootSec=1`).
|
data/Gemfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gemspec
|
|
6
|
+
|
|
7
|
+
group :development, :test do
|
|
8
|
+
gem "irb", require: false
|
|
9
|
+
gem "minitest"
|
|
10
|
+
gem "rake"
|
|
11
|
+
gem "rdoc", require: false
|
|
12
|
+
gem "redcarpet", require: false
|
|
13
|
+
gem "rubocop", "~> 1.0", require: false
|
|
14
|
+
gem "simplecov", require: false
|
|
15
|
+
gem "yard", require: false
|
|
16
|
+
end
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
wheneverd (0.1.0)
|
|
5
|
+
clamp (~> 1.3)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
ast (2.4.3)
|
|
11
|
+
clamp (1.3.2)
|
|
12
|
+
date (3.5.1)
|
|
13
|
+
docile (1.4.1)
|
|
14
|
+
erb (6.0.1)
|
|
15
|
+
io-console (0.8.2)
|
|
16
|
+
irb (1.16.0)
|
|
17
|
+
pp (>= 0.6.0)
|
|
18
|
+
rdoc (>= 4.0.0)
|
|
19
|
+
reline (>= 0.4.2)
|
|
20
|
+
json (2.18.0)
|
|
21
|
+
language_server-protocol (3.17.0.5)
|
|
22
|
+
lint_roller (1.1.0)
|
|
23
|
+
minitest (6.0.1)
|
|
24
|
+
prism (~> 1.5)
|
|
25
|
+
parallel (1.27.0)
|
|
26
|
+
parser (3.3.10.0)
|
|
27
|
+
ast (~> 2.4.1)
|
|
28
|
+
racc
|
|
29
|
+
pp (0.6.3)
|
|
30
|
+
prettyprint
|
|
31
|
+
prettyprint (0.2.0)
|
|
32
|
+
prism (1.7.0)
|
|
33
|
+
psych (5.3.1)
|
|
34
|
+
date
|
|
35
|
+
stringio
|
|
36
|
+
racc (1.8.1)
|
|
37
|
+
rainbow (3.1.1)
|
|
38
|
+
rake (13.3.1)
|
|
39
|
+
rdoc (7.1.0)
|
|
40
|
+
erb
|
|
41
|
+
psych (>= 4.0.0)
|
|
42
|
+
tsort
|
|
43
|
+
redcarpet (3.6.1)
|
|
44
|
+
regexp_parser (2.11.3)
|
|
45
|
+
reline (0.6.3)
|
|
46
|
+
io-console (~> 0.5)
|
|
47
|
+
rubocop (1.82.1)
|
|
48
|
+
json (~> 2.3)
|
|
49
|
+
language_server-protocol (~> 3.17.0.2)
|
|
50
|
+
lint_roller (~> 1.1.0)
|
|
51
|
+
parallel (~> 1.10)
|
|
52
|
+
parser (>= 3.3.0.2)
|
|
53
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
54
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
55
|
+
rubocop-ast (>= 1.48.0, < 2.0)
|
|
56
|
+
ruby-progressbar (~> 1.7)
|
|
57
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
58
|
+
rubocop-ast (1.49.0)
|
|
59
|
+
parser (>= 3.3.7.2)
|
|
60
|
+
prism (~> 1.7)
|
|
61
|
+
ruby-progressbar (1.13.0)
|
|
62
|
+
simplecov (0.22.0)
|
|
63
|
+
docile (~> 1.1)
|
|
64
|
+
simplecov-html (~> 0.11)
|
|
65
|
+
simplecov_json_formatter (~> 0.1)
|
|
66
|
+
simplecov-html (0.13.2)
|
|
67
|
+
simplecov_json_formatter (0.1.4)
|
|
68
|
+
stringio (3.2.0)
|
|
69
|
+
tsort (0.2.0)
|
|
70
|
+
unicode-display_width (3.2.0)
|
|
71
|
+
unicode-emoji (~> 4.1)
|
|
72
|
+
unicode-emoji (4.2.0)
|
|
73
|
+
yard (0.9.38)
|
|
74
|
+
|
|
75
|
+
PLATFORMS
|
|
76
|
+
ruby
|
|
77
|
+
x86_64-linux
|
|
78
|
+
|
|
79
|
+
DEPENDENCIES
|
|
80
|
+
irb
|
|
81
|
+
minitest
|
|
82
|
+
rake
|
|
83
|
+
rdoc
|
|
84
|
+
redcarpet
|
|
85
|
+
rubocop (~> 1.0)
|
|
86
|
+
simplecov
|
|
87
|
+
wheneverd!
|
|
88
|
+
yard
|
|
89
|
+
|
|
90
|
+
CHECKSUMS
|
|
91
|
+
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
92
|
+
clamp (1.3.2) sha256=4f6a99a8678d51abbf1650263a74d1ac50939edc11986271431d2e03a0d7a022
|
|
93
|
+
date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
|
|
94
|
+
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
|
|
95
|
+
erb (6.0.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5
|
|
96
|
+
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
97
|
+
irb (1.16.0) sha256=2abe56c9ac947cdcb2f150572904ba798c1e93c890c256f8429981a7675b0806
|
|
98
|
+
json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505
|
|
99
|
+
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
100
|
+
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
101
|
+
minitest (6.0.1) sha256=7854c74f48e2e975969062833adc4013f249a4b212f5e7b9d5c040bf838d54bb
|
|
102
|
+
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
|
|
103
|
+
parser (3.3.10.0) sha256=ce3587fa5cc55a88c4ba5b2b37621b3329aadf5728f9eafa36bbd121462aabd6
|
|
104
|
+
pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
|
|
105
|
+
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
|
106
|
+
prism (1.7.0) sha256=10062f734bf7985c8424c44fac382ac04a58124ea3d220ec3ba9fe4f2da65103
|
|
107
|
+
psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
|
|
108
|
+
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
109
|
+
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
110
|
+
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
|
|
111
|
+
rdoc (7.1.0) sha256=494899df0706c178596ca6e1d50f1b7eb285a9b2aae715be5abd742734f17363
|
|
112
|
+
redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445
|
|
113
|
+
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
|
|
114
|
+
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
115
|
+
rubocop (1.82.1) sha256=09f1a6a654a960eda767aebea33e47603080f8e9c9a3f019bf9b94c9cab5e273
|
|
116
|
+
rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
|
|
117
|
+
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
118
|
+
simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5
|
|
119
|
+
simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246
|
|
120
|
+
simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
|
|
121
|
+
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
|
|
122
|
+
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
123
|
+
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
|
|
124
|
+
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
|
|
125
|
+
wheneverd (0.1.0)
|
|
126
|
+
yard (0.9.38) sha256=721fb82afb10532aa49860655f6cc2eaa7130889df291b052e1e6b268283010f
|
|
127
|
+
|
|
128
|
+
BUNDLED WITH
|
|
129
|
+
4.0.3
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Go
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# wheneverd
|
|
2
|
+
|
|
3
|
+
Wheneverd is to systemd timers what the [`whenever` gem](https://github.com/javan/whenever) is to cron.
|
|
4
|
+
|
|
5
|
+
Tagline / repo: `git@github.com:bigcurl/wheneverd.git`
|
|
6
|
+
|
|
7
|
+
## Status
|
|
8
|
+
|
|
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`.
|
|
10
|
+
|
|
11
|
+
Known limitations: `roles:` is accepted but not used for filtering yet.
|
|
12
|
+
|
|
13
|
+
See `FEATURE_SUMMARY.md` for high-level user-visible behavior, and `CHANGELOG.md` for release notes.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Add this line to your application's Gemfile:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem "wheneverd"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
And then execute:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bundle install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
wheneverd --help
|
|
33
|
+
wheneverd init
|
|
34
|
+
wheneverd show
|
|
35
|
+
wheneverd write
|
|
36
|
+
wheneverd delete
|
|
37
|
+
wheneverd activate
|
|
38
|
+
wheneverd deactivate
|
|
39
|
+
wheneverd reload
|
|
40
|
+
wheneverd current
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Minimal `config/schedule.rb` example
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# frozen_string_literal: true
|
|
47
|
+
|
|
48
|
+
every "5m" do
|
|
49
|
+
command "echo hello"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
every 1.day, at: "4:30 am" do
|
|
53
|
+
command "echo four_thirty"
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Preview the generated units:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
wheneverd show
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Activating / deactivating (systemd)
|
|
64
|
+
|
|
65
|
+
After `wheneverd write`, use `wheneverd activate` to enable + start the generated timer units (by default, user units
|
|
66
|
+
in `~/.config/systemd/user`):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
wheneverd activate
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Deactivate a timer:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
wheneverd deactivate
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
After changing your schedule, rewrite units and restart the timer(s) to pick up changes:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
wheneverd reload
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Syntax
|
|
85
|
+
|
|
86
|
+
Schedules are defined in a Ruby file (default: `config/schedule.rb`) and evaluated in a dedicated DSL context.
|
|
87
|
+
|
|
88
|
+
Note: schedule files are executed as Ruby. Do not run untrusted schedule code.
|
|
89
|
+
|
|
90
|
+
The core shape is:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
every(period, at: nil, roles: nil) do
|
|
94
|
+
command "echo hello"
|
|
95
|
+
end
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For calendar schedules, you can also pass multiple period symbols (or an array) to run the same jobs on multiple days:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
every :tuesday, :wednesday, at: "12pm" do
|
|
102
|
+
command "echo midweek"
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `command`
|
|
107
|
+
|
|
108
|
+
`command("...")` appends a oneshot `ExecStart=` job. Commands must be non-empty strings.
|
|
109
|
+
|
|
110
|
+
The command string is inserted into `ExecStart=` as-is (no shell wrapping). If you need shell features
|
|
111
|
+
(pipes, redirects, globbing, env var expansion), wrap it yourself, for example:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
command "/bin/bash -lc 'echo hello | sed -e s/hello/hi/'"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `every` periods
|
|
118
|
+
|
|
119
|
+
Supported `period` forms:
|
|
120
|
+
|
|
121
|
+
- Interval strings: `"<n>s|m|h|d|w"` (examples: `"5m"`, `"1h"`) for monotonic timers (`OnActiveSec=` + `OnUnitActiveSec=`).
|
|
122
|
+
- Duration objects: `1.second`, `1.minute`, `1.hour`, `1.day`, `1.week` (and plurals), using the same interval semantics.
|
|
123
|
+
- Symbol shortcuts:
|
|
124
|
+
- `:hour`, `:day`, `:month`, `:year` (calendar schedules, mapped to `hourly`, `daily`, `monthly`, `yearly`)
|
|
125
|
+
- `:reboot` (boot trigger, mapped to `OnBootSec=1`).
|
|
126
|
+
- Day selectors: `:monday..:sunday`, plus `:weekday` and `:weekend` (calendar schedules; multiple day symbols supported).
|
|
127
|
+
- Cron strings (5 fields), like `"0 0 27-31 * *"` (calendar schedules).
|
|
128
|
+
|
|
129
|
+
Notes:
|
|
130
|
+
|
|
131
|
+
- Interval/duration schedules are monotonic (run relative to last execution), while calendar schedules are wall-clock
|
|
132
|
+
based. In particular, `every 1.day` is monotonic, but `every :day` is calendar-based.
|
|
133
|
+
- `at:` is only supported with calendar periods. `every 1.day, at: ...` is supported as a convenience and is treated
|
|
134
|
+
as a daily calendar trigger.
|
|
135
|
+
- `at:` is not supported with `every :reboot`.
|
|
136
|
+
|
|
137
|
+
### `at:` times
|
|
138
|
+
|
|
139
|
+
`at:` may be a single string or an array of strings. Times are normalized at render time.
|
|
140
|
+
|
|
141
|
+
`at:` is not supported for interval strings (like `"5m"`) or cron strings.
|
|
142
|
+
|
|
143
|
+
Accepted examples:
|
|
144
|
+
|
|
145
|
+
- `"4:30 am"`, `"6:00 pm"`, `"12pm"`
|
|
146
|
+
- `"00:15"` (24h)
|
|
147
|
+
|
|
148
|
+
### Cron strings
|
|
149
|
+
|
|
150
|
+
Cron translation supports standard 5-field crontab strings (`minute hour day-of-month month day-of-week`), including:
|
|
151
|
+
|
|
152
|
+
- Wildcards, lists, ranges, and steps (`*`, `1,2,3`, `1-5`, `*/15`, `1-10/2`)
|
|
153
|
+
- Month and day-of-week names (`Jan`, `Mon`)
|
|
154
|
+
- Cron day-of-month vs day-of-week OR semantics (may expand into multiple `OnCalendar=` lines)
|
|
155
|
+
|
|
156
|
+
Unsupported cron patterns raise an error at render time (e.g. non-5-field strings, `@daily`, `L`, `W`, `#`, `?`).
|
|
157
|
+
|
|
158
|
+
### `roles:`
|
|
159
|
+
|
|
160
|
+
`roles:` is accepted and stored on entries, but is ignored in v1 (no role-based filtering yet).
|
|
161
|
+
|
|
162
|
+
## CLI
|
|
163
|
+
|
|
164
|
+
Defaults:
|
|
165
|
+
|
|
166
|
+
- schedule path: `config/schedule.rb` (override with `--schedule PATH`)
|
|
167
|
+
- identifier: current directory name (override with `--identifier NAME`)
|
|
168
|
+
- unit dir: `~/.config/systemd/user` (override with `--unit-dir PATH`)
|
|
169
|
+
|
|
170
|
+
Notes:
|
|
171
|
+
|
|
172
|
+
- Errors use Clamp-style `ERROR: ...` formatting; add `--verbose` to include error details.
|
|
173
|
+
- `wheneverd delete` / `wheneverd current` only operate on units matching the identifier *and* the generated marker line.
|
|
174
|
+
- Identifiers are sanitized for use in unit file names (non-alphanumeric characters become `-`).
|
|
175
|
+
- Unit basenames include a stable ID derived from the job’s trigger + command (reordering schedule blocks won’t rename units).
|
|
176
|
+
- `wheneverd write` / `wheneverd reload` prune previously generated units for the identifier by default (use `--no-prune` to keep old units around).
|
|
177
|
+
|
|
178
|
+
Commands:
|
|
179
|
+
|
|
180
|
+
- `wheneverd init [--schedule PATH] [--force]` writes a template schedule file.
|
|
181
|
+
- `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.
|
|
184
|
+
- `wheneverd activate [--schedule PATH] [--identifier NAME]` runs `systemctl --user daemon-reload` and enables/starts the timers.
|
|
185
|
+
- `wheneverd deactivate [--schedule PATH] [--identifier NAME]` stops and disables the timers.
|
|
186
|
+
- `wheneverd reload [--schedule PATH] [--identifier NAME] [--unit-dir PATH] [--[no-]prune]` writes units, reloads systemd, and restarts timers.
|
|
187
|
+
- `wheneverd current [--identifier NAME] [--unit-dir PATH]` prints the currently installed unit file contents from disk.
|
|
188
|
+
|
|
189
|
+
## Development
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
bundle install
|
|
193
|
+
bundle exec rake test
|
|
194
|
+
bundle exec rake ci
|
|
195
|
+
bundle exec rake yard
|
|
196
|
+
|
|
197
|
+
# Also supported after `bundle install`:
|
|
198
|
+
rake ci
|
|
199
|
+
rake yard
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Test runs write a coverage report to `coverage/`.
|
|
203
|
+
|
|
204
|
+
YARD docs are written to `doc/` (and `.yardoc/`).
|