Merge branch 'refactor-release-domain' into 'ddd-intro'
Refactor release domain See merge request domaindrivenarchitecture/dda-devops-build!6
This commit is contained in:
commit
8e2ca4e420
15 changed files with 145 additions and 124 deletions
|
@ -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):
|
||||
def __init__(
|
||||
self,
|
||||
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):
|
||||
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
|
||||
self.main_branch = main_branch
|
||||
|
||||
def release_version(self):
|
||||
def release_version(self) -> Version:
|
||||
return self.version.create_release_version(self.release_type)
|
||||
|
||||
def bump_version(self):
|
||||
def bump_version(self) -> Version:
|
||||
return self.release_version().create_bump_version()
|
||||
|
||||
def validate(self):
|
||||
result = []
|
||||
if self.release_type is not None and self.main_branch != self.current_branch:
|
||||
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,
|
||||
):
|
||||
self.devops = devops
|
||||
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.release_context.release_version()
|
||||
|
||||
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_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…
Reference in a new issue