Summary
Currently DISABLE_INTERACTIVE_MODE=true can only be set as a real OS environment variable or JVM system property (-DDISABLE_INTERACTIVE_MODE=true). Users should also be able to set it in a .env file so that CI pipelines and project-level tooling don't need to manage a separate environment variable.
Problem
Two places in the code read DISABLE_INTERACTIVE_MODE from Spring's Environment:
InteractiveModeRunnerConfiguration.resolveRunner() — selects NonInteractiveShellRunner vs JLineShellRunner at application startup
RunSuiteCommand.isNonInteractive() — controls output behavior (exit codes, error rendering, concise summary)
The existing DotEnvLoader is only called inside RunSuiteCommand.runSuite(), after Spring Shell has already parsed the command arguments. By then, runner selection has already happened. So the suite-directory .env is too late for runner selection.
Proposed Solution
Register a Spring Boot EnvironmentPostProcessor that loads a .env file from the current working directory (CWD) and adds its entries to Spring's Environment as a low-priority property source. This happens before any beans are created, so both InteractiveModeRunnerConfiguration and RunSuiteCommand.isNonInteractive() automatically see the value — no changes to those classes needed.
Priority order (highest to lowest):
- OS environment variable
- JVM system property (
-D...)
- CWD
.env file ← new
application.properties
Implementation Plan
1. Create DotEnvEnvironmentPostProcessor
src/main/java/.../config/DotEnvEnvironmentPostProcessor.java
- Implements
org.springframework.boot.env.EnvironmentPostProcessor
- Uses
io.github.cdimascio.dotenv.Dotenv directly (Spring beans are not available at this lifecycle stage)
- Loads
.env from System.getProperty("user.dir") with .ignoreIfMissing()
- Adds a
MapPropertySource("cwdDotenv", map) via environment.getPropertySources().addLast(...) so OS env vars always win
2. Register in META-INF/spring.factories
src/main/resources/META-INF/spring.factories (new file)
org.springframework.boot.env.EnvironmentPostProcessor=\
io.github.snytkine.apitester.api_tester_cli.config.DotEnvEnvironmentPostProcessor
3. Update GraalVM native-image hints
reflect-config.json — add constructor entry for DotEnvEnvironmentPostProcessor
resource-config.json — add META-INF/spring.factories so it is bundled in the native binary
4. Tests
src/test/java/.../config/DotEnvEnvironmentPostProcessorTest.java
- Write a
.env to a temp dir, set user.dir to that dir, call postProcessEnvironment(), assert the property is visible
- Assert OS env / system property overrides
.env value
- Assert missing
.env does not throw
Files to change
| File |
Action |
src/main/java/.../config/DotEnvEnvironmentPostProcessor.java |
Create |
src/main/resources/META-INF/spring.factories |
Create |
src/main/resources/META-INF/native-image/.../reflect-config.json |
Add 1 entry |
src/main/resources/META-INF/native-image/.../resource-config.json |
Add 1 entry |
src/test/java/.../config/DotEnvEnvironmentPostProcessorTest.java |
Create |
No changes required to InteractiveModeRunnerConfiguration or RunSuiteCommand.
Summary
Currently
DISABLE_INTERACTIVE_MODE=truecan only be set as a real OS environment variable or JVM system property (-DDISABLE_INTERACTIVE_MODE=true). Users should also be able to set it in a.envfile so that CI pipelines and project-level tooling don't need to manage a separate environment variable.Problem
Two places in the code read
DISABLE_INTERACTIVE_MODEfrom Spring'sEnvironment:InteractiveModeRunnerConfiguration.resolveRunner()— selectsNonInteractiveShellRunnervsJLineShellRunnerat application startupRunSuiteCommand.isNonInteractive()— controls output behavior (exit codes, error rendering, concise summary)The existing
DotEnvLoaderis only called insideRunSuiteCommand.runSuite(), after Spring Shell has already parsed the command arguments. By then, runner selection has already happened. So the suite-directory.envis too late for runner selection.Proposed Solution
Register a Spring Boot
EnvironmentPostProcessorthat loads a.envfile from the current working directory (CWD) and adds its entries to Spring'sEnvironmentas a low-priority property source. This happens before any beans are created, so bothInteractiveModeRunnerConfigurationandRunSuiteCommand.isNonInteractive()automatically see the value — no changes to those classes needed.Priority order (highest to lowest):
-D...).envfile ← newapplication.propertiesImplementation Plan
1. Create
DotEnvEnvironmentPostProcessorsrc/main/java/.../config/DotEnvEnvironmentPostProcessor.javaorg.springframework.boot.env.EnvironmentPostProcessorio.github.cdimascio.dotenv.Dotenvdirectly (Spring beans are not available at this lifecycle stage).envfromSystem.getProperty("user.dir")with.ignoreIfMissing()MapPropertySource("cwdDotenv", map)viaenvironment.getPropertySources().addLast(...)so OS env vars always win2. Register in
META-INF/spring.factoriessrc/main/resources/META-INF/spring.factories(new file)3. Update GraalVM native-image hints
reflect-config.json— add constructor entry forDotEnvEnvironmentPostProcessorresource-config.json— addMETA-INF/spring.factoriesso it is bundled in the native binary4. Tests
src/test/java/.../config/DotEnvEnvironmentPostProcessorTest.java.envto a temp dir, setuser.dirto that dir, callpostProcessEnvironment(), assert the property is visible.envvalue.envdoes not throwFiles to change
src/main/java/.../config/DotEnvEnvironmentPostProcessor.javasrc/main/resources/META-INF/spring.factoriessrc/main/resources/META-INF/native-image/.../reflect-config.jsonsrc/main/resources/META-INF/native-image/.../resource-config.jsonsrc/test/java/.../config/DotEnvEnvironmentPostProcessorTest.javaNo changes required to
InteractiveModeRunnerConfigurationorRunSuiteCommand.