minor fixes & initialize context for release

This commit is contained in:
Michael Jerger 2023-05-18 18:23:51 +02:00
parent 7f7878fe36
commit 639815388e
11 changed files with 138 additions and 96 deletions

View file

@ -9,10 +9,10 @@ class ReleaseService:
self.build_file_repository = build_file_repository self.build_file_repository = build_file_repository
@classmethod @classmethod
def prod(cls): def prod(cls, base_dir: str):
return cls( return cls(
GitApi(), GitApi(),
BuildFileRepository(), BuildFileRepository(base_dir),
) )
def prepare_release(self, release: Release): def prepare_release(self, release: Release):

View file

@ -2,7 +2,7 @@ from .common import Validateable, DnsRecord, Devops, BuildType, MixinType, Relea
from .devops_factory import DevopsFactory from .devops_factory import DevopsFactory
from .image import Image from .image import Image
from .c4k import C4k from .c4k import C4k
from .release import Release, EnvironmentKeys from .release import Release
from .credentials import Credentials, CredentialMapping, GopassType from .credentials import Credentials, CredentialMapping, GopassType
from .version import Version from .version import Version
from .build_file import BuildFileType, BuildFile from .build_file import BuildFileType, BuildFile

View file

@ -33,7 +33,7 @@ class DevopsFactory:
return devops return devops
def merge(self, input: dict, context: dict, authorization: dict) -> dict: def merge(self, input: dict, context: dict, authorization: dict) -> dict:
return {} | input | context | authorization return {} | context | authorization | input
def __parse_build_types__(self, build_types: List[str]) -> List[BuildType]: def __parse_build_types__(self, build_types: List[str]) -> List[BuildType]:
result = [] result = []

View file

@ -4,19 +4,22 @@ from .common import Devops, MixinType, BuildType
from .credentials import Credentials, GopassType from .credentials import Credentials, GopassType
from .devops_factory import DevopsFactory from .devops_factory import DevopsFactory
from .version import Version from .version import Version
from .release import ReleaseType
from src.main.python.ddadevops.infrastructure import ( from src.main.python.ddadevops.infrastructure import (
BuildFileRepository, BuildFileRepository,
CredentialsApi, CredentialsApi,
EnvironmentApi, EnvironmentApi,
GitApi
) )
class InitService: class InitService:
def __init__(self, devops_factory, build_file_repository, credentials_api, environment_api): def __init__(self, devops_factory, build_file_repository, credentials_api, environment_api, git_api):
self.devops_factory = devops_factory self.devops_factory = devops_factory
self.build_file_repository = build_file_repository self.build_file_repository = build_file_repository
self.credentials_api = credentials_api self.credentials_api = credentials_api
self.environment_api = environment_api self.environment_api = environment_api
self.git_api = git_api
@classmethod @classmethod
def prod(cls, base_dir: str): def prod(cls, base_dir: str):
@ -25,6 +28,7 @@ class InitService:
BuildFileRepository(base_dir), BuildFileRepository(base_dir),
CredentialsApi(), CredentialsApi(),
EnvironmentApi(), EnvironmentApi(),
GitApi(),
) )
def initialize(self, input: dict) -> Devops: def initialize(self, input: dict) -> Devops:
@ -47,9 +51,11 @@ class InitService:
}, },
] ]
credentials = Credentials(input, default_mappings) credentials = Credentials(input, default_mappings)
authorization = self.resolve_passwords(credentials) authorization = self.authorization(credentials)
merged = self.devops_factory.merge(input, {}, authorization) context = self.context()
merged = self.devops_factory.merge(input, context, authorization)
if MixinType.RELEASE in mixin_types: if MixinType.RELEASE in mixin_types:
primary_build_file_id = merged.get( primary_build_file_id = merged.get(
@ -62,7 +68,23 @@ class InitService:
return self.devops_factory.build_devops(merged, version=version) return self.devops_factory.build_devops(merged, version=version)
def resolve_passwords(self, credentials: Credentials) -> List[str]: def context(self) -> dict:
result = {}
release_type = self.environment_api.get("RELEASE_TYPE")
if not release_type:
latest_commit = self.git_api.get_latest_commit()
if latest_commit in [ReleaseType.MAJOR.name, ReleaseType.MINOR.name,
ReleaseType.PATCH.name, ReleaseType.NONE.name]:
release_type = latest_commit
result["release_type"] = release_type
result["release_current_branch"] = self.git_api.get_current_branch()
return result
def authorization(self, credentials: Credentials) -> List[str]:
result = {} result = {}
for name in credentials.mappings.keys(): for name in credentials.mappings.keys():
mapping = credentials.mappings[name] mapping = credentials.mappings[name]

View file

@ -11,19 +11,19 @@ from .version import (
) )
class EnvironmentKeys(Enum):
DDADEVOPS_RELEASE_TYPE = 0
class Release(Validateable): class Release(Validateable):
def __init__(self, input: dict, version: Version): def __init__(self, input: dict, version: Version):
self.release_type = ReleaseType[input.get("release_type", "NONE")] self.release_type = ReleaseType[input.get("release_type", "NONE")]
self.release_main_branch = input.get("release_main_branch", "main") self.release_main_branch = input.get("release_main_branch", "main")
self.release_current_branch = input.get("release_current_branch") self.release_current_branch = input.get("release_current_branch")
self.release_primary_build_file = input.get("release_primary_build_file", "./project.clj") self.release_primary_build_file = input.get(
self.release_secondary_build_files = input.get("release_secondary_build_files", []) "release_primary_build_file", "./project.clj"
)
self.release_secondary_build_files = input.get(
"release_secondary_build_files", []
)
self.version = version self.version = version
def validate(self): def validate(self):
result = [] result = []
result += self.__validate_is_not_empty__("release_type") result += self.__validate_is_not_empty__("release_type")
@ -34,12 +34,16 @@ class Release(Validateable):
try: try:
Path(self.release_primary_build_file) Path(self.release_primary_build_file)
except Exception as e: except Exception as e:
result.append(f"release_primary_build_file must be a valid path but was {e}") result.append(
f"release_primary_build_file must be a valid path but was {e}"
)
for path in self.release_secondary_build_files: for path in self.release_secondary_build_files:
try: try:
Path(path) Path(path)
except Exception as e: except Exception as e:
result.append(f"release_secondary_build_file must be contain valid paths but was {e}") result.append(
f"release_secondary_build_file must be contain valid paths but was {e}"
)
if self.version: if self.version:
result += self.version.validate() result += self.version.validate()
if ( if (

View file

@ -3,7 +3,6 @@ from sys import stdout
from os import chmod, environ from os import chmod, environ
from pkg_resources import resource_string from pkg_resources import resource_string
import yaml import yaml
import deprecation
from subprocess import check_output, Popen, PIPE, run from subprocess import check_output, Popen, PIPE, run
from ..domain import Devops, Image, C4k, Release, BuildFile from ..domain import Devops, Image, C4k, Release, BuildFile
@ -135,52 +134,50 @@ class CredentialsApi:
return credential return credential
class GitApi(): class GitApi:
def __init__(self): def __init__(self):
self.execution_api = ExecutionApi() self.execution_api = ExecutionApi()
# pylint: disable=invalid-name # pylint: disable=invalid-name
def get_latest_n_commits(self, n: int): def get_latest_n_commits(self, n: int):
return self.execution_api.execute( return self.execution_api.execute(f'git log --oneline --format="%s %b" -n {n}')
f'git log --oneline --format="%s %b" -n {n}')
def get_latest_commit(self): def get_latest_commit(self):
return self.get_latest_n_commits(1) return self.get_latest_n_commits(1)
def tag_annotated(self, annotation: str, message: str, count: int): def tag_annotated(self, annotation: str, message: str, count: int):
return self.execution_api.execute( return self.execution_api.execute(
f'git tag -a {annotation} -m {message} HEAD~{count}') f"git tag -a {annotation} -m {message} HEAD~{count}"
)
def tag_annotated_second_last(self, annotation: str, message:str): def tag_annotated_second_last(self, annotation: str, message: str):
return self.tag_annotated(annotation, message, 1) return self.tag_annotated(annotation, message, 1)
def get_latest_tag(self): def get_latest_tag(self):
return self.execution_api.execute('git describe --tags --abbrev=0') return self.execution_api.execute("git describe --tags --abbrev=0")
def get_current_branch(self): def get_current_branch(self):
return ''.join(self.execution_api.execute('git branch --show-current')).rstrip() return "".join(self.execution_api.execute("git branch --show-current")).rstrip()
def init(self, default_branch: str = "main"): def init(self, default_branch: str = "main"):
self.execution_api.execute('git init') self.execution_api.execute("git init")
self.execution_api.execute(f'git checkout -b {default_branch}') self.execution_api.execute(f"git checkout -b {default_branch}")
def set_user_config(self, email: str, name: str): def set_user_config(self, email: str, name: str):
self.execution_api.execute(f'git config user.email {email}') self.execution_api.execute(f"git config user.email {email}")
self.execution_api.execute(f'git config user.name {name}') self.execution_api.execute(f"git config user.name {name}")
def add_file(self, file_path: Path): def add_file(self, file_path: Path):
return self.execution_api.execute(f'git add {file_path}') return self.execution_api.execute(f"git add {file_path}")
def add_remote(self, origin: str, url: str): def add_remote(self, origin: str, url: str):
return self.execution_api.execute(f'git remote add {origin} {url}') return self.execution_api.execute(f"git remote add {origin} {url}")
def commit(self, commit_message: str): def commit(self, commit_message: str):
return self.execution_api.execute( return self.execution_api.execute(f'git commit -m "{commit_message}"')
f'git commit -m "{commit_message}"')
def push(self): def push(self):
return self.execution_api.execute('git push') return self.execution_api.execute("git push")
def checkout(self, branch: str): def checkout(self, branch: str):
return self.execution_api.execute(f'git checkout {branch}') return self.execution_api.execute(f"git checkout {branch}")

View file

@ -7,7 +7,7 @@ from src.main.python.ddadevops.domain import MixinType
class ReleaseMixin(DevopsBuild): class ReleaseMixin(DevopsBuild):
def __init__(self, project: Project, input: dict): def __init__(self, project: Project, input: dict):
super().__init__(project, input) super().__init__(project, input)
self.release_service = ReleaseService.prod() self.release_service = ReleaseService.prod(project.basedir)
devops = self.devops_repo.get_devops(self.project) devops = self.devops_repo.get_devops(self.project)
if MixinType.RELEASE not in devops.mixins: if MixinType.RELEASE not in devops.mixins:
raise ValueError(f"ReleaseMixin requires MixinType.RELEASE") raise ValueError(f"ReleaseMixin requires MixinType.RELEASE")

View file

@ -74,3 +74,44 @@ class CredentialsApiMock:
def gopass_password_from_path(self, path): def gopass_password_from_path(self, path):
return self.mappings.get(path, None) return self.mappings.get(path, None)
class GitApiMock:
def get_latest_n_commits(self, n: int):
pass
def get_latest_commit(self):
pass
def tag_annotated(self, annotation: str, message: str, count: int):
pass
def tag_annotated_second_last(self, annotation: str, message: str):
pass
def get_latest_tag(self):
pass
def get_current_branch(self):
pass
def init(self, default_branch: str = "main"):
pass
def set_user_config(self, email: str, name: str):
pass
def add_file(self, file_path: Path):
pass
def add_remote(self, origin: str, url: str):
pass
def commit(self, commit_message: str):
pass
def push(self):
pass
def checkout(self, branch: str):
pass

View file

@ -10,6 +10,7 @@ from .helper import (
BuildFileRepositoryMock, BuildFileRepositoryMock,
EnvironmentApiMock, EnvironmentApiMock,
CredentialsApiMock, CredentialsApiMock,
GitApiMock,
devops_config, devops_config,
) )
@ -23,6 +24,7 @@ def test_should_load_build_file():
"server/meissa/grafana-cloud": "gopass-gfc-password", "server/meissa/grafana-cloud": "gopass-gfc-password",
}), }),
EnvironmentApiMock({}), EnvironmentApiMock({}),
GitApiMock(),
) )
assert ( assert (
Version.from_str("1.1.5-SNAPSHOT") Version.from_str("1.1.5-SNAPSHOT")
@ -41,8 +43,12 @@ def test_should_resolve_passwords():
} }
), ),
EnvironmentApiMock({"C4K_GRAFANA_CLOUD_USER": "env-gfc-user"}), EnvironmentApiMock({"C4K_GRAFANA_CLOUD_USER": "env-gfc-user"}),
GitApiMock(),
) )
devops = sut.initialize(devops_config({})) config = devops_config({})
del config["c4k_grafana_cloud_user"]
del config["c4k_grafana_cloud_password"]
devops = sut.initialize(config)
c4k = devops.specialized_builds[BuildType.C4K] c4k = devops.specialized_builds[BuildType.C4K]
assert { assert {
"mon-auth": { "mon-auth": {

View file

@ -5,7 +5,7 @@ from src.main.python.ddadevops.c4k_build import C4kBuild, add_c4k_mixin_config
from .domain.helper import devops_config from .domain.helper import devops_config
def test_c4k_mixin(tmp_path): def test_c4k_build(tmp_path):
str_tmp_path = str(tmp_path) str_tmp_path = str(tmp_path)
project = Project(str_tmp_path, name="name") project = Project(str_tmp_path, name="name")

View file

@ -1,83 +1,55 @@
import pytest as pt import pytest as pt
import os
from pathlib import Path from pathlib import Path
from pybuilder.core import Project from pybuilder.core import Project
from src.main.python.ddadevops.release_mixin import ReleaseMixin from src.main.python.ddadevops.release_mixin import ReleaseMixin
from src.main.python.ddadevops.domain import Devops, Release from src.main.python.ddadevops.domain import Devops, Release
from .domain.helper import devops_config
from .resource_helper import ResourceHelper from .resource_helper import copy_resource
from .domain.test_helper import devops_config
MAIN_BRANCH = "main"
STAGE = "test"
PROJECT_ROOT_PATH = "."
MODULE = "test"
BUILD_DIR_NAME = "build_dir"
def change_test_dir(tmp_path: Path, monkeypatch: pt.MonkeyPatch):
monkeypatch.chdir(tmp_path)
def initialize_with_object(project, CONFIG_FILE):
project.build_depends_on("ddadevops>=3.1.2")
build = ReleaseMixin(
project,
devops_config(
{
"name": "release_test",
"stage": "test",
"module": MODULE,
"project_root_path": PROJECT_ROOT_PATH,
"build_types": [],
"mixin_types": ["RELEASE"],
"build_dir_name": BUILD_DIR_NAME,
"release_main_branch": MAIN_BRANCH,
"release_config_file": CONFIG_FILE,
}
),
)
return build
def test_release_mixin(tmp_path): def test_release_mixin(tmp_path):
tmp_path_str = str(tmp_path) str_tmp_path = str(tmp_path)
copy_resource(Path('package.json'), tmp_path)
project = Project(str_tmp_path, name="name")
project = Project(tmp_path_str, name="name")
sut = ReleaseMixin( sut = ReleaseMixin(
project, project,
devops_config( devops_config(
{ {
"project_root_path": tmp_path_str, "project_root_path": str_tmp_path,
"build_types": [],
"mixin_types": ["RELEASE"], "mixin_types": ["RELEASE"],
"build_types": [],
"module": "release-test",
} }
), ),
) )
assert sut is not None
sut.initialize_build_dir()
assert sut.build_path() == f"{str_tmp_path}/target/name/release-test"
def test_release_mixin_git(tmp_path: Path, monkeypatch: pt.MonkeyPatch): # def test_release_mixin_git(tmp_path: Path, monkeypatch: pt.MonkeyPatch):
# init # # init
th = ResourceHelper() # th = ResourceHelper()
th.copy_files(th.TEST_FILE_PATH, tmp_path) # th.copy_files(th.TEST_FILE_PATH, tmp_path)
th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME # th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
change_test_dir(tmp_path, monkeypatch) # change_test_dir(tmp_path, monkeypatch)
project = Project(tmp_path) # project = Project(tmp_path)
git_api = GitApi() # git_api = GitApi()
git_api.init() # git_api.init()
git_api.set_user_config("ex.ample@mail.com", "Ex Ample") # git_api.set_user_config("ex.ample@mail.com", "Ex Ample")
git_api.add_file(th.TEST_FILE_NAME) # git_api.add_file(th.TEST_FILE_NAME)
git_api.commit("MAJOR release") # git_api.commit("MAJOR release")
build = initialize_with_object(project, th.TEST_FILE_PATH) # build = initialize_with_object(project, th.TEST_FILE_PATH)
build.prepare_release() # build.prepare_release()
release_version = build.release_repo.version_repository.get_version() # release_version = build.release_repo.version_repository.get_version()
# test # # test
assert "124.0.1-SNAPSHOT" in release_version.get_version_string() # assert "124.0.1-SNAPSHOT" in release_version.get_version_string()
# def test_release_mixin_environment(tmp_path: Path, monkeypatch: pt.MonkeyPatch): # def test_release_mixin_environment(tmp_path: Path, monkeypatch: pt.MonkeyPatch):