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.
45 Answers
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!"
# commentand 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.shBecause 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.shbut 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-exportOh, 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 "$@" 3 Found this and it worked great:
at top of makefile
ifneq (,$(wildcard ./.env)) include .env export
endifThen you have make variables for all your env, for example MY_VAR use as $(MY_VAR)
1You 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 2 I faced the same issue and here how I solve it.
- First, I recommend checking if the
.envfile exists, otherwise theincludewill raise an exception. - Second, feel free to add the
.env-examplefile to your repo.
ENVFILE = .env ENVFILE_EXAMPLE = .env-example # Create the .env file if not exit. ifeq ("$(wildcard $(ENVFILE))","") $(shell cp $(ENVFILE_EXAMPLE) $(ENVFILE)) endif # Load the environment variables from .env file include $(ENVFILE) export $(shell sed '/^\#/d; s/=.*//' $(ENVFILE)) FYI: this approach does handle comments also, which is not supposed by default by Makefile syntax.
Sanhoj - Thanks
Here's a sample .env and makefile usage:
.env:
DATABASE_URL=postgres://postgres:[email protected]:5432/my_db_development?sslmode=disablemakefile:
include .env
migrate: migrate -source file://postgres/migrations \ -database $(DATABASE_URL) up