diff --git a/src/modules/process-managers/process-compose.nix b/src/modules/process-managers/process-compose.nix index 7504f3b87..97e4ac0e1 100644 --- a/src/modules/process-managers/process-compose.nix +++ b/src/modules/process-managers/process-compose.nix @@ -178,6 +178,11 @@ in let command = if !value.start.enable then value.exec + # Interactive processes must be a direct child of the process-compose PTY. + # Routing them through `devenv-tasks` pipes their stdout/stderr and breaks + # interactivity (no prompt, block-buffered output). They therefore bypass + # the task runner and lose before/after task-dependency handling. + else if value.process-compose.is_interactive or false then value.exec else if value.process-compose.is_elevated or false then config.process.taskCommandsBase.${name} else config.process.taskCommands.${name}; diff --git a/tests/process-compose-interactive/devenv.nix b/tests/process-compose-interactive/devenv.nix new file mode 100644 index 000000000..dbb5aae4a --- /dev/null +++ b/tests/process-compose-interactive/devenv.nix @@ -0,0 +1,37 @@ +{ pkgs, lib, config, ... }: +let + pcProcesses = config.process.managers.process-compose.settings.processes; +in +{ + process.manager.implementation = "process-compose"; + + # An interactive process must be a direct child of the process-compose PTY. + # Routing it through the `devenv-tasks` runner pipes its stdout/stderr and + # breaks interactivity (no prompt, block-buffered output). Its generated + # `command` must therefore be the raw `exec`, not the devenv-tasks wrapper. + processes.repl = { + exec = "python3"; + process-compose.is_interactive = true; + }; + + # A regular process must still be routed through `devenv-tasks` so that + # task-dependency handling and env injection keep working. + processes.web = { + exec = "sleep infinity"; + }; + + assertions = [ + { + assertion = pcProcesses.repl.command == "python3"; + message = "interactive process should run `exec` directly, not via devenv-tasks. Got: ${pcProcesses.repl.command}"; + } + { + assertion = lib.hasInfix "devenv-tasks" pcProcesses.web.command; + message = "non-interactive process should still route through devenv-tasks. Got: ${pcProcesses.web.command}"; + } + ]; + + enterTest = '' + echo "interactive process command assertions passed" + ''; +}