refactor(modules)!: allow multiple instances for taskfile & process-compose

This commit is contained in:
technofab 2025-12-19 16:05:00 +01:00
parent 25fb9162ff
commit 5262901404
Signed by: technofab
SSH key fingerprint: SHA256:bV4h88OqS/AxjbPn66uUdvK9JsgIW4tv3vwJQ8tpMqQ
6 changed files with 199 additions and 134 deletions

View file

@ -13,6 +13,8 @@ devshell.mkShell {
# use the modules # use the modules
lefthook.enable = true; lefthook.enable = true;
task.",".tasks = { ... };
} }
``` ```

View file

@ -4,32 +4,33 @@
config, config,
... ...
}: let }: let
inherit (lib) mkEnableOption mkOption mkIf types; inherit (lib) mkOption types;
cfg = config.process-compose;
configFile = (pkgs.formats.yaml {}).generate "process-compose.yaml" cfg.config;
pcAlias = pkgs.writeTextFile {
name = "pc-alias";
destination = "/bin/${cfg.alias}";
executable = true;
text =
# sh
''
${pkgs.process-compose}/bin/process-compose --config "${configFile}" ''${@:1}
'';
};
in { in {
options.process-compose = { options.process-compose = mkOption {
enable = type = types.attrsOf (types.submodule ({
mkEnableOption "Process-Compose" config,
// { name,
default = cfg.config != {}; ...
}; }: {
options = {
alias = mkOption { alias = mkOption {
type = types.str; type = types.str;
default = "pc"; default =
if name == "default"
then "pc"
else name;
description = '' description = ''
Alias for `process-compose`. Alias for `process-compose`.
Defaults to `"pc"` if `${name}` is `"default"`
'';
};
lazy = mkOption {
type = types.bool;
default = true;
description = ''
Whether the process compose config should be built on-demand/lazily.
It will probably not land in the gcroot and thus might get cleaned up with every gc.
On the other hand, this way loading the devshell is faster. Decide for yourself :)
''; '';
}; };
config = mkOption { config = mkOption {
@ -39,9 +40,43 @@ in {
Configure process-compose here. Configure process-compose here.
''; '';
}; };
configFile = mkOption {
internal = true;
type = types.package;
};
pcAlias = mkOption {
internal = true;
type = types.package;
};
};
config = rec {
configFile = (pkgs.formats.yaml {}).generate "process-compose.yaml" config.config;
pcAlias = pkgs.writeTextFile {
name = "pc-alias";
destination = "/bin/${config.alias}";
executable = true;
text = let
configScript =
if config.lazy
then
# sh
"$(nix build '${builtins.unsafeDiscardOutputDependency configFile.drvPath}^*' --no-link --print-out-paths)"
else configFile;
in
# sh
''
CONFIG_FILE="${configScript}"
${pkgs.process-compose}/bin/process-compose --config "$CONFIG_FILE" ''${@:1}
'';
};
};
}));
default = {};
description = ''
Define your process-compose instances here.
'';
}; };
config = mkIf cfg.enable { config.packages = map (val: val.pcAlias) (builtins.attrValues config.process-compose);
packages = [pcAlias];
};
} }

View file

@ -14,7 +14,7 @@ in {
script = let script = let
shell = devshell.mkShell { shell = devshell.mkShell {
imports = [module]; imports = [module];
process-compose.enable = true; process-compose."default" = {};
}; };
in in
# sh # sh
@ -23,6 +23,21 @@ in {
assert "-f ${shell}/bin/pc" "/bin/pc should exist" assert "-f ${shell}/bin/pc" "/bin/pc should exist"
''; '';
} }
{
name = "alias";
type = "script";
script = let
shell = devshell.mkShell {
imports = [module];
process-compose."pctest" = {};
};
in
# sh
''
${ntlib.helpers.scriptHelpers}
assert "-f ${shell}/bin/pctest" "/bin/pctest should exist"
'';
}
]; ];
}; };
} }

View file

@ -4,66 +4,21 @@
config, config,
... ...
}: let }: let
inherit (lib) mapAttrs mkEnableOption mkOption mkIf types; inherit (lib) mapAttrs mkOption types;
cfg = config.task;
patchedTasks = mapAttrs (_name: value: let
taskDir = value.dir or "";
absolutePathOrTemplate = (builtins.substring 0 1 taskDir) == "/" || (builtins.substring 0 1 taskDir) == "{";
in
value
// {
dir =
if absolutePathOrTemplate
then taskDir
else ''{{env "TASK_ROOT_DIR" | default .USER_WORKING_DIR}}/${taskDir}'';
})
cfg.tasks;
generator = name: value: generator = name: value:
pkgs.writeTextFile { pkgs.writeTextFile {
inherit name; inherit name;
text = builtins.toJSON value; text = builtins.toJSON value;
}; };
# NOTE: this requires python just to convert json to yaml, since json is valid yaml we just ignore that
# generator = (pkgs.formats.yaml {}).generate;
taskfile = generator "taskfile" {
version = 3;
inherit (cfg) interval;
tasks = patchedTasks;
};
# when using a , as alias for example the store path looks weird.
# This way it can be identified as being the task alias
taskAlias = pkgs.writeTextFile {
name = "task-alias";
destination = "/bin/${cfg.alias}";
executable = true;
text = let
taskfileScript =
if cfg.lazy
then
# sh
"$(nix build '${builtins.unsafeDiscardOutputDependency taskfile.drvPath}^*' --no-link --print-out-paths)"
else taskfile;
in
# sh
''
TASKFILE="${taskfileScript}"
STATE_DIR="''${REN_STATE:-''${DEVENV_STATE:-''${PRJ_CACHE_HOME}}}"
ROOT_DIR="''${REN_ROOT:-''${DEVENV_ROOT:-''${PRJ_ROOT}}}"
TASK_TEMP_DIR="''${STATE_DIR}/.task" \
TASK_ROOT_DIR="$ROOT_DIR" \
${pkgs.go-task}/bin/task --taskfile "$TASKFILE" ''${@:1}
'';
};
in { in {
options.task = { options.task = mkOption {
enable = type = types.attrsOf (types.submodule ({
mkEnableOption "Task" config,
// { name,
default = cfg.tasks != {}; ...
}; }: {
options = {
lazy = mkOption { lazy = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
@ -75,9 +30,13 @@ in {
}; };
alias = mkOption { alias = mkOption {
type = types.str; type = types.str;
default = "task"; default =
if name == "default"
then "task"
else name;
description = '' description = ''
Alias for `task`, eg. set to `,` to be able to run `, --list-all`. Alias for `task`, eg. set to `,` to be able to run `, --list-all`.
Defaults to `"task"` if `${name}` is `"default"`
''; '';
}; };
interval = mkOption { interval = mkOption {
@ -94,9 +53,69 @@ in {
Configure all your tasks here. Configure all your tasks here.
''; '';
}; };
taskfile = mkOption {
internal = true;
type = types.package;
};
taskAlias = mkOption {
internal = true;
type = types.package;
};
};
config = let
patchedTasks = mapAttrs (_name: value: let
taskDir = value.dir or "";
absolutePathOrTemplate = (builtins.substring 0 1 taskDir) == "/" || (builtins.substring 0 1 taskDir) == "{";
in
value
// {
dir =
if absolutePathOrTemplate
then taskDir
else ''{{env "TASK_ROOT_DIR" | default .USER_WORKING_DIR}}/${taskDir}'';
})
config.tasks;
in rec {
# NOTE: this requires python just to convert json to yaml, since json is valid yaml we just ignore that
# generator = (pkgs.formats.yaml {}).generate;
taskfile = generator "taskfile" {
version = 3;
inherit (config) interval;
tasks = patchedTasks;
};
# when using a , as alias for example the store path looks weird.
# This way it can be identified as being the task alias
taskAlias = pkgs.writeTextFile {
name = "task-alias";
destination = "/bin/${config.alias}";
executable = true;
text = let
taskfileScript =
if config.lazy
then
# sh
"$(nix build '${builtins.unsafeDiscardOutputDependency taskfile.drvPath}^*' --no-link --print-out-paths)"
else taskfile;
in
# sh
''
TASKFILE="${taskfileScript}"
STATE_DIR="''${REN_STATE:-''${DEVENV_STATE:-''${PRJ_CACHE_HOME}}}"
ROOT_DIR="''${REN_ROOT:-''${DEVENV_ROOT:-''${PRJ_ROOT}}}"
TASK_TEMP_DIR="''${STATE_DIR}/.task" \
TASK_ROOT_DIR="$ROOT_DIR" \
${pkgs.go-task}/bin/task --taskfile "$TASKFILE" ''${@:1}
'';
};
};
}));
default = {};
description = ''
Define your Taskfile instances here.
'';
}; };
config = mkIf cfg.enable { config.packages = map (val: val.taskAlias) (builtins.attrValues config.task);
packages = [taskAlias];
};
} }

View file

@ -14,7 +14,7 @@ in {
script = let script = let
shell = devshell.mkShell { shell = devshell.mkShell {
imports = [module]; imports = [module];
task.enable = true; task."default" = {};
}; };
in in
# sh # sh
@ -29,10 +29,7 @@ in {
script = let script = let
shell = devshell.mkShell { shell = devshell.mkShell {
imports = [module]; imports = [module];
task = { task."," = {};
enable = true;
alias = ",";
};
}; };
in in
# sh # sh

View file

@ -25,14 +25,11 @@ in {
treefmtWrapper treefmtWrapper
]; ];
task = { task.",".tasks = {
alias = ",";
tasks = {
"hello" = { "hello" = {
cmd = "echo world!"; cmd = "echo world!";
}; };
}; };
};
lefthook.config = { lefthook.config = {
skip_output = ["meta" "execution_out"]; skip_output = ["meta" "execution_out"];
@ -54,7 +51,7 @@ in {
}; };
}; };
process-compose.config.processes = { process-compose."default".config.processes = {
hello.command = "echo 'Hello World'"; hello.command = "echo 'Hello World'";
pc = { pc = {
command = "echo 'From Process Compose'"; command = "echo 'From Process Compose'";