bonsai: begin to push the service upstream
This commit is contained in:
@@ -1,93 +1,7 @@
|
||||
# bonsai docs: <https://sr.ht/~stacyharper/bonsai/>
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, options, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.programs.bonsai;
|
||||
|
||||
delayType = with lib; types.submodule {
|
||||
options = {
|
||||
type = mkOption {
|
||||
type = types.enum [ "delay" ];
|
||||
# default = "delay";
|
||||
};
|
||||
delay_duration = mkOption {
|
||||
type = types.int;
|
||||
description = ''
|
||||
used for "delay" types only.
|
||||
nanoseconds until the event is finalized.
|
||||
'';
|
||||
};
|
||||
transitions = mkOption {
|
||||
type = types.listOf transitionType;
|
||||
default = [];
|
||||
description = ''
|
||||
list of transitions out of this state (i.e. after completing the delay).
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
eventType = with lib; types.submodule {
|
||||
options = {
|
||||
type = mkOption {
|
||||
type = types.enum [ "event" ];
|
||||
# default = "event";
|
||||
};
|
||||
event_name = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
name of event which this transition applies to.
|
||||
'';
|
||||
};
|
||||
transitions = mkOption {
|
||||
type = types.listOf transitionType;
|
||||
default = [];
|
||||
description = ''
|
||||
list of transitions out of this state.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
execType = with lib; types.submodule {
|
||||
options = {
|
||||
type = mkOption {
|
||||
type = types.enum [ "exec" ];
|
||||
# default = "exec";
|
||||
};
|
||||
command = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
command to run when the event is triggered.
|
||||
'';
|
||||
};
|
||||
transitions = mkOption {
|
||||
type = types.listOf transitionType;
|
||||
default = [];
|
||||
description = ''
|
||||
list of transitions out of this state (i.e. after successfully executing the command)
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
isDelay = x: delayType.check x && x.type == "delay";
|
||||
isEvent = x: eventType.check x && x.type == "event";
|
||||
isExec = x: execType.check x && x.type == "exec";
|
||||
# unfortunately, `types.oneOf` is naive about submodules, so we need our own type.
|
||||
# transitionType = lib.types.oneOf [ delayType eventType execType ];
|
||||
transitionType = with lib.types; mkOptionType {
|
||||
name = "transition";
|
||||
check = x: isDelay x || isEvent x || isExec x;
|
||||
merge = loc: defs: let
|
||||
defList = builtins.map (d: d.value) defs;
|
||||
in
|
||||
if builtins.all isDelay defList then
|
||||
delayType.merge loc defs
|
||||
else if builtins.all isEvent defList then
|
||||
eventType.merge loc defs
|
||||
else if builtins.all isExec defList then
|
||||
execType.merge loc defs
|
||||
else
|
||||
mergeOneOption loc defs
|
||||
;
|
||||
};
|
||||
in
|
||||
{
|
||||
sane.programs.bonsai = {
|
||||
@@ -96,7 +10,7 @@ in
|
||||
type = types.submodule {
|
||||
options = {
|
||||
transitions = mkOption {
|
||||
type = types.listOf transitionType;
|
||||
type = options.services.bonsaid.settings.type;
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
@@ -111,7 +25,7 @@ in
|
||||
'';
|
||||
});
|
||||
|
||||
fs.".config/bonsai/bonsai_tree.json".symlink.target = pkgs.writers.writeJSON "bonsai_tree.json" cfg.config.transitions;
|
||||
# fs.".config/bonsai/bonsai_tree.json".symlink.target = config.services.bonsaid.settingsFile;
|
||||
|
||||
sandbox.extraRuntimePaths = [
|
||||
"bonsai"
|
||||
@@ -135,4 +49,11 @@ in
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# plug into the (proposed) nixpkgs bonsaid service.
|
||||
# it's a user service, and since i don't use the service manager it doesn't actually activate:
|
||||
# i just steal the config file generation from it :)
|
||||
services.bonsaid.settings = lib.mkIf cfg.enabled cfg.config.transitions;
|
||||
# vvv not actually necessary. TODO: delete this line once the service is upstreamed?
|
||||
services.bonsaid.enable = lib.mkIf cfg.enabled true;
|
||||
}
|
||||
|
139
modules/services/bonsaid.nix
Normal file
139
modules/services/bonsaid.nix
Normal file
@@ -0,0 +1,139 @@
|
||||
# this is a version of the bonsaid service which i intend to upstream to nixpkgs.
|
||||
# it runs as a user service, so i'd need to wrap it for my own setup in some other module.
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
json = pkgs.formats.json { };
|
||||
transitionType = with lib; types.submodule {
|
||||
freeformType = json.type;
|
||||
options.type = mkOption {
|
||||
type = types.enum [ "delay" "event" "exec" ];
|
||||
};
|
||||
options.command = mkOption {
|
||||
type = types.nullOr (types.listOf types.str);
|
||||
default = null;
|
||||
description = ''
|
||||
Command to run when this transition is taken.
|
||||
This is executed inline by `bonsaid` and blocks handling of any other events until completion.
|
||||
To perform the command asynchronously, specify it like `[ "setsid" "-f" "my-command" ]`.
|
||||
|
||||
Only effects transitions with `type = "exec"`.
|
||||
'';
|
||||
};
|
||||
options.delay_duration = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = ''
|
||||
Nanoseconds to wait after the previous state change before performing this transition.
|
||||
This can be placed at the same level as a `type = "event"` transition to achieve a
|
||||
timeout mechanism.
|
||||
|
||||
Only effects transitions with `type = "delay"`.
|
||||
'';
|
||||
};
|
||||
options.event_name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Name of the event which should trigger this transition when received by `bonsaid`.
|
||||
Events are sent to `bonsaid` by running `bonsaictl -e <event_name>`.
|
||||
|
||||
Only effects transitions with `type = "event"`.
|
||||
'';
|
||||
};
|
||||
options.transitions = mkOption {
|
||||
type = types.listOf transitionType;
|
||||
default = [];
|
||||
description = ''
|
||||
List of transitions out of this state.
|
||||
If left empty, then this state is considered a terminal state and entering it will
|
||||
trigger an immediate transition back to the root state (after processing side effects).
|
||||
'';
|
||||
};
|
||||
};
|
||||
cfg = config.services.bonsaid;
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [ colinsane ];
|
||||
|
||||
options.services.bonsaid = with lib; {
|
||||
enable = mkEnableOption "bonsaid";
|
||||
package = mkPackageOption pkgs "bonsai" { };
|
||||
extraFlags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
Extra flags to pass to `bonsaid`, such as `[ "-v" ]` to enable verbose logging.
|
||||
'';
|
||||
};
|
||||
settings = mkOption {
|
||||
type = types.listOf transitionType;
|
||||
default = [];
|
||||
description = ''
|
||||
State transition definitions. See the upstream [[README]](https://git.sr.ht/~stacyharper/bonsai)
|
||||
for extended documentation and a more complete example.
|
||||
'';
|
||||
example = [
|
||||
{
|
||||
type = "event";
|
||||
event_name = "power_button_pressed";
|
||||
transitions = [
|
||||
{
|
||||
# Hold power button for 600ms to trigger a command
|
||||
type = "delay";
|
||||
delay_duration = 600000000;
|
||||
transitions = [
|
||||
{
|
||||
type = "exec";
|
||||
command = [ "swaymsg" "--" "output" "*" "power" "off" ];
|
||||
# `transitions = []` marks this as a terminal state,
|
||||
# so bonsai will return to the root state immediately after executing the above command.
|
||||
transitions = [];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
# If the power button is released before the 600ms elapses, return to the root state.
|
||||
type = "event";
|
||||
event_name = "power_button_released";
|
||||
transitions = [];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
settingsFile = mkOption {
|
||||
type = types.path;
|
||||
default = let
|
||||
filterNulls = v: if lib.isAttrs v then
|
||||
lib.mapAttrs (_: filterNulls) (lib.filterAttrs (_: a: a != null) v)
|
||||
else if lib.isList v then
|
||||
lib.map filterNulls (lib.filter (a: a != null) v)
|
||||
else
|
||||
v
|
||||
;
|
||||
in
|
||||
json.generate "bonsai_tree.json" (filterNulls cfg.settings);
|
||||
description = ''
|
||||
Path to a .json file specifying the state transitions.
|
||||
You don't need to set this unless you prefer to provide the json file
|
||||
yourself instead of using the `settings` option.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
systemd.user.services.bonsaid = {
|
||||
description = "Bonsai Finite State Machine daemon";
|
||||
unitConfig.Documentation = [ "https://git.sr.ht/~stacyharper/bonsai" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = lib.escapeShellArgs (
|
||||
[ (lib.getExe' cfg.package "bonsaid") "-t" cfg.settingsFile ] ++ cfg.extraFlags
|
||||
);
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
RestartSec = "5s";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./bonsaid.nix
|
||||
./buffyboard.nix
|
||||
./dropbear.nix
|
||||
./clightning.nix
|
||||
|
Reference in New Issue
Block a user