Skip to content

Commit

Permalink
Rework config validation format
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane Brown committed Aug 23, 2023
1 parent c330ef4 commit 3ede554
Showing 1 changed file with 99 additions and 96 deletions.
195 changes: 99 additions & 96 deletions buildrunner/config_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from typing import Dict, List, Optional, Set, Union

from pydantic import BaseModel # pylint: disable=no-name-in-module
from pydantic import BaseModel, validator # pylint: disable=no-name-in-module


class StepBuild(BaseModel):
Expand Down Expand Up @@ -38,117 +38,120 @@ def is_multi_platform(self):
return self.build is not None and \
self.build.platforms is not None

# def has_multi_platform_build(self):
# """
# Check if the config file has multi-platform build steps

# Returns:
# bool: True if the config file has multi-platform build steps, False otherwise
# """
# if self.steps is None:
# return False

# for step in self.steps.values():
# if step.is_multi_platform():
# return True
# return False


class Config(BaseModel):
""" Top level config model """
version: Optional[float]
steps: Optional[Dict[str, Step]]

def __init__(self, **data) -> None:
super().__init__(**data)
self.validate()

def has_multi_platform_build(self):
"""
Check if the config file has multi-platform build steps
Returns:
bool: True if the config file has multi-platform build steps, False otherwise
"""
if self.steps is None:
return False

for step in self.steps.values():
if step.is_multi_platform():
return True
return False

def validate_push(self,
push: Union[StepPushDict, List[Union[str, StepPushDict]], str],
mp_push_tags: Set[str],
step_name: str,
update_mp_push_tags: bool = True):
"""
Validate push step
Args:
push (StepPushDict | list[str | StepPushDict] | str): Push step
mp_push_tags (Set[str]): Set of all tags used in multi-platform build steps
step_name (str): Name of the step
update_mp_push_tags (bool, optional): Whether to update the set of tags used in multi-platform steps.
Raises:
ValueError: If the config file is invalid
"""
# Check for valid push section, duplicate mp tags are not allowed
if push is not None:
name = None
names = None
if isinstance(push, str):
name = push
if ":" not in name:
name = f'{name}:latest'

if isinstance(push, StepPushDict):
names = [f"{push.repository}:{tag}" for tag in push.tags]

if names is not None:
for current_name in names:
if current_name in mp_push_tags:
# raise ValueError(f'Cannot specify duplicate tag {current_name} in build step {step_name}')
raise ValueError(f'Cannot specify duplicate tag {current_name} in build step {step_name}')

if name is not None and name in mp_push_tags:
# raise ValueError(f'Cannot specify duplicate tag {name} in build step {step_name}')
raise ValueError(f'Cannot specify duplicate tag {name} in build step {step_name}')

if update_mp_push_tags and names is not None:
mp_push_tags.update(names)

if update_mp_push_tags and name is not None:
mp_push_tags.add(name)

def validate_multi_platform_build(self, mp_push_tags: Set[str]):
"""
Validate multi-platform build steps
Args:
mp_push_tags (Set[str]): Set of all tags used in multi-platform build steps
Raises:
ValueError | pydantic.ValidationError: If the config file is invalid
"""
# Iterate through each step
for step_name, step in self.steps.items():
if step.is_multi_platform():
if step.build.platform is not None:
raise ValueError(f'Cannot specify both platform ({step.build.platform}) and '
f'platforms ({step.build.platforms}) in build step {step_name}')

if not isinstance(step.build.platforms, list):
raise ValueError(f'platforms must be a list in build step {step_name}')

# Check for valid push section, duplicate mp tags are not allowed
self.validate_push(step.push, mp_push_tags, step_name)

def validate(self):
# Note this is pydantic version 1.10 syntax
@validator('steps')
def validate_steps(cls, values) -> None:
"""
Validate the config file
Raises:
ValueError | pydantic.ValidationError : If the config file is invalid
"""
if self.has_multi_platform_build():

def validate_push(push: Union[StepPushDict, List[Union[str, StepPushDict]], str],
mp_push_tags: Set[str],
step_name: str,
update_mp_push_tags: bool = True):
"""
Validate push step
Args:
push (StepPushDict | list[str | StepPushDict] | str): Push step
mp_push_tags (Set[str]): Set of all tags used in multi-platform build steps
step_name (str): Name of the step
update_mp_push_tags (bool, optional): Whether to update the set of tags used in multi-platform steps.
Raises:
ValueError: If the config file is invalid
"""
# Check for valid push section, duplicate mp tags are not allowed
if push is not None:
name = None
names = None
if isinstance(push, str):
name = push
if ":" not in name:
name = f'{name}:latest'

if isinstance(push, StepPushDict):
names = [f"{push.repository}:{tag}" for tag in push.tags]

if names is not None:
for current_name in names:
if current_name in mp_push_tags:
# raise ValueError(f'Cannot specify duplicate tag {current_name} in build step {step_name}')
raise ValueError(f'Cannot specify duplicate tag {current_name} in build step {step_name}')

if name is not None and name in mp_push_tags:
# raise ValueError(f'Cannot specify duplicate tag {name} in build step {step_name}')
raise ValueError(f'Cannot specify duplicate tag {name} in build step {step_name}')

if update_mp_push_tags and names is not None:
mp_push_tags.update(names)

if update_mp_push_tags and name is not None:
mp_push_tags.add(name)

def validate_multi_platform_build(mp_push_tags: Set[str]):
"""
Validate multi-platform build steps
Args:
mp_push_tags (Set[str]): Set of all tags used in multi-platform build steps
Raises:
ValueError | pydantic.ValidationError: If the config file is invalid
"""
# Iterate through each step
for step_name, step in values.items():
if step.is_multi_platform():
if step.build.platform is not None:
raise ValueError(f'Cannot specify both platform ({step.build.platform}) and '
f'platforms ({step.build.platforms}) in build step {step_name}')

if not isinstance(step.build.platforms, list):
raise ValueError(f'platforms must be a list in build step {step_name}')

# Check for valid push section, duplicate mp tags are not allowed
validate_push(step.push, mp_push_tags, step_name)

has_multi_platform_build = False
for step in values.values():
has_multi_platform_build = has_multi_platform_build or step.is_multi_platform()

if has_multi_platform_build:
mp_push_tags = set()
self.validate_multi_platform_build(mp_push_tags)
validate_multi_platform_build(mp_push_tags)

# Validate that all tags are unique across all multi-platform step
for step_name, step in self.steps.items():
for step_name, step in values.items():

# Check that there are no single platform tags that match multi-platform tags
if not step.is_multi_platform():
if step.push is not None:
self.validate_push(push=step.push,
mp_push_tags=mp_push_tags,
step_name=step_name,
update_mp_push_tags=False)
validate_push(push=step.push,
mp_push_tags=mp_push_tags,
step_name=step_name,
update_mp_push_tags=False)
return values

0 comments on commit 3ede554

Please sign in to comment.