Skip to content

feat(scheduler): allow a custom command for scheduled tasks - #540

Open
xchacha20-poly1305 wants to merge 2 commits into
NucleusFramework:mainfrom
xchacha20-poly1305:custom-scheduler
Open

feat(scheduler): allow a custom command for scheduled tasks#540
xchacha20-poly1305 wants to merge 2 commits into
NucleusFramework:mainfrom
xchacha20-poly1305:custom-scheduler

Conversation

@xchacha20-poly1305

Copy link
Copy Markdown

Closes #498

Problem

All three scheduler backends resolved the program the OS should wake up from ProcessHandle.current().info().command(). Apps that bootstrap through a custom launcher therefore had the OS start the JVM/native binary rather than the launcher, and there was no way to override it.

Solution

SchedulerConfig (new, modelled on the existing AutoLaunchConfig):

SchedulerConfig.executablePath = "/opt/myapp/myapp-launcher"
SchedulerConfig.executableArguments = listOf("--background")

DesktopTaskScheduler.enqueue(TaskRequest.periodic(TaskId("sync"), 1.hours))

The scheduled invocation becomes <executablePath> <executableArguments…> --nucleus-scheduler-run <taskId>. Leaving both unset keeps today's behavior exactly, so this is additive — no API break.

Implementation notes

  • New internal object SchedulerExecutable resolves the command for all three backends and centralizes the scheduler flag on DesktopBootReceiver.SCHEDULER_ARG, replacing the three duplicated executablePath getters and two copies of the constant.
  • Threaded through LinuxSystemdScheduler, MacOSLaunchdScheduler (native + shell plist, and both retry paths) and WindowsTaskScheduler (wscript wrapper + retry fallback).
  • TaskWrapperScript gained an execArgs parameter, and content generation is split from file writing (buildLinuxScript / buildWindowsScript) so the generated scripts are unit-testable.
  • Escaping fixes that fell out of passing user-supplied arguments through: Linux script values are single-quote escaped instead of naively wrapped in "…", Windows arguments containing spaces are quoted, and macOS plist <string> values are XML-escaped (they were interpolated raw before).
  • The wrapper scripts still test the configured executable for existence and self-destruct when it is gone, so a custom launcher must be an absolute path — documented in the SchedulerConfig KDoc.

Testing

  • :scheduler:test :scheduler:apiCheck :scheduler:detekt :scheduler:ktlintCheck :scheduler-testing:test pass; scheduler/api/scheduler.api re-dumped.
  • New SchedulerExecutableTest (resolution order, blank override ignored, argument ordering) and TaskWrapperScriptTest (Linux/Windows wrapper content, escaping, self-destruct branch).
  • Manually verified on Linux: generated a real wrapper script against a dummy launcher, which ran as launcher --background "--log dir" --nucleus-scheduler-run sync with the space-containing argument preserved as one argument; deleting the launcher made the script correctly unregister and delete itself.
  • macOS and Windows are covered by unit tests only — I could not exercise launchd / Task Scheduler locally.

@kdroidFilter

Copy link
Copy Markdown
Collaborator

can you fix the ci ?

xchacha20-poly1305 and others added 2 commits August 18, 2026 17:50
Every backend resolved the program to wake up from
ProcessHandle.current().info().command(), so apps that bootstrap through a
custom launcher had the OS start the JVM/native binary instead of the
launcher, with no way to override it.

SchedulerConfig now exposes executablePath and executableArguments, mirroring
AutoLaunchConfig. The scheduled invocation becomes
`<executablePath> <executableArguments…> --nucleus-scheduler-run <taskId>`;
leaving both unset keeps the previous behavior.

The new internal SchedulerExecutable resolves the command for all three
backends and centralizes the scheduler flag on DesktopBootReceiver, replacing
the three duplicated executablePath getters and two copies of the constant.
Wrapper-script content generation is split from file writing so it can be
unit-tested, and while threading the arguments through: Linux script values
are single-quote escaped instead of naively wrapped in double quotes, Windows
arguments containing spaces are quoted, and macOS plist <string> values are
XML-escaped.

Closes NucleusFramework#498

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SchedulerExecutableTest pins the resolution order (config override, blank
override ignored, ProcessHandle fallback) and the argument ordering.
TaskWrapperScriptTest asserts the generated Linux and Windows wrappers invoke
the configured launcher with its extra arguments ahead of the scheduler flag,
escape quotes and spaces, and still self-destruct when the executable is gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xchacha20-poly1305

Copy link
Copy Markdown
Author

can you fix the ci ?

The test all passed on my machine. Can you re-run it? My change isn't relevant to decorated-window-tao.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow using custom command for scheduler

2 participants