Sunday, September 10, 2023

Using .env with maven

Dotfiles are quite popular in ruby and js ecosystem.
They are not popular however in java world as we can use maven (still not 0xDEAD ;) and pass configuration directly from the command line or create script for that. But why not take some standards from the other universes?
Having other ways to configure app provided by eg. spring boot it may be still useful for java developers.

You can use .env file to inject variables into your maven build, then it can get picked by spring boot and passed to configuration eg. while running tests.
Use case is to avoid putting the secrets into version control, while letting local integration tests to run. Without any extra script files and using tool that is known by other developers.

There is of course a maven plugin for that:
<groupid>io.github.mjourard</groupid>
<artifactid>env-file-maven-plugin</artifactid>
I have prepared a small example of usage with groovy and spring boot.
You can find it here: https://github.com/konopski/using-dotenv-with-maven.

It is a simplest spring boot application containing one bean (TheBean) which is using to configuration values. The very standard stuff to obtain a DB connection.
@Component
class TheBean {

    @Value('${spring.datasource.username}')
    String username

    @Value('${spring.datasource.password}')
    String pass
//...
Now spring lets us deliver the actual values using externalized config mechanism . Typically we use properties files and spring profiles. In our example there is a 'dev' profile defined, that provides a clear text password. It is good enough if you remeber to keep the dedicated profile properties file out of your version control.
The file (application-dev.properties) can look like this:
spring.datasource.password=secret
In the main application.properties file we do not define this entry - so in case it is missing (eg. ommiting the profile) the app will not start.

You can also have another mechanism controlling your password. Spring will take a corresponding definition from system environment.
In our case it is delivered by our .env file.
SPRING_DATASOURCE_PASSWORD=t3st_s3cr3t
That is verified in ApplicationTest class:

@SpringBootTest
class ApplicationTests {

    @Autowired TheBean bean

//...

    @Test
    void shouldReadFromDotEnv() {
            bean.pass == "t3st_s3cr3t"
    }
}
We can also use different values by using spring's properties overriding, adding to our .env file:
OVERRIDE_DB_PASS=password_override
And test for that:

@SpringBootTest(
    properties = ['spring.datasource.password=${OVERRIDE_DB_PASS}'] )
class PropsOverrideTest {

    @Autowired TheBean bean

//...

    @Test
    void shouldOverride() {
        bean.pass == "password_override1"
    }

}
For me this approach is quite clean, allows easy sharing among the team and composes quite well with existing environment.
And remember to keep your passwords away from git.