mirror of
https://github.com/LnL7/nix-darwin.git
synced 2025-03-15 21:08:21 +00:00
Merge pull request #716 from tmillr/fix-launchd-calendar-interval
fix(launchd): improve `StartCalendarInterval`
This commit is contained in:
commit
58b905ea87
4 changed files with 146 additions and 57 deletions
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
launchdTypes = import ./types.nix { inherit config lib; };
|
||||||
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
options = {
|
options = {
|
||||||
Label = mkOption {
|
Label = mkOption {
|
||||||
|
@ -344,55 +348,21 @@ with lib;
|
||||||
default = null;
|
default = null;
|
||||||
example = [{ Hour = 2; Minute = 30; }];
|
example = [{ Hour = 2; Minute = 30; }];
|
||||||
description = ''
|
description = ''
|
||||||
This optional key causes the job to be started every calendar interval as specified. Missing arguments
|
This optional key causes the job to be started every calendar interval as specified. The semantics are
|
||||||
are considered to be wildcard. The semantics are much like `crontab(5)`. Unlike cron which skips job
|
much like {manpage}`crontab(5)`: Missing attributes are considered to be wildcard. Unlike cron which skips
|
||||||
invocations when the computer is asleep, launchd will start the job the next time the computer wakes
|
job invocations when the computer is asleep, launchd will start the job the next time the computer wakes
|
||||||
up. If multiple intervals transpire before the computer is woken, those events will be coalesced into
|
up. If multiple intervals transpire before the computer is woken, those events will be coalesced into
|
||||||
one event upon wake from sleep.
|
one event upon waking from sleep.
|
||||||
|
|
||||||
|
::: {.important}
|
||||||
|
The list must not be empty and must not contain duplicate entries (attrsets which compare equally).
|
||||||
|
:::
|
||||||
|
|
||||||
|
::: {.caution}
|
||||||
|
Since missing attrs become wildcards, an empty attrset effectively means "every minute".
|
||||||
|
:::
|
||||||
'';
|
'';
|
||||||
type = types.nullOr (types.listOf (types.submodule {
|
type = types.nullOr launchdTypes.StartCalendarInterval;
|
||||||
options = {
|
|
||||||
Minute = mkOption {
|
|
||||||
type = types.nullOr types.int;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
The minute on which this job will be run.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
Hour = mkOption {
|
|
||||||
type = types.nullOr types.int;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
The hour on which this job will be run.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
Day = mkOption {
|
|
||||||
type = types.nullOr types.int;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
The day on which this job will be run.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
Weekday = mkOption {
|
|
||||||
type = types.nullOr types.int;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
The weekday on which this job will be run (0 and 7 are Sunday).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
Month = mkOption {
|
|
||||||
type = types.nullOr types.int;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
The month on which this job will be run.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
StandardInPath = mkOption {
|
StandardInPath = mkOption {
|
||||||
|
@ -895,6 +865,5 @@ with lib;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
110
modules/launchd/types.nix
Normal file
110
modules/launchd/types.nix
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
{ lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib) imap1 types mkOption showOption optionDescriptionPhrase mergeDefinitions;
|
||||||
|
inherit (builtins) map filter length deepSeq throw toString concatLists;
|
||||||
|
inherit (lib.options) showDefs;
|
||||||
|
wildcardText = lib.literalMD "`*`";
|
||||||
|
|
||||||
|
/**
|
||||||
|
A type of list which does not allow duplicate elements. The base/inner
|
||||||
|
list type to use (e.g. `types.listOf` or `types.nonEmptyListOf`) is passed
|
||||||
|
via argument `listType`, which must be the final type and not a function.
|
||||||
|
|
||||||
|
NOTE: The extra check for duplicates is quadratic and strict, so use this
|
||||||
|
type sparingly and only:
|
||||||
|
|
||||||
|
* when needed, and
|
||||||
|
* when the list is expected to be recursively short (e.g. < 10 elements)
|
||||||
|
and shallow (i.e. strict evaluation of the list won't take too long)
|
||||||
|
|
||||||
|
The implementation of this function is similar to that of
|
||||||
|
`types.nonEmptyListOf`.
|
||||||
|
*/
|
||||||
|
types'.uniqueList = listType: listType // {
|
||||||
|
description = "unique ${types.optionDescriptionPhrase (class: class == "noun") listType}";
|
||||||
|
substSubModules = m: types'.uniqueList (listType.substSubModules m);
|
||||||
|
# This has been taken from the implementation of `types.listOf`, but has
|
||||||
|
# been modified to throw on duplicates. This check cannot be done in the
|
||||||
|
# `check` fn as this check is deep/strict, and because `check` runs
|
||||||
|
# prior to merging.
|
||||||
|
merge = loc: defs:
|
||||||
|
let
|
||||||
|
# Each element of `dupes` is a list. When there are duplicates,
|
||||||
|
# later lists will be duplicates of earlier lists, so just throw on
|
||||||
|
# the first set of duplicates found so that we don't have duplicate
|
||||||
|
# error msgs.
|
||||||
|
checked = filter (li:
|
||||||
|
if length li > 1
|
||||||
|
then throw "The option `${showOption loc}' contains duplicate entries after merging:\n${showDefs li}"
|
||||||
|
else false) dupes;
|
||||||
|
dupes = map (def: filter (def': def'.value == def.value) merged) merged;
|
||||||
|
merged = filter (x: x ? value) (concatLists (imap1 (n: def:
|
||||||
|
imap1 (m: el:
|
||||||
|
let
|
||||||
|
inherit (def) file;
|
||||||
|
loc' = loc ++ ["[definition ${toString n}-entry ${toString m}]"];
|
||||||
|
in
|
||||||
|
(mergeDefinitions
|
||||||
|
loc'
|
||||||
|
listType.nestedTypes.elemType
|
||||||
|
[{ inherit file; value = el; }]
|
||||||
|
).optionalValue // {inherit loc' file;}
|
||||||
|
) def.value
|
||||||
|
) defs));
|
||||||
|
in
|
||||||
|
deepSeq checked (map (x: x.value) merged);
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
StartCalendarInterval = let
|
||||||
|
CalendarIntervalEntry = types.submodule {
|
||||||
|
options = {
|
||||||
|
Minute = mkOption {
|
||||||
|
type = types.nullOr (types.ints.between 0 59);
|
||||||
|
default = null;
|
||||||
|
defaultText = wildcardText;
|
||||||
|
description = ''
|
||||||
|
The minute on which this job will be run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
Hour = mkOption {
|
||||||
|
type = types.nullOr (types.ints.between 0 23);
|
||||||
|
default = null;
|
||||||
|
defaultText = wildcardText;
|
||||||
|
description = ''
|
||||||
|
The hour on which this job will be run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
Day = mkOption {
|
||||||
|
type = types.nullOr (types.ints.between 1 31);
|
||||||
|
default = null;
|
||||||
|
defaultText = wildcardText;
|
||||||
|
description = ''
|
||||||
|
The day on which this job will be run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
Weekday = mkOption {
|
||||||
|
type = types.nullOr (types.ints.between 0 7);
|
||||||
|
default = null;
|
||||||
|
defaultText = wildcardText;
|
||||||
|
description = ''
|
||||||
|
The weekday on which this job will be run (0 and 7 are Sunday).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
Month = mkOption {
|
||||||
|
type = types.nullOr (types.ints.between 1 12);
|
||||||
|
default = null;
|
||||||
|
defaultText = wildcardText;
|
||||||
|
description = ''
|
||||||
|
The month on which this job will be run.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
types.either CalendarIntervalEntry (types'.uniqueList (types.nonEmptyListOf CalendarIntervalEntry));
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ with lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.nix.gc;
|
cfg = config.nix.gc;
|
||||||
|
launchdTypes = import ../../launchd/types.nix { inherit config lib; };
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -35,9 +36,13 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
interval = mkOption {
|
interval = mkOption {
|
||||||
type = types.attrs;
|
type = launchdTypes.StartCalendarInterval;
|
||||||
default = { Hour = 3; Minute = 15; };
|
default = [{ Weekday = 7; Hour = 3; Minute = 15; }];
|
||||||
description = "The time interval at which the garbage collector will run.";
|
description = ''
|
||||||
|
The calendar interval at which the garbage collector will run.
|
||||||
|
See the {option}`serviceConfig.StartCalendarInterval` option of
|
||||||
|
the {option}`launchd` module for more info.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
options = mkOption {
|
options = mkOption {
|
||||||
|
@ -63,7 +68,7 @@ in
|
||||||
command = "${config.nix.package}/bin/nix-collect-garbage ${cfg.options}";
|
command = "${config.nix.package}/bin/nix-collect-garbage ${cfg.options}";
|
||||||
environment.NIX_REMOTE = optionalString config.nix.useDaemon "daemon";
|
environment.NIX_REMOTE = optionalString config.nix.useDaemon "daemon";
|
||||||
serviceConfig.RunAtLoad = false;
|
serviceConfig.RunAtLoad = false;
|
||||||
serviceConfig.StartCalendarInterval = [ cfg.interval ];
|
serviceConfig.StartCalendarInterval = cfg.interval;
|
||||||
serviceConfig.UserName = cfg.user;
|
serviceConfig.UserName = cfg.user;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ let
|
||||||
;
|
;
|
||||||
|
|
||||||
cfg = config.nix.optimise;
|
cfg = config.nix.optimise;
|
||||||
|
launchdTypes = import ../../launchd/types.nix { inherit config lib; };
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -41,9 +42,13 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
interval = mkOption {
|
interval = mkOption {
|
||||||
type = types.attrs;
|
type = launchdTypes.StartCalendarInterval;
|
||||||
default = { Hour = 3; Minute = 15; };
|
default = [{ Weekday = 7; Hour = 4; Minute = 15; }];
|
||||||
description = "The time interval at which the optimiser will run.";
|
description = ''
|
||||||
|
The calendar interval at which the optimiser will run.
|
||||||
|
See the {option}`serviceConfig.StartCalendarInterval` option of
|
||||||
|
the {option}`launchd` module for more info.
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -63,7 +68,7 @@ in
|
||||||
"/bin/wait4path ${config.nix.package} && exec ${config.nix.package}/bin/nix-store --optimise"
|
"/bin/wait4path ${config.nix.package} && exec ${config.nix.package}/bin/nix-store --optimise"
|
||||||
];
|
];
|
||||||
RunAtLoad = false;
|
RunAtLoad = false;
|
||||||
StartCalendarInterval = [ cfg.interval ];
|
StartCalendarInterval = cfg.interval;
|
||||||
UserName = cfg.user;
|
UserName = cfg.user;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue