1
0
mirror of https://github.com/docker/compose.git synced 2025-04-08 17:05:13 +02:00

Added additional argument (--env-file) for docker-compose to import environment variables from a given PATH.

Signed-off-by: Dimitrios Mavrommatis <jim.mavrommatis@gmail.com>
This commit is contained in:
slowr 2019-02-22 18:40:28 -08:00 committed by Ulysses Souza
parent 572032fc0b
commit b09d8802ed
6 changed files with 61 additions and 9 deletions
compose
tests
acceptance
fixtures/default-env-file
unit/config

@ -39,7 +39,8 @@ SILENT_COMMANDS = set((
def project_from_options(project_dir, options):
override_dir = options.get('--project-directory')
environment = Environment.from_env_file(override_dir or project_dir)
environment_file = options.get('--env-file')
environment = Environment.from_env_file(override_dir or project_dir, environment_file)
environment.silent = options.get('COMMAND', None) in SILENT_COMMANDS
set_parallel_limit(environment)
@ -77,7 +78,8 @@ def set_parallel_limit(environment):
def get_config_from_options(base_dir, options):
override_dir = options.get('--project-directory')
environment = Environment.from_env_file(override_dir or base_dir)
environment_file = options.get('--env-file')
environment = Environment.from_env_file(override_dir or base_dir, environment_file)
config_path = get_config_path_from_options(
base_dir, options, environment
)

@ -208,6 +208,7 @@ class TopLevelCommand(object):
(default: the path of the Compose file)
--compatibility If set, Compose will attempt to convert keys
in v3 files to their non-Swarm equivalent
--env-file PATH Specify an alternate environment file
Commands:
build Build or rebuild services
@ -274,7 +275,8 @@ class TopLevelCommand(object):
'--build-arg is only supported when services are specified for API version < 1.25.'
' Please use a Compose file version > 2.2 or specify which services to build.'
)
environment = Environment.from_env_file(self.project_dir)
environment_file = options.get('--env-file')
environment = Environment.from_env_file(self.project_dir, environment_file)
build_args = resolve_build_args(build_args, environment)
self.project.build(
@ -423,8 +425,10 @@ class TopLevelCommand(object):
Compose file
-t, --timeout TIMEOUT Specify a shutdown timeout in seconds.
(default: 10)
--env-file PATH Specify an alternate environment file
"""
environment = Environment.from_env_file(self.project_dir)
environment_file = options.get('--env-file')
environment = Environment.from_env_file(self.project_dir, environment_file)
ignore_orphans = environment.get_boolean('COMPOSE_IGNORE_ORPHANS')
if ignore_orphans and options['--remove-orphans']:
@ -481,8 +485,10 @@ class TopLevelCommand(object):
-e, --env KEY=VAL Set environment variables (can be used multiple times,
not supported in API < 1.25)
-w, --workdir DIR Path to workdir directory for this command.
--env-file PATH Specify an alternate environment file
"""
environment = Environment.from_env_file(self.project_dir)
environment_file = options.get('--env-file')
environment = Environment.from_env_file(self.project_dir, environment_file)
use_cli = not environment.get_boolean('COMPOSE_INTERACTIVE_NO_CLI')
index = int(options.get('--index'))
service = self.project.get_service(options['SERVICE'])
@ -1038,6 +1044,7 @@ class TopLevelCommand(object):
container. Implies --abort-on-container-exit.
--scale SERVICE=NUM Scale SERVICE to NUM instances. Overrides the
`scale` setting in the Compose file if present.
--env-file PATH Specify an alternate environment file
"""
start_deps = not options['--no-deps']
always_recreate_deps = options['--always-recreate-deps']
@ -1052,7 +1059,8 @@ class TopLevelCommand(object):
if detached and (cascade_stop or exit_value_from):
raise UserError("--abort-on-container-exit and -d cannot be combined.")
environment = Environment.from_env_file(self.project_dir)
environment_file = options.get('--env-file')
environment = Environment.from_env_file(self.project_dir, environment_file)
ignore_orphans = environment.get_boolean('COMPOSE_IGNORE_ORPHANS')
if ignore_orphans and remove_orphans:
@ -1345,7 +1353,8 @@ def run_one_off_container(container_options, project, service, options, toplevel
if options['--rm']:
project.client.remove_container(container.id, force=True, v=True)
environment = Environment.from_env_file(project_dir)
environment_file = options.get('--env-file')
environment = Environment.from_env_file(project_dir, environment_file)
use_cli = not environment.get_boolean('COMPOSE_INTERACTIVE_NO_CLI')
signals.set_signal_handler_to_shutdown()

@ -59,12 +59,15 @@ class Environment(dict):
self.silent = False
@classmethod
def from_env_file(cls, base_dir):
def from_env_file(cls, base_dir, env_file=None):
def _initialize():
result = cls()
if base_dir is None:
return result
env_file_path = os.path.join(base_dir, '.env')
if env_file:
env_file_path = os.path.join(base_dir, env_file)
else:
env_file_path = os.path.join(base_dir, '.env')
try:
return cls(env_vars_from_file(env_file_path))
except EnvFileNotFound:

@ -324,6 +324,21 @@ class CLITestCase(DockerClientTestCase):
'version': '2.4'
}
def test_config_with_env_file(self):
self.base_dir = 'tests/fixtures/default-env-file'
result = self.dispatch(['--env-file', '.env2', 'config'])
json_result = yaml.load(result.stdout)
assert json_result == {
'services': {
'web': {
'command': 'false',
'image': 'alpine:latest',
'ports': ['5644/tcp', '9998/tcp']
}
},
'version': '2.4'
}
def test_config_with_dot_env_and_override_dir(self):
self.base_dir = 'tests/fixtures/default-env-file'
result = self.dispatch(['--project-directory', 'alt/', 'config'])

4
tests/fixtures/default-env-file/.env2 vendored Normal file

@ -0,0 +1,4 @@
IMAGE=alpine:latest
COMMAND=false
PORT1=5644
PORT2=9998

@ -3465,6 +3465,25 @@ class InterpolationTest(unittest.TestCase):
'command': 'true'
}
@mock.patch.dict(os.environ)
def test_config_file_with_options_environment_file(self):
project_dir = 'tests/fixtures/default-env-file'
service_dicts = config.load(
config.find(
project_dir, None, Environment.from_env_file(project_dir, '.env2')
)
).services
assert service_dicts[0] == {
'name': 'web',
'image': 'alpine:latest',
'ports': [
types.ServicePort.parse('5644')[0],
types.ServicePort.parse('9998')[0]
],
'command': 'false'
}
@mock.patch.dict(os.environ)
def test_config_file_with_environment_variable(self):
project_dir = 'tests/fixtures/environment-interpolation'