diff --git a/docs/.pages b/docs/.pages index a8a96f2b3..28de28969 100644 --- a/docs/.pages +++ b/docs/.pages @@ -7,6 +7,7 @@ nav: - Basics: basics.md - Packages: packages.md - Scripts: scripts.md + - Tasks: tasks.md - Languages: - Overview: languages.md - Supported Languages: supported-languages diff --git a/docs/tasks.md b/docs/tasks.md new file mode 100644 index 000000000..1203a555d --- /dev/null +++ b/docs/tasks.md @@ -0,0 +1,53 @@ +Tasks allow you to form dependencies between commands. + +```nix title="devenv.nix" +{ pkgs, ... }: + +{ + tasks.hello = { + exec = ''echo "Hello, world!"''; + desc = "hello world in bash"; + }; +} +``` + +```shell-session +$ devenv shell task hello +• Building shell ... +• Entering shell ... +Hello, world! +$ +``` + +## Using your favourite language + +Tasks can also reference scripts and depend on other tasks, for example when entering the shell. + +```nix title="devenv.nix" +{ pkgs, lib, config, ... }: + +{ + scripts.python-hello = { + exec = ''print("Hello world from Python!")''; + expose = false; + package = config.languages.python.package; + description = "hello world in Python"; + }; + + tasks.python-hello.exec = config.scripts.python-hello.scriptPackage; + tasks.hello = { + exec = "echo 'Hello world from bash!'"; + deps = [ "python-hello" ]; + } + tasks.enterShell.deps = [ "hello" ]; +} +``` + +```shell-session +$ devenv shell +• Building shell ... +• Entering shell ... +Hello world from Python! +Hello world from bash! +$ +``` \ No newline at end of file diff --git a/src/modules/scripts.nix b/src/modules/scripts.nix index c74ece408..2470e090c 100644 --- a/src/modules/scripts.nix +++ b/src/modules/scripts.nix @@ -19,6 +19,11 @@ let description = "The package to use to run the script."; default = pkgs.bash; }; + expose = lib.mkOption { + type = types.bool; + description = "Whether to expose the script in the environment."; + default = true; + }; binary = lib.mkOption { type = types.str; description = "Override the binary name if it doesn't match package name"; @@ -50,6 +55,8 @@ let ${name}${lib.optionalString (script.description != "") ": ${script.description}"} ${script.scriptPackage} ''; + + exposedScripts = lib.filterAttrs (_: script: script.expose) config.scripts; in { options = { @@ -61,8 +68,8 @@ in }; config = { - packages = lib.mapAttrsToList (_: script: script.scriptPackage) config.scripts; + packages = lib.mapAttrsToList (_: script: script.scriptPackage) exposedScripts; - infoSections."scripts" = lib.mapAttrsToList renderInfoSection config.scripts; + infoSections."scripts" = lib.mapAttrsToList renderInfoSection exposedScripts; }; } diff --git a/src/modules/tasks.nix b/src/modules/tasks.nix new file mode 100644 index 000000000..aae42a109 --- /dev/null +++ b/src/modules/tasks.nix @@ -0,0 +1,80 @@ +{ pkgs, lib, config, ... }: +let + types = lib.types; + taskType = types.submodule ({ name, config, ... }: { + options = { + exec = lib.mkOption { + type = types.str; + description = "Bash code to run the process."; + }; + script = lib.mkOption { + type = types.package; + internal = true; + default = pkgs.writeShellScript name config.exec; + description = "Path to the script to run."; + }; + desc = lib.mkOption { + type = types.str; + default = ""; + description = "Description of the task."; + }; + deps = lib.mkOption { + type = types.listOf types.str; + description = "List of tasks to run before this task."; + default = [ ]; + }; + aliases = lib.mkOption { + type = types.listOf types.str; + description = "List of aliases for this task."; + default = [ ]; + }; + }; + }); + # ladder is a hack to force Nix into proper indentation + renderTask = name: task: '' + # + ${name}: + cmds: + - ${task.script} + desc: ${task.desc} + deps: [${lib.concatStringsSep " " task.deps}] + aliases: [${lib.concatStringsSep " " task.aliases}] + ''; +in +{ + options.tasks = lib.mkOption { + type = types.attrsOf taskType; + }; + + options.task.config = lib.mkOption { + type = types.package; + internal = true; + default = pkgs.writeText "tasks.yaml" '' + # auto generated by devenv.nix + version: '3' + + tasks: + ${lib.concatStringsSep "\n" (lib.mapAttrsToList renderTask config.tasks)} + ''; + }; + + config = { + packages = [ pkgs.go-task ]; + info.infoSections.tasks = lib.mapAttrsToList (name: task: "${name}: ${task.desc} ${task.script}") config.tasks; + tasks = { + enterShell = { + desc = "Runs when entering the shell"; + exec = "echo"; + }; + enterTest = { + desc = "Runs when entering the test environment"; + exec = "echo"; + }; + }; + enterShell = '' + ln -sf ${config.task.config} Taskfile.yaml + task enterShell + ''; + enterTest = "task enterTest"; + }; +} diff --git a/src/modules/top-level.nix b/src/modules/top-level.nix index edf4b361c..5dce1edf6 100644 --- a/src/modules/top-level.nix +++ b/src/modules/top-level.nix @@ -216,6 +216,7 @@ in ./lib.nix ./tests.nix ./cachix.nix + ./tasks.nix ] ++ (listEntries ./languages) ++ (listEntries ./services) diff --git a/tests/tasks/devenv.nix b/tests/tasks/devenv.nix new file mode 100644 index 000000000..cc118a598 --- /dev/null +++ b/tests/tasks/devenv.nix @@ -0,0 +1,19 @@ +{ + tasks = { + shell.exec = "touch shell"; + enterShell.deps = [ "shell" ]; + test.exec = "touch test"; + }; + + enterTest = '' + if [ ! -f shell ]; then + echo "shell does not exist" + exit 1 + fi + task test + if [ ! -f test ]; then + echo "test does not exist" + exit 1 + fi + ''; +}