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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/.rubocop.yml +41 -0
  4. data/.yardopts +8 -0
  5. data/AGENTS.md +42 -0
  6. data/CHANGELOG.md +28 -0
  7. data/FEATURE_SUMMARY.md +38 -0
  8. data/Gemfile +16 -0
  9. data/Gemfile.lock +129 -0
  10. data/LICENSE +21 -0
  11. data/README.md +204 -0
  12. data/Rakefile +196 -0
  13. data/bin/console +8 -0
  14. data/bin/setup +5 -0
  15. data/exe/wheneverd +9 -0
  16. data/lib/wheneverd/cli/activate.rb +19 -0
  17. data/lib/wheneverd/cli/current.rb +22 -0
  18. data/lib/wheneverd/cli/deactivate.rb +19 -0
  19. data/lib/wheneverd/cli/delete.rb +20 -0
  20. data/lib/wheneverd/cli/help.rb +18 -0
  21. data/lib/wheneverd/cli/init.rb +78 -0
  22. data/lib/wheneverd/cli/reload.rb +40 -0
  23. data/lib/wheneverd/cli/show.rb +23 -0
  24. data/lib/wheneverd/cli/write.rb +32 -0
  25. data/lib/wheneverd/cli.rb +87 -0
  26. data/lib/wheneverd/core_ext/numeric_duration.rb +56 -0
  27. data/lib/wheneverd/dsl/at_normalizer.rb +48 -0
  28. data/lib/wheneverd/dsl/calendar_symbol_period_list.rb +42 -0
  29. data/lib/wheneverd/dsl/context.rb +72 -0
  30. data/lib/wheneverd/dsl/errors.rb +29 -0
  31. data/lib/wheneverd/dsl/loader.rb +49 -0
  32. data/lib/wheneverd/dsl/period_parser.rb +135 -0
  33. data/lib/wheneverd/duration.rb +27 -0
  34. data/lib/wheneverd/entry.rb +31 -0
  35. data/lib/wheneverd/errors.rb +9 -0
  36. data/lib/wheneverd/interval.rb +37 -0
  37. data/lib/wheneverd/job/command.rb +29 -0
  38. data/lib/wheneverd/schedule.rb +25 -0
  39. data/lib/wheneverd/systemd/calendar_spec.rb +109 -0
  40. data/lib/wheneverd/systemd/cron_parser.rb +352 -0
  41. data/lib/wheneverd/systemd/errors.rb +23 -0
  42. data/lib/wheneverd/systemd/renderer.rb +153 -0
  43. data/lib/wheneverd/systemd/systemctl.rb +38 -0
  44. data/lib/wheneverd/systemd/time_parser.rb +75 -0
  45. data/lib/wheneverd/systemd/unit_deleter.rb +64 -0
  46. data/lib/wheneverd/systemd/unit_lister.rb +59 -0
  47. data/lib/wheneverd/systemd/unit_namer.rb +69 -0
  48. data/lib/wheneverd/systemd/unit_writer.rb +132 -0
  49. data/lib/wheneverd/trigger/boot.rb +26 -0
  50. data/lib/wheneverd/trigger/calendar.rb +26 -0
  51. data/lib/wheneverd/trigger/interval.rb +30 -0
  52. data/lib/wheneverd/version.rb +6 -0
  53. data/lib/wheneverd.rb +41 -0
  54. data/test/cli_activate_test.rb +110 -0
  55. data/test/cli_current_test.rb +94 -0
  56. data/test/cli_deactivate_test.rb +111 -0
  57. data/test/cli_end_to_end_test.rb +98 -0
  58. data/test/cli_reload_test.rb +132 -0
  59. data/test/cli_systemctl_integration_test.rb +76 -0
  60. data/test/cli_systemd_analyze_test.rb +64 -0
  61. data/test/cli_test.rb +332 -0
  62. data/test/domain_model_test.rb +108 -0
  63. data/test/dsl_calendar_symbol_period_list_test.rb +53 -0
  64. data/test/dsl_loader_test.rb +384 -0
  65. data/test/support/cli_subprocess_test_helpers.rb +38 -0
  66. data/test/support/cli_test_helpers.rb +114 -0
  67. data/test/systemd_calendar_spec_test.rb +45 -0
  68. data/test/systemd_cron_parser_test.rb +114 -0
  69. data/test/systemd_renderer_errors_test.rb +85 -0
  70. data/test/systemd_renderer_test.rb +161 -0
  71. data/test/systemd_systemctl_test.rb +46 -0
  72. data/test/systemd_time_parser_test.rb +25 -0
  73. data/test/systemd_unit_deleter_test.rb +83 -0
  74. data/test/systemd_unit_writer_prune_test.rb +85 -0
  75. data/test/systemd_unit_writer_test.rb +71 -0
  76. data/test/test_helper.rb +34 -0
  77. data/test/wheneverd_test.rb +9 -0
  78. data/wheneverd.gemspec +35 -0
  79. 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
@@ -0,0 +1,8 @@
1
+ --quiet
2
+ --markup markdown
3
+ --markup-provider redcarpet
4
+ --output-dir doc
5
+ --title "wheneverd"
6
+ --readme README.md
7
+ --files LICENSE,CHANGELOG.md,FEATURE_SUMMARY.md
8
+ lib/**/*.rb
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).
@@ -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/`).