diff --git a/newsfragments/1165-PROJECT_NAME-interpolation.bugfix b/newsfragments/1165-PROJECT_NAME-interpolation.bugfix new file mode 100644 index 00000000..e4200e0a --- /dev/null +++ b/newsfragments/1165-PROJECT_NAME-interpolation.bugfix @@ -0,0 +1 @@ +- Fixed interpolation of the environment variable **COMPOSE_PROJECT_NAME** when it is set from the top-level **name** value within the Compose file. diff --git a/newsfragments/1165-project-name-evaluation-order.bugfix b/newsfragments/1165-project-name-evaluation-order.bugfix new file mode 100644 index 00000000..d8834e17 --- /dev/null +++ b/newsfragments/1165-project-name-evaluation-order.bugfix @@ -0,0 +1 @@ +- Fixed project name evaluation order to match the order defined in the [compose spec](https://docs.docker.com/compose/how-tos/project-name/#set-a-project-name). diff --git a/podman_compose.py b/podman_compose.py index df1f9ee9..62ec408e 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -1980,6 +1980,25 @@ def _parse_compose_file(self): sys.exit(1) content = normalize(content) # log(filename, json.dumps(content, indent = 2)) + + # See also https://docs.docker.com/compose/how-tos/project-name/#set-a-project-name + # **project_name** is initialized to the argument of the `-p` command line flag. + if not project_name: + project_name = self.environ.get("COMPOSE_PROJECT_NAME") + if not project_name: + project_name = content.get("name") + if not project_name: + project_name = dir_basename.lower() + # More strict then actually needed for simplicity: + # podman requires [a-zA-Z0-9][a-zA-Z0-9_.-]* + project_name_normalized = norm_re.sub("", project_name) + if not project_name_normalized: + raise RuntimeError(f"Project name [{project_name}] normalized to empty") + project_name = project_name_normalized + + self.project_name = project_name + self.environ.update({"COMPOSE_PROJECT_NAME": self.project_name}) + content = rec_subs(content, self.environ) if isinstance(services := content.get('services'), dict): for service in services.values(): @@ -2012,19 +2031,6 @@ def _parse_compose_file(self): log.debug(" ** merged:\n%s", json.dumps(compose, indent=2)) # ver = compose.get('version') - if not project_name: - project_name = compose.get("name") - if project_name is None: - # More strict then actually needed for simplicity: - # podman requires [a-zA-Z0-9][a-zA-Z0-9_.-]* - project_name = self.environ.get("COMPOSE_PROJECT_NAME", dir_basename.lower()) - project_name = norm_re.sub("", project_name) - if not project_name: - raise RuntimeError(f"Project name [{dir_basename}] normalized to empty") - - self.project_name = project_name - self.environ.update({"COMPOSE_PROJECT_NAME": self.project_name}) - services = compose.get("services") if services is None: services = {} diff --git a/tests/integration/env-tests/container-compose.yml b/tests/integration/env-tests/container-compose.yml index 3a8b28eb..4498f09d 100644 --- a/tests/integration/env-tests/container-compose.yml +++ b/tests/integration/env-tests/container-compose.yml @@ -1,5 +1,7 @@ version: "3" +name: my-project-name + services: env-test: image: busybox @@ -8,3 +10,9 @@ services: ZZVAR1: myval1 ZZVAR2: 2-$ZZVAR1 ZZVAR3: 3-$ZZVAR2 + + project-name-test: + image: busybox + command: sh -c "echo $$PNAME" + environment: + PNAME: ${COMPOSE_PROJECT_NAME} diff --git a/tests/integration/env-tests/test_podman_compose_env.py b/tests/integration/env-tests/test_podman_compose_env.py index 351644f3..f1c35cf4 100644 --- a/tests/integration/env-tests/test_podman_compose_env.py +++ b/tests/integration/env-tests/test_podman_compose_env.py @@ -36,3 +36,54 @@ def test_env(self): compose_yaml_path(), "down", ]) + + """ + Tests interpolation of COMPOSE_PROJECT_NAME in the podman-compose config, + which is different from external environment variables because COMPOSE_PROJECT_NAME + is a predefined environment variable generated from the `name` value in the top-level + of the compose.yaml. + + See also + - https://docs.docker.com/reference/compose-file/interpolation/ + - https://docs.docker.com/reference/compose-file/version-and-name/#name-top-level-element + - https://docs.docker.com/compose/how-tos/environment-variables/envvars/ + - https://github.com/compose-spec/compose-spec/blob/main/04-version-and-name.md + """ + + def test_project_name(self): + try: + output, _ = self.run_subprocess_assert_returncode([ + podman_compose_path(), + "-f", + compose_yaml_path(), + "run", + "project-name-test", + ]) + self.assertIn("my-project-name", str(output)) + finally: + self.run_subprocess_assert_returncode([ + podman_compose_path(), + "-f", + compose_yaml_path(), + "down", + ]) + + def test_project_name_override(self): + try: + output, _ = self.run_subprocess_assert_returncode([ + podman_compose_path(), + "-f", + compose_yaml_path(), + "run", + "-e", + "COMPOSE_PROJECT_NAME=project-name-override", + "project-name-test", + ]) + self.assertIn("project-name-override", str(output)) + finally: + self.run_subprocess_assert_returncode([ + podman_compose_path(), + "-f", + compose_yaml_path(), + "down", + ])