How to load and export variables from an .env file in Makefile?

17,724

Solution 1

Make does not offer any way to read a content of the file to some variable. So, I consider it impossible to achieve the result without using external tools. However, if I am wrong, I'd be glad to learn some new trick.

So, let's assume there are two files, .env, being a technically correct shell file:

FOO=bar

BAR="notfoo" # comment
   #comment
MULTILINE="This\nis\nSparta!"
# comment

and script.sh:

#!/bin/bash
echo FOO=${FOO}
echo BAR=${BAR}
echo -e ${MULTILINE}

One solution is to include the .env file, then make sure variables are exported:

include .env

$(eval export $(shell sed -ne 's/ *#.*$$//; /./ s/=.*$$// p' .env))

all:
    ./script.sh

Because of different treatment of quotes by shell and make, you will see the quotes in output.

You can avoid that by reprocessing the variables by make:

include .env

VARS:=$(shell sed -ne 's/ *\#.*$$//; /./ s/=.*$$// p' .env )
$(foreach v,$(VARS),$(eval $(shell echo export $(v)="$($(v))")))

all:
    ./script.sh

but then the multiline variable will become a one-liner.

Finally, you can generate a temporary file to be processed by bash and source it before any command is run:

SHELL=bash

all: .env-export
    . .env-export && ./script.sh

.env-export: .env
    sed -ne '/^export / {p;d}; /.*=/ s/^/export / p' .env > .env-export

Oh, new lines got messed in this case in multiline variable. You need to additionally quote them.

Finally, you can add export to .env using above sed command, and do:

SHELL=bash
%: .env-export
    . .env-export && make -f secondary "$@"

Solution 2

Found this and it worked great:

at top of makefile

ifneq (,$(wildcard ./.env))
    include .env
    export
endif

Then you have make variables for all your env, for example MY_VAR use as $(MY_VAR)

Solution 3

You can load specific .env file for each target by creating a function and target to use it with other targets when necessary. Here as an sample:

define setup_env
    $(eval ENV_FILE := $(1).env)
    @echo " - setup env $(ENV_FILE)"
    $(eval include $(1).env)
    $(eval export)
endef

devEnv: 
    $(call setup_env, dev)

prodEnv: 
    $(call setup_env, prod)

clean:
    rm -rf  bin/

build: clean
    GOOS=linux GOARCH=amd64 go build -o bin/ ./cmd/...

dev: build devEnv
    cd cmd/api && ./../../bin/api
 
migrate-dev-db: devEnv
    sh +x database/migration/migrate.sh dev

migrate-prod-db: prodEnv
    sh +x database/migration/migrate.sh


deploy: prodEnv
    sh +x script/deployment/production/ec2-deploy.sh
Share:
17,724

Related videos on Youtube

Bastian Venthur
Author by

Bastian Venthur

Free- and Open Source Software advocate, Debian Developer.

Updated on September 16, 2022

Comments

  • Bastian Venthur
    Bastian Venthur over 1 year

    What is the best way to use a .env in a Makefile, i.e. loading that file and exporting all variables for subshells in make?

    It would be great if the proposed solution would work with make only, e.g. not using any third party tools. Also .env files support multiline variables like:

    FOO="this\nis\na\nmultiline\nvar"

    this is why this solution is probably not adequate.

    • Bastian Venthur
      Bastian Venthur almost 7 years
      Not really. It might load the variables in most cases properly, but it does not automatically export them as environment for commands in sub shells right?
  • Bastian Venthur
    Bastian Venthur almost 7 years
    Alright, I think you convinced me to use an external tool. Thank you for the elaborate answer!
  • Bastian Venthur
    Bastian Venthur almost 7 years
    The whole treatment of quotes in shell vs make makes it really messy. One other solution seems to be to prepend each command with . .env; , i.e. . env; echo $FOO
  • ArturFH
    ArturFH almost 7 years
    In example given by you $FOO is expanded by the same shell sourcing .env file. To run . .env; ./script.sh variables need to be already exported in .env file, to be visisble inside ./script.sh.