Implement short syntax for env variables in compose.yml "environment:"

This commit allows compose file to directly use environment variable
values in "environment:" section when variables were set in `.env` file.
This functionality was missing, as docker-compose supports both: short
and variable interpolation syntax forms:
environment:
	- FOO
and
environment:
	- FOO=${FOO}
Relevant docker-compose documentation:
https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/
podman-compose is more compatible with docker-compose after this change.

Signed-off-by: Monika Kairaityte <monika@kibit.lt>
This commit is contained in:
Monika Kairaityte
2025-06-25 14:41:48 +03:00
parent 7105198ae1
commit 0cbf70a4e9
6 changed files with 90 additions and 2 deletions

View File

@ -0,0 +1 @@
Implemented short syntax for environment variables set in `.env` for compose.yml "environment:" section.

View File

@ -1159,7 +1159,14 @@ async def container_to_args(
podman_args.extend(["-e", e])
env = norm_as_list(cnt.get("environment", {}))
for e in env:
podman_args.extend(["-e", e])
# new environment variable is set
if "=" in e:
podman_args.extend(["-e", e])
else:
# environment variable already exists in environment so pass its value
if e in compose.environ.keys():
podman_args.extend(["-e", f"{e}={compose.environ[e]}"])
tmpfs_ls = cnt.get("tmpfs", [])
if isinstance(tmpfs_ls, str):
tmpfs_ls = [tmpfs_ls]

View File

@ -1,2 +1,3 @@
ZZVAR1='This value is loaded but should be overwritten'
ZZVAR2='This value is loaded from .env in project/ directory'
ZZVAR3=TEST

View File

@ -0,0 +1,10 @@
services:
app:
image: nopush/podman-compose-test
command: ["/bin/busybox", "sh", "-c", "env | grep ZZVAR3"]
# 'env_file:' section is not used, so .env file is searched in the same directory as compose.yml
# file
environment:
# this is short syntax: podman-compose takes only this variable value from '.env' file and
# sends it to container environment
- ZZVAR3

View File

@ -233,7 +233,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
[
'ZZVAR1=This value is loaded but should be overwritten\r',
'ZZVAR2=This value is loaded from .env in project/ directory\r',
'ZZVAR3=\r',
'ZZVAR3=TEST\r',
'',
],
)
@ -244,3 +244,36 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
path_compose_file,
"down",
])
def test_env_var_value_accessed_in_compose_file_short_syntax(self) -> None:
# Test that compose file can access the environment variable set in .env file using
# short syntax, that is: only the name of environment variable is used in "environment:" in
# compose.yml file and its value is picked up directly from .env file
# long syntax of environment variables interpolation is tested in
# tests/integration/interpolation
base_path = compose_base_path()
compose_file_path = os.path.join(base_path, "project/container-compose.short_syntax.yaml")
try:
self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_file_path,
"up",
"-d",
])
output, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_file_path,
"logs",
])
# ZZVAR3 was set in .env file
self.assertEqual(output, b"ZZVAR3=TEST\n")
finally:
self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_file_path,
"down",
])

View File

@ -262,6 +262,42 @@ class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
],
)
@parameterized.expand([
# short syntax: only take this specific environment variable value from .env file
("use_env_var_from_default_env_file_short_syntax", ["ZZVAR1"], "ZZVAR1=TEST1"),
# long syntax: environment variable value from .env file is taken through variable
# interpolation
# only the value required in 'environment:' compose file is sent to containers
# environment
("use_env_var_from_default_env_file_long_syntax", ["ZZVAR1=TEST1"], "ZZVAR1=TEST1"),
# "environment:" section in compose file overrides environment variable value from .env file
(
"use_env_var_from_default_env_file_override_value",
["ZZVAR1=NEW_TEST1"],
"ZZVAR1=NEW_TEST1",
),
])
async def test_env_file(self, test_name: str, cnt_env: list, expected_var: str) -> None:
c = create_compose_mock()
# environment variables were set in .env file
c.environ = {"ZZVAR1": "TEST1", "ZZVAR2": "TEST2"}
cnt = get_minimal_container()
cnt["environment"] = cnt_env
args = await container_to_args(c, cnt)
self.assertEqual(
args,
[
"--name=project_name_service_name1",
"-d",
"-e",
f"{expected_var}",
"--network=bridge:alias=service_name",
"busybox",
],
)
async def test_env_file_str(self) -> None:
c = create_compose_mock()