From c34997ddd2984494c21dd48162d2b5fd36bfe267 Mon Sep 17 00:00:00 2001 From: David Pilar Date: Mon, 16 Feb 2026 00:12:47 +0100 Subject: [PATCH] Add environment post processor to determine if shell should run non-interactively when a command is present Signed-off-by: David Pilar --- .../SpringShellEnvironmentPostProcessor.java | 31 ++++++++ .../main/resources/META-INF/spring.factories | 1 + ...ingShellEnvironmentPostProcessorTests.java | 73 +++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 spring-shell-core-autoconfigure/src/main/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessor.java create mode 100644 spring-shell-core-autoconfigure/src/main/resources/META-INF/spring.factories create mode 100644 spring-shell-core-autoconfigure/src/test/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessorTests.java diff --git a/spring-shell-core-autoconfigure/src/main/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessor.java b/spring-shell-core-autoconfigure/src/main/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessor.java new file mode 100644 index 000000000..59639f669 --- /dev/null +++ b/spring-shell-core-autoconfigure/src/main/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessor.java @@ -0,0 +1,31 @@ +package org.springframework.shell.core.autoconfigure; + +import org.springframework.boot.EnvironmentPostProcessor; +import org.springframework.boot.SpringApplication; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import java.util.Map; + +import static org.springframework.core.env.CommandLinePropertySource.DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME; + +/** + * This class disables the interactive shell mode by setting + * {@code spring.shell.interactive.enabled} to {@code false} when certain command-line + * arguments are passed to the application. + * + * @author David Pilar + */ +public class SpringShellEnvironmentPostProcessor implements EnvironmentPostProcessor { + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + String[] args = environment.getProperty(DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME, String[].class, new String[] {}); + if (args.length > 0) { + environment.getPropertySources() + .addFirst(new MapPropertySource("interactiveDisabledProps", + Map.of("spring.shell.interactive.enabled", "false"))); + } + } + +} diff --git a/spring-shell-core-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-shell-core-autoconfigure/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..111053b05 --- /dev/null +++ b/spring-shell-core-autoconfigure/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.EnvironmentPostProcessor=org.springframework.shell.core.autoconfigure.SpringShellEnvironmentPostProcessor \ No newline at end of file diff --git a/spring-shell-core-autoconfigure/src/test/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessorTests.java b/spring-shell-core-autoconfigure/src/test/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessorTests.java new file mode 100644 index 000000000..99b272330 --- /dev/null +++ b/spring-shell-core-autoconfigure/src/test/java/org/springframework/shell/core/autoconfigure/SpringShellEnvironmentPostProcessorTests.java @@ -0,0 +1,73 @@ +package org.springframework.shell.core.autoconfigure; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.boot.SpringApplication; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.StandardEnvironment; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.springframework.core.env.CommandLinePropertySource.DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME; + +/** + * @author David Pilar + */ +class SpringShellEnvironmentPostProcessorTests { + + private SpringShellEnvironmentPostProcessor postProcessor; + + private ConfigurableEnvironment environment; + + private SpringApplication application; + + @BeforeEach + void setUp() { + postProcessor = new SpringShellEnvironmentPostProcessor(); + environment = new StandardEnvironment(); + application = Mockito.mock(SpringApplication.class); + } + + @Test + void shouldDisableInteractiveMode_whenNonOptionArgsIsPresent() { + environment.getPropertySources() + .addFirst(new MapPropertySource("test", Map.of(DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME, "some-command"))); + + postProcessor.postProcessEnvironment(environment, application); + + assertEquals("false", environment.getProperty("spring.shell.interactive.enabled")); + } + + @Test + void shouldNotModifyEnvironment_whenNonOptionArgsIsNotPresent() { + postProcessor.postProcessEnvironment(environment, application); + + assertNull(environment.getProperty("spring.shell.interactive.enabled")); + } + + @Test + void shouldNotModifyEnvironment_whenNonOptionArgsIsEmpty() { + environment.getPropertySources() + .addFirst(new MapPropertySource("test", Map.of(DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME, ""))); + + postProcessor.postProcessEnvironment(environment, application); + + assertNull(environment.getProperty("spring.shell.interactive.enabled")); + } + + @Test + void customPropsShouldHaveHighestPriority() { + environment.getPropertySources() + .addFirst(new MapPropertySource("existing", Map.of("spring.shell.interactive.enabled", "true", + DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME, "some-command"))); + + postProcessor.postProcessEnvironment(environment, application); + + assertEquals("false", environment.getProperty("spring.shell.interactive.enabled")); + } + +} \ No newline at end of file