Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add project_name top-level config key #6815

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 8 additions & 3 deletions compose/cli/command.py
Expand Up @@ -129,10 +129,10 @@ def get_project(project_dir, config_path=None, project_name=None, verbose=False,
if not environment:
environment = Environment.from_env_file(project_dir)
config_details = config.find(project_dir, config_path, environment, override_dir)
config_data = config.load(config_details, compatibility, interpolate)
project_name = get_project_name(
config_details.working_dir, project_name, environment
config_details.working_dir, config_data, project_name, environment
)
config_data = config.load(config_details, compatibility, interpolate)

api_version = environment.get(
'COMPOSE_API_VERSION',
Expand All @@ -149,7 +149,7 @@ def get_project(project_dir, config_path=None, project_name=None, verbose=False,
)


def get_project_name(working_dir, project_name=None, environment=None):
def get_project_name(working_dir, config_data=None, project_name=None, environment=None):
def normalize_name(name):
return re.sub(r'[^-_a-z0-9]', '', name.lower())

Expand All @@ -159,6 +159,11 @@ def normalize_name(name):
if project_name:
return normalize_name(project_name)

if config_data:
project_name = config_data.project_name
if project_name:
return normalize_name(project_name)

project = os.path.basename(os.path.abspath(working_dir))
if project:
return normalize_name(project)
Expand Down
24 changes: 22 additions & 2 deletions compose/config/config.py
Expand Up @@ -240,8 +240,11 @@ def get_secrets(self):
def get_configs(self):
return {} if self.version < const.COMPOSEFILE_V3_3 else self.config.get('configs', {})

def get_project_name(self):
return {} if self.version < const.COMPOSEFILE_V3_7 else self.config.get('project_name', '')

class Config(namedtuple('_Config', 'version services volumes networks secrets configs')):

class Config(namedtuple('_Config', 'version services volumes networks secrets configs project_name')):
"""
:param version: configuration version
:type version: int
Expand All @@ -255,6 +258,8 @@ class Config(namedtuple('_Config', 'version services volumes networks secrets co
:type secrets: :class:`dict`
:param configs: Dictionary mapping config names to description dictionaries
:type configs: :class:`dict`
:param project_name The name of the project
:type project_name: str
"""


Expand Down Expand Up @@ -401,6 +406,13 @@ def load(config_details, compatibility=False, interpolate=True):
configs = load_mapping(
config_details.config_files, 'get_configs', 'Config', config_details.working_dir
)

project_name = ''
for config_file in config_details.config_files:
name = config_file.get_project_name()
if name:
project_name = name

service_dicts = load_services(config_details, main_file, compatibility)

if main_file.version != V1:
Expand All @@ -411,7 +423,7 @@ def load(config_details, compatibility=False, interpolate=True):

version = V2_3 if compatibility and main_file.version >= V3_0 else main_file.version

return Config(version, service_dicts, volumes, networks, secrets, configs)
return Config(version, service_dicts, volumes, networks, secrets, configs, project_name)


def load_mapping(config_files, get_func, entity_type, working_dir=None):
Expand Down Expand Up @@ -568,6 +580,14 @@ def process_config_file(config_file, environment, service_name=None, interpolate
environment,
interpolate,
)
if config_file.version > const.COMPOSEFILE_V3_7:
processed_config['project_name'] = process_config_section(
config_file,
config_file.get_project_name(),
'project_name',
environment,
interpolate,
)
else:
processed_config = services

Expand Down
4 changes: 4 additions & 0 deletions compose/config/config_schema_v3.7.json
Expand Up @@ -9,6 +9,10 @@
"type": "string"
},

"project_name": {
"type": "string"
},

"services": {
"id": "#/properties/services",
"type": "object",
Expand Down
1 change: 1 addition & 0 deletions tests/integration/project_test.py
Expand Up @@ -55,6 +55,7 @@ def build_config(**kwargs):
networks=kwargs.get('networks'),
secrets=kwargs.get('secrets'),
configs=kwargs.get('configs'),
project_name=kwargs.get('project_name'),
)


Expand Down
3 changes: 2 additions & 1 deletion tests/unit/bundle_test.py
Expand Up @@ -91,7 +91,8 @@ def test_to_bundle():
volumes={'special': {}},
networks={'extra': {}},
secrets={},
configs={}
configs={},
project_name=''
)

with mock.patch('compose.bundle.log.warning', autospec=True) as mock_log:
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/config/config_test.py
Expand Up @@ -5278,7 +5278,7 @@ def test_serialize_ports(self):
'image': 'alpine',
'name': 'web'
}
], volumes={}, networks={}, secrets={}, configs={})
], volumes={}, networks={}, secrets={}, configs={}, project_name='')

serialized_config = yaml.load(serialize_config(config_dict))
assert '8080:80/tcp' in serialized_config['services']['web']['ports']
Expand All @@ -5290,7 +5290,7 @@ def test_serialize_ports_with_ext_ip(self):
'image': 'alpine',
'name': 'web'
}
], volumes={}, networks={}, secrets={}, configs={})
], volumes={}, networks={}, secrets={}, configs={}, project_name='')

serialized_config = yaml.load(serialize_config(config_dict))
assert '127.0.0.1:8080:80/tcp' in serialized_config['services']['web']['ports']
Expand Down
17 changes: 16 additions & 1 deletion tests/unit/project_test.py
Expand Up @@ -36,6 +36,7 @@ def setUp(self):

def test_from_config_v1(self):
config = Config(
project_name='',
version=V1,
services=[
{
Expand Down Expand Up @@ -67,6 +68,7 @@ def test_from_config_v1(self):
@mock.patch('compose.network.Network.true_name', lambda n: n.full_name)
def test_from_config_v2(self):
config = Config(
project_name='',
version=V2_0,
services=[
{
Expand Down Expand Up @@ -174,6 +176,7 @@ def test_use_volumes_from_container(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[{
'name': 'test',
Expand Down Expand Up @@ -202,6 +205,7 @@ def test_use_volumes_from_service_no_container(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[
{
Expand Down Expand Up @@ -230,6 +234,7 @@ def test_use_volumes_from_service_container(self):
name='test',
client=None,
config_data=Config(
project_name='',
version=V2_0,
services=[
{
Expand Down Expand Up @@ -540,6 +545,7 @@ def test_net_unset(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V1,
services=[
{
Expand All @@ -565,6 +571,7 @@ def test_use_net_from_container(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[
{
Expand Down Expand Up @@ -596,6 +603,7 @@ def test_use_net_from_service(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[
{
Expand Down Expand Up @@ -623,6 +631,7 @@ def test_uses_default_network_true(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[
{
Expand All @@ -644,6 +653,7 @@ def test_uses_default_network_false(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[
{
Expand Down Expand Up @@ -679,6 +689,7 @@ def test_container_without_name(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[{
'name': 'web',
Expand All @@ -697,6 +708,7 @@ def test_down_with_no_resources(self):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[{
'name': 'web',
Expand Down Expand Up @@ -748,7 +760,8 @@ def test_project_platform_value(self):
'image': BUSYBOX_IMAGE_WITH_TAG,
}
config_data = Config(
version=V2_4, services=[service_config], networks={}, volumes={}, secrets=None, configs=None
project_name='', version=V2_4, services=[service_config], networks={},
volumes={}, secrets=None, configs=None
)

project = Project.from_config(name='test', client=self.mock_client, config_data=config_data)
Expand All @@ -770,6 +783,7 @@ def test_project_platform_value(self):

def test_build_container_operation_with_timeout_func_does_not_mutate_options_with_timeout(self):
config_data = Config(
project_name='',
version=V3_7,
services=[
{'name': 'web', 'image': BUSYBOX_IMAGE_WITH_TAG},
Expand Down Expand Up @@ -802,6 +816,7 @@ def test_error_parallel_pull(self, mock_write):
name='test',
client=self.mock_client,
config_data=Config(
project_name='',
version=V2_0,
services=[{
'name': 'web',
Expand Down