feat: add support for after-login scheduling across multiple platforms#654
feat: add support for after-login scheduling across multiple platforms#654creativeprojects wants to merge 2 commits into
Conversation
- Implemented `@reboot` entries in crond for jobs that should run after system boot. - Introduced `RunAtLoad` option in launchd jobs to trigger tasks at user login. - Added `AfterLogin` configuration option in systemd to start timers when the user session begins. - Enhanced Windows Task Scheduler to support logon triggers for tasks. - Updated documentation to reflect the new after-login scheduling capabilities. - Added tests to ensure correct behavior for after-login configurations across different schedulers.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #654 +/- ##
==========================================
+ Coverage 80.96% 81.05% +0.09%
==========================================
Files 162 162
Lines 12122 12224 +102
==========================================
+ Hits 9814 9907 +93
- Misses 1810 1816 +6
- Partials 498 501 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. |
There was a problem hiding this comment.
Pull request overview
This PR adds a cross-platform “after login” scheduling trigger (schedule-after-login) and wires it through config parsing, scheduler backends (systemd, launchd, Windows Task Scheduler, crond), and documentation so a job can run at user session start (or best-effort equivalent).
Changes:
- Added
AfterLoginconfiguration and trigger detection (HasTriggers) so schedules can be event-based, not only calendar-based. - Implemented after-login mappings per scheduler: systemd user timers (
OnStartupSec=0), launchd (RunAtLoad), Windows Task Scheduler (logon trigger), and crond (@rebootbest-effort). - Added/updated tests and docs to cover after-login behavior across platforms.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| systemd/read.go | Detects after-login from OnStartupSec when reading timers. |
| systemd/read_test.go | Adds read-path coverage for OnStartupSec → AfterLogin. |
| systemd/generate.go | Emits OnStartupSec=0 when AfterLogin is enabled; plumbs flag through templates/config. |
| systemd/generate_test.go | Validates generated user timer content for after-login (including “only” case). |
| schtasks/trigger.go | Adds Task Scheduler LogonTrigger XML model. |
| schtasks/taskscheduler.go | Adds logon trigger creation when AfterLogin is enabled. |
| schtasks/task.go | Implements addLogonTrigger on the Task model. |
| schtasks/task_test.go | Adds unit tests asserting logon trigger XML emission. |
| schtasks/config.go | Adds AfterLogin to schtasks config. |
| schedule/permission.go | Adds shared validation: after-login requires user_logged_on. |
| schedule/permission_test.go | Tests the shared after-login permission validation. |
| schedule/handler_windows.go | Enforces after-login permission and passes AfterLogin into schtasks config. |
| schedule/handler_systemd.go | Enforces after-login permission and plumbs flag into systemd config + read-back mapping. |
| schedule/handler_darwin.go | Enforces after-login permission; maps to/from launchd RunAtLoad. |
| schedule/handler_darwin_test.go | Adds tests for RunAtLoad mapping and permission rejection. |
| schedule/handler_crond.go | Adds @reboot entry generation and parses it back into AfterLogin. |
| schedule/handler_crond_test.go | Tests crond after-login creation/read-back and permission rejection. |
| schedule/config.go | Adds AfterLogin to internal schedule job config. |
| schedule_jobs.go | Treats “no schedules and no triggers” as removal-only; maps AfterLogin into job config. |
| docs/content/schedules/task_scheduler/index.md | Documents Task Scheduler after-login usage/limitations. |
| docs/content/schedules/systemd.md | Documents systemd after-login and template variable usage. |
| docs/content/schedules/launchd.md | Documents launchd after-login behavior and caveats. |
| docs/content/schedules/cron.md | Documents cron’s best-effort @reboot behavior for after-login. |
| docs/content/schedules/configuration.md | Documents schedule-after-login and its per-scheduler mapping table. |
| darwin/launchd.go | Adds RunAtLoad field to LaunchdJob model. |
| crond/entry.go | Adds @reboot entry support and string rendering. |
| crond/entry_test.go | Tests @reboot entry formatting behaviors. |
| crond/crontab.go | Adds parsing support for @reboot resticprofile entries. |
| crond/crontab_test.go | Tests parsing/round-tripping @reboot entries. |
| config/schedule.go | Adds after-login and HasTriggers() to preserve trigger-only schedules. |
| config/schedule_after_login_test.go | Tests trigger detection + config resolution for after-login schedules. |
| config/profile.go | Adds override field and uses HasTriggers() in schedule resolution. |
| config/group.go | Uses HasTriggers() when resolving group schedules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Note: when the task runs as the system account, the logon trigger applies to any user logging on. | ||
|
|
||
| Note: the registered logon trigger is not reported back by `resticprofile status` because the underlying `schtasks /query` text output does not expose triggers; the task itself works normally. |
| if index := slices.IndexFunc(configs, func(cfg Config) bool { | ||
| return cfg.ProfileName == profileName && cfg.CommandName == commandName && cfg.ConfigFile == configFile | ||
| }); index >= 0 { | ||
| configs[index].Schedules = append(configs[index].Schedules, entry.Event().String()) | ||
| if entry.AtReboot() { | ||
| configs[index].AfterLogin = true | ||
| } else { | ||
| configs[index].Schedules = append(configs[index].Schedules, entry.Event().String()) | ||
| } |
| cfg := Config{ | ||
| ProfileName: profileName, | ||
| CommandName: commandName, | ||
| ConfigFile: configFile, | ||
| Schedules: []string{entry.Event().String()}, | ||
| Command: args[0], | ||
| Arguments: NewCommandArguments(args[1:]), | ||
| WorkingDirectory: entry.WorkDir(), | ||
| Permission: permission, | ||
| }) | ||
| } | ||
| if entry.AtReboot() { | ||
| cfg.AfterLogin = true | ||
| } else { | ||
| cfg.Schedules = []string{entry.Event().String()} | ||
| } | ||
| configs = append(configs, cfg) |
@rebootentries in crond for jobs that should run after system boot.RunAtLoadoption in launchd jobs to trigger tasks at user login.AfterLoginconfiguration option in systemd to start timers when the user session begins.Fixes #303