Refactor release domain

merge-requests/9/head
Michael Jerger 1 year ago
parent 92081947c5
commit 749c8f2849

@ -32,8 +32,19 @@ classDiagram
ipv6
}
class Release {
main_branch
config_file
}
class ReleaseContext {
release_type
version
current_branch
}
C4k *-- DnsRecord
Image *-- Devops
Release *-- "0..1" ReleaseContext
```

@ -20,6 +20,6 @@ from .devops_build import DevopsBuild, create_devops_build_config, get_devops_bu
from .credential import gopass_password_from_path, gopass_field_from_path
from .release_mixin import ReleaseMixin
from .domain import Validateable, DnsRecord, Devops, Image
from .domain import Validateable, DnsRecord, Devops, Image, Release, ReleaseContext
__version__ = "${version}"

@ -1,16 +1,16 @@
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseRepository, GitApi
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseContextRepository, GitApi
from src.main.python.ddadevops.domain import Version
class PrepareReleaseService():
def __init__(self, release_repo: ReleaseRepository):
def __init__(self, release_repo: ReleaseContextRepository):
self.release_repo = release_repo
self.release = release_repo.get_release() # TODO: make stateless: argument to receiving func
self.git_api = GitApi()
def __write_and_commit_version(self, version: Version, commit_message: str):
self.release.is_valid(self.release_repo.main_branch)
self.release.is_valid()
self.release_repo.version_repository.write_file(version.get_version_string())
self.git_api.add_file(self.release_repo.version_repository.file)
@ -24,7 +24,7 @@ class PrepareReleaseService():
class TagAndPushReleaseService():
def __init__(self, git_api: GitApi, release_repo: ReleaseRepository):
def __init__(self, git_api: GitApi, release_repo: ReleaseContextRepository):
self.git_api = git_api
self.release_repo = release_repo
self.release = release_repo.get_release() # TODO: make stateless: argument to receiving func

@ -1,4 +1,4 @@
from .common import Validateable, DnsRecord, Devops
from .image import Image
from .c4k import C4k
from .release import Release, ReleaseConfig, ReleaseType, Version, EnvironmentKeys
from .release import Release, ReleaseContext, ReleaseType, Version, EnvironmentKeys

@ -9,7 +9,7 @@ class Validateable:
def __validate_is_not_empty__(self, field_name: str) -> List[str]:
value = self.__dict__[field_name]
if value is None or value == "":
return [f"Field '{field_name}' may not be empty."]
return [f"Field '{field_name}' must not be empty."]
else:
return []

@ -63,39 +63,60 @@ class Version():
bump_version.increment(ReleaseType.BUMP)
return bump_version
class ReleaseConfig(Validateable):
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 release_version(self) -> Version:
return self.version.create_release_version(self.release_type)
def bump_version(self) -> Version:
return self.release_version().create_bump_version()
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")
return result
def validate_branch(self, main_branch: str):
result = []
if self.release_type is not None and main_branch != self.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,
devops: Devops,
release_version: Optional[str | None] = None,
bump_version: Optional[str | None] = None
):
self.main_branch = main_branch
self.config_file = config_file
# TODO: this information may be transient?
self.release_version = release_version
self.bump_version = bump_version
self.devops = devops
class Release(Validateable):
def __init__(self, release_type: ReleaseType | None, version: Version, current_branch: str, main_branch: str):
self.release_type = release_type
self.version = version
self.current_branch = current_branch
self.main_branch = main_branch
self.config_file = config_file
self.release_context = None
def set_release_context(self, set_release_context: ReleaseContext) -> None:
self.release_context = set_release_context
def release_version(self):
return self.version.create_release_version(self.release_type)
return self.release_context.release_version()
def bump_version(self):
return self.release_version().create_bump_version()
return self.release_context.bump_version()
def validate(self):
result = []
if self.release_type is not None and self.main_branch != self.current_branch:
result.append(f"Releases are allowed only on {main_branch}")
result += self.__validate_is_not_empty__("main_branch")
result += self.__validate_is_not_empty__("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)
return result

@ -4,7 +4,7 @@ from os import chmod
from subprocess import run
from pkg_resources import resource_string
import yaml
from ..domain import Devops, Image, C4k, ReleaseConfig
from ..domain import Devops, Image, C4k, Release
from ..python_util import execute
@ -30,10 +30,10 @@ class ProjectRepository:
def set_c4k(self, project, build: C4k):
project.set_property("c4k_build", build)
def get_release(self, project) -> ReleaseConfig:
def get_release(self, project) -> Release:
return project.get_property("release_build")
def set_release(self, project, build: ReleaseConfig):
def set_release(self, project, build: Release):
project.set_property("release_build", build)

@ -1,2 +1,2 @@
from .infrastructure_api import FileHandler, EnvironmentApi, GitApi, JsonFileHandler, GradleFileHandler, PythonFileHandler, ClojureFileHandler
from .repo import VersionRepository, ReleaseRepository, ReleaseTypeRepository
from .repo import VersionRepository, ReleaseContextRepository, ReleaseTypeRepository

@ -1,6 +1,6 @@
from typing import Optional
from src.main.python.ddadevops.domain import (
Release,
ReleaseContext,
Version,
ReleaseType,
EnvironmentKeys,
@ -106,7 +106,8 @@ class ReleaseTypeRepository:
raise ValueError("No valid api passed to ReleaseTypeRepository")
class ReleaseRepository:
# TODO: Repo has state & repository should exist only for AggregateRoot
class ReleaseContextRepository:
def __init__(
self,
version_repository: VersionRepository,
@ -117,9 +118,13 @@ class ReleaseRepository:
self.release_type_repository = release_type_repository
self.main_branch = main_branch
def get_release(self) -> Release:
return Release(
def get_release(self) -> ReleaseContext:
result = ReleaseContext(
self.release_type_repository.get_release_type(),
self.version_repository.get_version(),
self.main_branch,
)
if not result.is_valid():
issues = '\n'.join.result.validate()
raise ValueError(f"invalid release: {issues}")
return result

@ -1,41 +1,14 @@
from typing import Optional
from pybuilder.core import Project
from src.main.python.ddadevops.devops_build import DevopsBuild
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseRepository, ReleaseTypeRepository, VersionRepository, GitApi, EnvironmentApi
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseContextRepository, ReleaseTypeRepository, VersionRepository, GitApi, EnvironmentApi
from src.main.python.ddadevops.application import PrepareReleaseService, TagAndPushReleaseService
from src.main.python.ddadevops.domain import ReleaseConfig, EnvironmentKeys
from src.main.python.ddadevops.domain import Release, EnvironmentKeys
# TODO: remove the config creation
def create_release_mixin_config(config_file, main_branch) -> dict:
config = {}
config.update(
{'ReleaseMixin':
{'main_branch': main_branch,
'config_file': config_file}})
return config
# TODO: remove the config creation
def add_versions(config, release_version, bump_version) -> dict:
config['ReleaseMixin'].update(
{'release_version': release_version,
'bump_version': bump_version})
return config
# TODO: remove the config argument
class ReleaseMixin(DevopsBuild):
def __init__(self, project: Project, release_config: Optional[ReleaseConfig] = None):
if not release_config:
if not config:
raise ValueError("Release parameters could not be set.")
super().__init__(project, config=config)
release_mixin_config = config['ReleaseMixin']
release_config = ReleaseConfig(
main_branch = release_mixin_config['main_branch'],
config_file = release_mixin_config['config_file'],
devops=self.repo.get_devops(project)
)
else:
super().__init__(project, devops=release_config.devops)
self.repo.set_release(self.project, release_config)
def __init__(self, project: Project, release: Release):
super().__init__(project, devops=release.devops)
self.repo.set_release(self.project, release)
git_api = GitApi()
environment_api = EnvironmentApi()
@ -47,8 +20,8 @@ class ReleaseMixin(DevopsBuild):
else:
release_type_repo = ReleaseTypeRepository.from_git(git_api)
version_repo = VersionRepository(release_config.config_file)
release_repo = ReleaseRepository(version_repo, release_type_repo, release_config.main_branch)
version_repo = VersionRepository(release.config_file)
release_repo = ReleaseContextRepository(version_repo, release_type_repo, release.main_branch)
self.prepare_release_service = PrepareReleaseService(release_repo)
self.tag_and_push_release_service = TagAndPushReleaseService(git_api, release_repo)

@ -1,9 +1,11 @@
from pybuilder.core import Project
from pathlib import Path
from src.main.python.ddadevops.domain.common import (
Validateable,
DnsRecord,
Devops,
)
from src.main.python.ddadevops.domain import Version, ReleaseType, Release, ReleaseContext
from src.main.python.ddadevops.domain.image import Image
from src.main.python.ddadevops.domain.c4k import C4k
from src.main.python.ddadevops.c4k_mixin import add_c4k_mixin_config
@ -44,7 +46,7 @@ def test_should_validate_non_empty_others():
def test_validate_with_reason():
sut = MockValidateable(None)
assert sut.validate()[0] == "Field 'field' may not be empty."
assert sut.validate()[0] == "Field 'field' must not be empty."
def test_should_validate_DnsRecord():
@ -173,3 +175,52 @@ def test_c4k_build_should_calculate_command():
+ "/target/name/module/out_module.yaml"
== sut.command(devops)
)
def test_version(tmp_path: Path):
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.SNAPSHOT)
assert version.get_version_string() == "1.2.3-SNAPSHOT"
assert version.version_list == [1, 2, 3]
assert version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.BUMP)
assert version.get_version_string() == "1.2.4-SNAPSHOT"
assert version.version_list == [1, 2, 4]
assert version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.PATCH)
assert version.get_version_string() == "1.2.4"
assert version.version_list == [1, 2, 4]
assert not version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.MINOR)
assert version.get_version_string() == "1.3.0"
assert version.version_list == [1, 3, 0]
assert not version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.MAJOR)
assert version.get_version_string() == "2.0.0"
assert version.version_list == [2, 0, 0]
assert not version.is_snapshot
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"
def test_release(tmp_path):
devops = Devops(stage="test", project_root_path="", module="module", name="name")
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"))
assert sut.is_valid()

@ -1,5 +1,7 @@
from src.main.python.ddadevops.domain import ReleaseType
# TODO: Domain never should be needed to mock! Why do you not yust create the needed object?
class MockVersion():
def __init__(self, id = None, version_list = None):
@ -33,9 +35,9 @@ class MockRelease():
def bump_version(self):
return self.release_version().create_bump_version()
def validate(self, main_branch):
def validate(self):
self.validate_count += 1
return []
def is_valid(self, main_branch):
return len(self.validate(main_branch)) < 1
def is_valid(self):
return len(self.validate()) < 1

@ -1,43 +0,0 @@
from pathlib import Path
from src.main.python.ddadevops.domain import Version, ReleaseType, Release
def test_version(tmp_path: Path):
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.SNAPSHOT)
assert version.get_version_string() == "1.2.3-SNAPSHOT"
assert version.version_list == [1, 2, 3]
assert version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.BUMP)
assert version.get_version_string() == "1.2.4-SNAPSHOT"
assert version.version_list == [1, 2, 4]
assert version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.PATCH)
assert version.get_version_string() == "1.2.4"
assert version.version_list == [1, 2, 4]
assert not version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.MINOR)
assert version.get_version_string() == "1.3.0"
assert version.version_list == [1, 3, 0]
assert not version.is_snapshot
version = Version(tmp_path, [1, 2, 3])
version.increment(ReleaseType.MAJOR)
assert version.get_version_string() == "2.0.0"
assert version.version_list == [2, 0, 0]
assert not version.is_snapshot
def test_release(tmp_path):
version = Version(tmp_path, [1, 2, 3])
release = Release(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"

@ -1,5 +1,5 @@
from src.main.python.ddadevops.domain import ReleaseType
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseTypeRepository, VersionRepository, ReleaseRepository
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseTypeRepository, VersionRepository, ReleaseContextRepository
from .mock_infrastructure_api import MockGitApi, MockEnvironmentApi
from .helper import Helper
@ -22,7 +22,8 @@ def test_release_repository(tmp_path):
release_type_repo = ReleaseTypeRepository.from_git(MockGitApi('MINOR test'))
# test
sut = ReleaseRepository(version_repo, release_type_repo, 'main')
sut = ReleaseContextRepository(version_repo, release_type_repo, 'main')
release = sut.get_release()
assert release is not None

@ -4,7 +4,7 @@ from pybuilder.core import Project
from src.main.python.ddadevops.release_mixin import ReleaseMixin
from src.main.python.ddadevops.infrastructure.release_mixin import GitApi, EnvironmentApi
from src.main.python.ddadevops.domain import Devops, ReleaseConfig
from src.main.python.ddadevops.domain import Devops, ReleaseContext, Release
from .helper import Helper
@ -23,8 +23,8 @@ class MyBuild(ReleaseMixin):
def initialize_with_object(project, CONFIG_FILE):
project.build_depends_on('ddadevops>=3.1.2')
devops = Devops(STAGE, PROJECT_ROOT_PATH, MODULE, "release_test", BUILD_DIR_NAME)
release_config = ReleaseConfig(MAIN_BRANCH, CONFIG_FILE, devops)
build = MyBuild(project, release_config=release_config)
release = Release(devops, MAIN_BRANCH, CONFIG_FILE)
build = MyBuild(project, release)
return build
def test_release_mixin_git(tmp_path: Path, monkeypatch: pt.MonkeyPatch):

Loading…
Cancel
Save