integrate release as mixin

This commit is contained in:
Michael Jerger 2023-04-29 22:03:44 +02:00
parent 26b76a045a
commit d670605d37
7 changed files with 133 additions and 53 deletions

View file

@ -45,7 +45,7 @@ classDiagram
Devops *-- "0..1" Image: spcialized_builds
Devops *-- "0..1" C4k: spcialized_builds
Devops *-- Release: release
Devops *-- "0..1" Release: mixins
C4k *-- DnsRecord
Release *-- "0..1" ReleaseContext

View file

@ -4,13 +4,20 @@ from typing import List, TypedDict
import logging
import deprecation
def filter_none(list_to_filter):
return [x for x in list_to_filter if x is not None]
class BuildType(Enum):
IMAGE = 0
C4K = 1
class MixinType(Enum):
RELEASE = 0
class Validateable:
def __validate_is_not_none__(self, field_name: str) -> List[str]:
value = self.__dict__[field_name]
@ -32,9 +39,10 @@ class Validateable:
def throw_if_invalid(self):
if not self.is_valid():
issues = '\n'.join(self.validate())
issues = "\n".join(self.validate())
raise ValueError(f"Invalid Validateable: {issues}")
class DnsRecord(Validateable):
def __init__(self, fqdn, ipv4=None, ipv6=None):
self.fqdn = fqdn
@ -50,13 +58,19 @@ class DnsRecord(Validateable):
class Devops(Validateable):
def __init__(self, input: dict, specialized_builds: dict[BuildType, Validateable]):
self.stage = input.get('stage')
self.project_root_path = input.get('project_root_path')
self.module = input.get('module')
self.name = input.get('name', self.module)
self.build_dir_name = input.get('build_dir_name', 'target')
self.specialized_builds=specialized_builds
def __init__(
self,
input: dict,
specialized_builds: dict[BuildType, Validateable],
mixins: dict[MixinType, Validateable],
):
self.stage = input.get("stage")
self.project_root_path = input.get("project_root_path")
self.module = input.get("module")
self.name = input.get("name", self.module)
self.build_dir_name = input.get("build_dir_name", "target")
self.specialized_builds = specialized_builds
self.mixins = mixins
def build_path(self):
path = [self.project_root_path, self.build_dir_name, self.name, self.module]
@ -71,6 +85,9 @@ class Devops(Validateable):
if self.specialized_builds:
for build in self.specialized_builds:
result += self.specialized_builds[build].validate()
if self.mixins:
for mixin in self.mixins:
result += self.mixins[mixin].validate()
return result
def __put__(self, key, value):

View file

@ -1,9 +1,11 @@
import deprecation
from enum import Enum
from typing import List
from .common import Devops, BuildType
from .common import Devops, BuildType, MixinType
from .image import Image
from .c4k import C4k
from .release import Release, ReleaseContext
class DevopsFactory:
def __init__(self):
@ -11,13 +13,20 @@ class DevopsFactory:
def build_devops(self, input) -> Devops:
build_types = self.__parse_build_types__(input["build_types"])
mixin_types = self.__parse_mixin_types__(input["mixin_types"])
specialized_builds = {}
if BuildType.IMAGE in build_types:
specialized_builds[BuildType.IMAGE] = Image(input)
if BuildType.C4K in build_types:
specialized_builds[BuildType.C4K] = C4k(input)
devops = Devops(input, specialized_builds=specialized_builds)
mixins = {}
if MixinType.RELEASE in mixin_types:
release_context = ReleaseContext(input)
mixins[MixinType.RELEASE] = Release(input, release_context=release_context)
devops = Devops(input, specialized_builds=specialized_builds, mixins=mixins)
devops.throw_if_invalid()
@ -31,3 +40,9 @@ class DevopsFactory:
for build_type in build_types:
result += [BuildType[build_type]]
return result
def __parse_mixin_types__(self, mixin_types: List[str]) -> List[MixinType]:
result = []
for mixin_type in mixin_types:
result += [MixinType[mixin_type]]
return result

View file

@ -6,18 +6,21 @@ from .common import (
Devops,
)
class ReleaseType(Enum):
MAJOR = 0
MINOR = 1
PATCH = 2
SNAPSHOT = 3
BUMP = None
NONE = 15
class EnvironmentKeys(Enum):
DDADEVOPS_RELEASE_TYPE = 0
class Version():
class Version(Validateable):
def __init__(self, path: Path, version_list: list):
self.path = path
self.version_list = version_list
@ -62,11 +65,25 @@ class Version():
bump_version.increment(ReleaseType.BUMP)
return bump_version
class ReleaseContext(Validateable):
def __init__(self, release_type: ReleaseType | None, version: Version, current_branch: str):
self.release_type = release_type
self.version = version
self.current_branch = current_branch
def __init__(
self,
input: dict,
):
self.release_type = ReleaseType[input.get("release_type", "SNAPSHOT")]
self.release_current_version = input.get("release_current_version")
self.release_current_branch = input.get("release_current_branch")
self.version = self.__version_from_str__()
# TODO: mv version parsing to version
def __version_from_str__(self):
if not self.release_current_version:
return
version_parsed = []
for x in self.release_current_version.split("."):
version_parsed += [int(x)]
return Version("unused", version_parsed)
def release_version(self) -> Version:
return self.version.create_release_version(self.release_type)
@ -77,30 +94,32 @@ class ReleaseContext(Validateable):
def validate(self):
result = []
result += self.__validate_is_not_empty__("release_type")
result += self.__validate_is_not_empty__("version")
result += self.__validate_is_not_empty__("current_branch")
result += self.__validate_is_not_empty__("release_current_version")
result += self.__validate_is_not_empty__("release_current_branch")
if self.version:
result += self.version.validate()
return result
def validate_branch(self, main_branch: str):
result = []
if self.release_type is not None and main_branch != self.current_branch:
if (
self.release_type is not None
and self.release_type != ReleaseType.NONE
and main_branch != self.release_current_branch
):
result.append(f"Releases are allowed only on {main_branch}")
return result
class Release(Validateable):
def __init__(
self,
devops: Devops,
main_branch: str,
config_file: str,
input: dict,
release_context: ReleaseContext,
):
self.devops = devops
self.main_branch = main_branch
self.config_file = config_file
self.release_context: ReleaseContext | None = None
def set_release_context(self, set_release_context: ReleaseContext):
self.release_context = set_release_context
self.release_main_branch = input.get("release_main_branch", "main")
self.release_config_file = input.get("release_config_file", "project.clj")
self.release_context = release_context
def release_version(self):
return self.release_context.release_version()
@ -108,14 +127,12 @@ class Release(Validateable):
def bump_version(self):
return self.release_context.bump_version()
def validate(self):
result = []
result += self.__validate_is_not_empty__("main_branch")
result += self.__validate_is_not_empty__("config_file")
result += self.__validate_is_not_empty__("release_main_branch")
result += self.__validate_is_not_empty__("release_config_file")
result += self.__validate_is_not_empty__("release_context")
if self.release_context is not None:
result += self.release_context.validate()
result += self.release_context.validate_branch(self.main_branch)
result += self.release_context.validate_branch(self.release_main_branch)
return result

View file

@ -22,12 +22,13 @@ def test_devops_factory():
"module": "test_image",
"project_root_path": "../../..",
"build_types": ["IMAGE"],
"mixin_types": [],
"image_dockerhub_user": "dockerhub_user",
"image_dockerhub_password": "dockerhub_password",
"image_tag": "docker_image_tag",
}
)
assert sut != None
assert sut is not None
sut = DevopsFactory().build_devops(
{
@ -36,8 +37,26 @@ def test_devops_factory():
"module": "test_image",
"project_root_path": "../../..",
"build_types": ["C4K"],
"mixin_types": [],
"c4k_grafana_cloud_user": "user",
"c4k_grafana_cloud_password": "password",
}
)
assert sut != None
assert sut is not None
sut = DevopsFactory().build_devops(
{
"stage": "test",
"name": "mybuild",
"module": "test_image",
"project_root_path": "../../..",
"build_types": [],
"mixin_types": ["RELEASE"],
"release_main_branch": "main",
"release_config_file": "project.clj",
"release_type": "NONE",
"release_current_version": "1.0.0",
"release_current_branch": "my_feature",
}
)
assert sut is not None

View file

@ -9,14 +9,20 @@ def devops_config(overrides: dict) -> dict:
"project_root_path": "../../..",
"build_dir_name": "target",
"build_types": ["IMAGE", "C4K"],
"mixin_types": ["RELEASE"],
"image_dockerhub_user": "dockerhub_user",
"image_dockerhub_password": "dockerhub_password",
"image_tag": "image_tag",
'c4k_config': {},
"c4k_config": {},
"c4k_grafana_cloud_user": "user",
"c4k_grafana_cloud_password": "password",
"c4k_grafana_cloud_url": "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push",
'c4k_auth': {},
"c4k_auth": {},
"release_main_branch": "main",
"release_config_file": "project.clj",
"release_type": "NONE",
"release_current_version": "1.0.0",
"release_current_branch": "my_feature",
}
input = default.copy()
input.update(overrides)

View file

@ -5,6 +5,7 @@ from src.main.python.ddadevops.domain.common import (
DnsRecord,
Devops,
BuildType,
MixinType,
)
from src.main.python.ddadevops.domain import (
Version,
@ -13,7 +14,8 @@ from src.main.python.ddadevops.domain import (
ReleaseContext,
)
from src.main.python.ddadevops.domain.image import Image
from .test_helper import build_devops
from .test_helper import build_devops, devops_config
def test_version(tmp_path: Path):
version = Version(tmp_path, [1, 2, 3])
@ -48,22 +50,26 @@ def test_version(tmp_path: Path):
def test_release_context(tmp_path):
version = Version(tmp_path, [1, 2, 3])
release = ReleaseContext(ReleaseType.MINOR, version, "main")
release_version = release.release_version()
assert release_version.get_version_string() in "1.3.0"
bump_version = release.bump_version()
assert bump_version.get_version_string() in "1.3.1-SNAPSHOT"
sut = ReleaseContext(
devops_config(
{
"release_type": "MINOR",
"release_current_version": "1.2.3",
"release_current_branch": "main",
}
)
)
assert sut.release_version().get_version_string() == "1.3.0"
assert sut.bump_version().get_version_string() == "1.3.1-SNAPSHOT"
def test_release(tmp_path):
devops = build_devops({})
sut = Release(devops, "main", "config_file.json")
assert not sut.is_valid()
sut.set_release_context(
ReleaseContext(ReleaseType.MINOR, Version("id", [1, 2, 3]), "main")
sut = build_devops(
{
"release_type": "MINOR",
"release_current_version": "1.2.3",
"release_current_branch": "main",
}
)
assert sut.mixins[MixinType.RELEASE]
assert sut.is_valid()