paulschwarz / spring-dotenv

Provides a Dotenv property source for Spring
MIT License
301 stars 23 forks source link

Recursively look for the .env file in the parent directories #22

Open JoaoBrlt opened 11 months ago

JoaoBrlt commented 11 months ago

Hi there,

First of all, many thanks for this simple project. It makes it really easy to import environment variables from .env files without worrying too much about technical details.

I was wondering if it would be possible to recursively look for the .env file in the parent directories of the Spring Boot project? Indeed, I would like to define a single .env file at the root of a monorepo containing multiple Spring Boot projects. It would make things even easier in my case, by avoiding having to define a .env file for each project or configuring the source directory (with a .env.properties file) and using relative paths (that may break if I decide to move projects around). I only configure a few environment variables and many are shared so it would be really cool to use a single .env file at the root of the repository.

I am open to contributing to the project if it is not currently possible and if you find it useful.

Thanks!

paulschwarz commented 11 months ago

Hi @JoaoBrlt

The library delegates to dotenv-java, so the "directory" property can be understood by reading https://github.com/cdimascio/dotenv-java#optional-directory

I must say, I haven't come across the need for a parent .env file as you're describing. My main objections would be: it breaks the connection to the project where it is actually used, making it harder for you to then figure out which environment variables are needed to be configured in your deployment pipeline.

It's not the strongest argument, so if you consider this useful, then perhaps look into the code and see if you can propose a clean, non-breaking way to support this.

JoaoBrlt commented 11 months ago

Hi @paulschwarz !

I looked a bit into the dotenv-java project. I think this feature would maybe add too much complexity to this simple project. I'll probably create an issue there to see if someone else would need this feature.

In the meantime, I managed to import environment variables from the closest environment file in my Spring Boot applications using a custom EnvironmentPostProcessor.

I'm sharing my code here in case someone else needs it:

@Component
@Profile("dev")
public class EnvironmentConfiguration implements EnvironmentPostProcessor, ApplicationListener<ApplicationEvent> {

    private static final DeferredLog log = new DeferredLog();

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        if (environment.acceptsProfiles(Profiles.of("dev"))) {
            loadEnvFile(environment);
        }
    }

    @Override
    public void onApplicationEvent(@Nonnull ApplicationEvent event) {
        log.replayTo(EnvironmentConfiguration.class);
    }

    private void loadEnvFile(ConfigurableEnvironment environment) {
        Optional<File> envFile = findFile(".env");
        if (envFile.isPresent()) {
            File file = envFile.get();
            try (FileInputStream fileInputStream = new FileInputStream(file)) {
                Properties properties = new Properties();
                properties.load(fileInputStream);
                PropertiesPropertySource propertySource = new PropertiesPropertySource("dotenv", properties);
                environment.getPropertySources().addFirst(propertySource);
                log.info("Loaded environment variables from the environment file: " + file.getAbsolutePath());
            } catch (IOException exception) {
                log.error("Failed to load environment variables from the environment file: " + file.getAbsolutePath(), exception);
            }
        } else {
            log.info("No environment file was found.");
        }
    }

    private Optional<File> findFile(String name) {
        File currentDirectory = new File(System.getProperty("user.dir"));
        File file = new File(currentDirectory, name);

        while (!file.exists() && currentDirectory.getParentFile() != null) {
            currentDirectory = currentDirectory.getParentFile();
            file = new File(currentDirectory, name);
        }

        return file.exists() ? Optional.of(file) : Optional.empty();
    }

}

I also had to create a file in META-INF called spring.factories to declare the custom EnvironmentPostProcessor:

org.springframework.boot.env.EnvironmentPostProcessor=com.myorg.app.EnvironmentConfiguration

Best regards.