From 3d439bd2e67d10591f363dec266ce404e2c4ed9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristiano=20Gavi=C3=A3o?= Date: Sun, 13 Sep 2026 19:50:06 +0100 Subject: [PATCH] Support RunShell's directory property from spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/open-workflow-specification/specification/issues/1191 Signed-off-by: Cristiano Gavião --- .../impl/executors/RunShellExecutor.java | 7 +++++++ .../impl/executors/RunShellExecutorBuilder.java | 4 ++++ .../impl/test/RunShellExecutorTest.java | 17 ++++++++++++++++- .../run-shell/pwd-directory.yaml | 12 ++++++++++++ types/src/main/resources/schema/workflow.yaml | 4 ++++ 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 impl/test/src/test/resources/workflows-samples/run-shell/pwd-directory.yaml diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutor.java index a5d1d3bc3..37c0e101b 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutor.java @@ -23,6 +23,7 @@ import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.WorkflowValueResolver; import io.serverlessworkflow.impl.scripts.ScriptUtils; +import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -32,16 +33,19 @@ public class RunShellExecutor implements CallableTask { private final WorkflowValueResolver shellCommand; private final List> shellArguments; + private final Optional> shellDirectory; private final Optional>> shellEnv; private final Optional returnType; public RunShellExecutor( WorkflowValueResolver shellCommand, List> shellArguments, + Optional> shellDirectory, Optional>> shellEnv, Optional returnType) { this.shellCommand = shellCommand; this.shellArguments = shellArguments; + this.shellDirectory = shellDirectory; this.shellEnv = shellEnv; this.returnType = returnType; } @@ -64,6 +68,9 @@ public CompletableFuture apply( shellArguments.forEach(f -> commandAndArgs.add(f.apply(workflowContext, taskContext, model))); ProcessBuilder builder = new ProcessBuilder(commandAndArgs); + shellDirectory.ifPresent( + directory -> + builder.directory(new File(directory.apply(workflowContext, taskContext, model)))); shellEnv.ifPresent( map -> ScriptUtils.addEnviromment(builder, map.apply(workflowContext, taskContext, model))); diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutorBuilder.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutorBuilder.java index 2143a5890..a32efed36 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutorBuilder.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/RunShellExecutorBuilder.java @@ -39,6 +39,10 @@ public CallableTask build(RunShell taskConfiguration, WorkflowDefinition definit .map(s -> WorkflowUtils.buildStringFilter(definition.application(), s)) .toList() : List.of(), + shell.getDirectory() != null + ? Optional.of( + WorkflowUtils.buildStringFilter(definition.application(), shell.getDirectory())) + : Optional.empty(), shell.getEnvironment() != null ? Optional.of( WorkflowUtils.buildMapResolver( diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/RunShellExecutorTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/RunShellExecutorTest.java index 2435365ca..a8b4426cf 100644 --- a/impl/test/src/test/java/io/serverlessworkflow/impl/test/RunShellExecutorTest.java +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/RunShellExecutorTest.java @@ -39,7 +39,7 @@ public class RunShellExecutorTest { @BeforeAll static void init() { - appl = WorkflowApplication.builder().withAllowedCommands(List.of("ls", "echo")).build(); + appl = WorkflowApplication.builder().withAllowedCommands(List.of("ls", "echo", "pwd")).build(); } @AfterAll @@ -89,6 +89,21 @@ void testEchoWithJqExpression() throws IOException { }); } + @Test + void testDirectory() throws IOException { + Workflow workflow = + WorkflowReader.readWorkflowFromClasspath("workflows-samples/run-shell/pwd-directory.yaml"); + WorkflowModel model = appl.workflowDefinition(workflow).instance(Map.of()).start().join(); + + SoftAssertions.assertSoftly( + softly -> { + ProcessResult result = model.as(ProcessResult.class).orElseThrow(); + softly.assertThat(result.code()).isEqualTo(0); + softly.assertThat(result.stdout().trim()).isEqualTo("/tmp"); + softly.assertThat(result.stderr()).isEmpty(); + }); + } + @Test void testMissingShellCommand() throws IOException { Workflow workflow = diff --git a/impl/test/src/test/resources/workflows-samples/run-shell/pwd-directory.yaml b/impl/test/src/test/resources/workflows-samples/run-shell/pwd-directory.yaml new file mode 100644 index 000000000..83e7549d5 --- /dev/null +++ b/impl/test/src/test/resources/workflows-samples/run-shell/pwd-directory.yaml @@ -0,0 +1,12 @@ +document: + dsl: '1.0.1' + namespace: test + name: pwd-directory + version: '0.1.0' +do: + - runShell: + run: + shell: + command: pwd + directory: /tmp + return: all diff --git a/types/src/main/resources/schema/workflow.yaml b/types/src/main/resources/schema/workflow.yaml index 01feb5e81..efb27347a 100644 --- a/types/src/main/resources/schema/workflow.yaml +++ b/types/src/main/resources/schema/workflow.yaml @@ -928,6 +928,10 @@ $defs: description: A list of the arguments, if any, of the shell command to run. items: type: string + directory: + type: string + title: ShellDirectory + description: The working directory, if any, in which to run the shell command. environment: type: object title: ShellEnvironment