Merge branch 'introduce-domain-factories' into 'ddd-intro'
refactoring to ddd See merge request domaindrivenarchitecture/dda-devops-build!12
This commit is contained in:
commit
22ad95737e
60 changed files with 2235 additions and 1557 deletions
|
@ -4,6 +4,9 @@ before_script:
|
|||
- python --version
|
||||
- python -m pip install --upgrade pip
|
||||
- pip install -r requirements.txt
|
||||
- export IMAGE_TAG=$CI_IMAGE_TAG
|
||||
- export IMAGE_DOCKERHUB_USER=&DOCKERHUB_USER
|
||||
- export IMAGE_DOCKERHUB_PASSWORD=$DOCKERHUB_PASSWORD
|
||||
|
||||
stages:
|
||||
- lint&test
|
||||
|
|
2
build.py
2
build.py
|
@ -28,7 +28,7 @@ use_plugin("python.distutils")
|
|||
default_task = "publish"
|
||||
|
||||
name = "ddadevops"
|
||||
version = "4.0.0-dev16"
|
||||
version = "4.0.0-dev41"
|
||||
summary = "tools to support builds combining gopass, terraform, dda-pallet, aws & hetzner-cloud"
|
||||
description = __doc__
|
||||
authors = [Author("meissa GmbH", "buero@meissa-gmbh.de")]
|
||||
|
|
|
@ -3,25 +3,26 @@
|
|||
```mermaid
|
||||
classDiagram
|
||||
class Devops {
|
||||
stage
|
||||
<<AggregateRoot>>
|
||||
name
|
||||
project_root_path
|
||||
module
|
||||
stage
|
||||
build_dir_name
|
||||
project_root_path
|
||||
}
|
||||
|
||||
class Image {
|
||||
dockerhub_user
|
||||
dockerhub_password
|
||||
build_dir_name
|
||||
use_package_common_files
|
||||
build_commons_path
|
||||
docker_build_commons_dir_name
|
||||
docker_publish_tag
|
||||
image_dockerhub_user
|
||||
image_dockerhub_password
|
||||
image_publish_tag
|
||||
image_build_dir_name
|
||||
image_use_package_common_files
|
||||
image_build_commons_path
|
||||
image_build_commons_dir_name
|
||||
}
|
||||
|
||||
class C4k {
|
||||
executabel_name
|
||||
c4k_executabel_name
|
||||
c4k_mixin_config
|
||||
c4k_mixin_auth
|
||||
}
|
||||
|
@ -33,18 +34,49 @@ classDiagram
|
|||
}
|
||||
|
||||
class Release {
|
||||
main_branch
|
||||
config_file
|
||||
}
|
||||
class ReleaseContext {
|
||||
release_type
|
||||
release_main_branch
|
||||
release_current_branch
|
||||
version
|
||||
current_branch
|
||||
}
|
||||
class Credentials {
|
||||
<<AggregateRoot>>
|
||||
}
|
||||
class CredentialMapping {
|
||||
name
|
||||
gopass_path
|
||||
gopass_field
|
||||
gopass_type()
|
||||
name_for_input()
|
||||
name_for_environment ()
|
||||
}
|
||||
|
||||
|
||||
class BuildFile {
|
||||
<<AggregateRoot>>
|
||||
file_path [id]
|
||||
content
|
||||
build_file_type()
|
||||
get_version()
|
||||
set_version(version)
|
||||
}
|
||||
|
||||
class Version {
|
||||
to_string()
|
||||
create_major()
|
||||
create_minor()
|
||||
create_patch()
|
||||
create_bump(snapshot_suffix)
|
||||
}
|
||||
|
||||
Devops *-- "0..1" Image: spcialized_builds
|
||||
Devops *-- "0..1" C4k: spcialized_builds
|
||||
Devops *-- "0..1" Release: mixins
|
||||
Release o-- "0..1" BuildFile: primary_build_file
|
||||
Release o-- "0..n" BuildFile: secondary_build_files
|
||||
BuildFile *-- "1" Version
|
||||
C4k *-- DnsRecord
|
||||
Image *-- Devops
|
||||
Release *-- "0..1" ReleaseContext
|
||||
Credentials *-- "0..n" CredentialMapping: mappings
|
||||
|
||||
```
|
||||
|
||||
|
|
|
@ -3,38 +3,25 @@ from pybuilder.core import task, init
|
|||
from ddadevops import *
|
||||
|
||||
name = "clojure"
|
||||
MODULE = "docker"
|
||||
MODULE = "image"
|
||||
PROJECT_ROOT_PATH = "../.."
|
||||
|
||||
|
||||
@init
|
||||
def initialize(project):
|
||||
|
||||
input = {
|
||||
"name": name,
|
||||
"module": MODULE,
|
||||
"stage": "notused",
|
||||
"project_root_path": PROJECT_ROOT_PATH,
|
||||
"build_types": ["IMAGE"],
|
||||
"mixin_types": [],
|
||||
}
|
||||
|
||||
project.build_depends_on("ddadevops>=4.0.0-dev")
|
||||
stage = "notused"
|
||||
dockerhub_user = environ.get("DOCKERHUB_USER")
|
||||
if not dockerhub_user:
|
||||
dockerhub_user = gopass_field_from_path("meissa/web/docker.com", "login")
|
||||
dockerhub_password = environ.get("DOCKERHUB_PASSWORD")
|
||||
if not dockerhub_password:
|
||||
dockerhub_password = gopass_password_from_path("meissa/web/docker.com")
|
||||
tag = environ.get("CI_COMMIT_TAG")
|
||||
if not tag:
|
||||
tag = get_tag_from_latest_commit()
|
||||
|
||||
devops = Devops(
|
||||
stage=stage,
|
||||
project_root_path=PROJECT_ROOT_PATH,
|
||||
module=MODULE,
|
||||
name=name,
|
||||
)
|
||||
image = Image(
|
||||
dockerhub_user=dockerhub_user,
|
||||
dockerhub_password=dockerhub_password,
|
||||
docker_publish_tag=tag,
|
||||
devops=devops,
|
||||
)
|
||||
|
||||
build = DevopsImageBuild(project, image=image)
|
||||
build = DevopsImageBuild(project, input)
|
||||
build.initialize_build_dir()
|
||||
|
||||
|
||||
|
|
|
@ -3,38 +3,25 @@ from pybuilder.core import task, init
|
|||
from ddadevops import *
|
||||
|
||||
name = "devops-build"
|
||||
MODULE = "docker"
|
||||
MODULE = "image"
|
||||
PROJECT_ROOT_PATH = "../.."
|
||||
|
||||
|
||||
@init
|
||||
def initialize(project):
|
||||
|
||||
input = {
|
||||
"name": name,
|
||||
"module": MODULE,
|
||||
"stage": "notused",
|
||||
"project_root_path": PROJECT_ROOT_PATH,
|
||||
"build_types": ["IMAGE"],
|
||||
"mixin_types": [],
|
||||
}
|
||||
|
||||
project.build_depends_on("ddadevops>=4.0.0-dev")
|
||||
stage = "notused"
|
||||
dockerhub_user = environ.get("DOCKERHUB_USER")
|
||||
if not dockerhub_user:
|
||||
dockerhub_user = gopass_field_from_path("meissa/web/docker.com", "login")
|
||||
dockerhub_password = environ.get("DOCKERHUB_PASSWORD")
|
||||
if not dockerhub_password:
|
||||
dockerhub_password = gopass_password_from_path("meissa/web/docker.com")
|
||||
tag = environ.get("CI_COMMIT_TAG")
|
||||
if not tag:
|
||||
tag = get_tag_from_latest_commit()
|
||||
|
||||
devops = Devops(
|
||||
stage=stage,
|
||||
project_root_path=PROJECT_ROOT_PATH,
|
||||
module=MODULE,
|
||||
name=name,
|
||||
)
|
||||
image = Image(
|
||||
dockerhub_user=dockerhub_user,
|
||||
dockerhub_password=dockerhub_password,
|
||||
docker_publish_tag=tag,
|
||||
devops=devops,
|
||||
)
|
||||
|
||||
build = DevopsImageBuild(project, image=image)
|
||||
build = DevopsImageBuild(project, input)
|
||||
build.initialize_build_dir()
|
||||
|
||||
|
||||
|
|
|
@ -5,3 +5,4 @@ dda-python-terraform==2.0.1
|
|||
packaging
|
||||
boto3
|
||||
pyyaml
|
||||
inflection
|
|
@ -9,7 +9,7 @@ from .provs_k3s_mixin import ProvsK3sMixin, add_provs_k3s_mixin_config
|
|||
from .aws_rds_pg_mixin import AwsRdsPgMixin, add_aws_rds_pg_mixin_config
|
||||
from .aws_mfa_mixin import AwsMfaMixin, add_aws_mfa_mixin_config
|
||||
from .aws_backend_properties_mixin import AwsBackendPropertiesMixin, add_aws_backend_properties_mixin_config
|
||||
from .c4k_mixin import C4kBuild, add_c4k_mixin_config
|
||||
from .c4k_build import C4kBuild, add_c4k_mixin_config
|
||||
from .exoscale_mixin import ExoscaleMixin, add_exoscale_mixin_config
|
||||
from .digitalocean_backend_properties_mixin import DigitaloceanBackendPropertiesMixin, add_digitalocean_backend_properties_mixin_config
|
||||
from .digitalocean_terraform_build import DigitaloceanTerraformBuild, create_digitalocean_terraform_build_config
|
||||
|
@ -20,6 +20,4 @@ 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, Release, ReleaseContext
|
||||
|
||||
__version__ = "${version}"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
from .image_build_service import ImageBuildService
|
||||
from .release_mixin_services import TagAndPushReleaseService, PrepareReleaseService
|
||||
from .release_mixin_services import ReleaseService
|
||||
|
|
|
@ -1,54 +1,66 @@
|
|||
from src.main.python.ddadevops.domain import Image
|
||||
from src.main.python.ddadevops.infrastructure import FileApi, ResourceApi, ImageApi
|
||||
from ..domain import Image, Devops, BuildType
|
||||
from ..infrastructure import FileApi, ResourceApi, ImageApi
|
||||
|
||||
|
||||
class ImageBuildService:
|
||||
def __init__(self):
|
||||
self.file_api = FileApi()
|
||||
self.resource_api = ResourceApi()
|
||||
self.docker_api = ImageApi()
|
||||
def __init__(self, file_api: FileApi, resource_api: ResourceApi, image_api: ImageApi):
|
||||
self.file_api = file_api
|
||||
self.resource_api = resource_api
|
||||
self.image_api = image_api
|
||||
|
||||
def __copy_build_resource_file_from_package__(self, resource_name, docker: Image):
|
||||
@classmethod
|
||||
def prod(cls):
|
||||
return cls(
|
||||
FileApi(),
|
||||
ResourceApi(),
|
||||
ImageApi(),
|
||||
)
|
||||
|
||||
def __copy_build_resource_file_from_package__(self, resource_name, devops: Devops):
|
||||
data = self.resource_api.read_resource(f"src/main/resources/docker/{resource_name}")
|
||||
self.file_api.write_data_to_file(
|
||||
f"{docker.devops.build_path()}/{resource_name}", data
|
||||
f"{devops.build_path()}/{resource_name}", data
|
||||
)
|
||||
|
||||
def __copy_build_resources_from_package__(self, docker: Image):
|
||||
def __copy_build_resources_from_package__(self, devops: Devops):
|
||||
self.__copy_build_resource_file_from_package__(
|
||||
"image/resources/install_functions.sh", docker
|
||||
"image/resources/install_functions.sh", devops
|
||||
)
|
||||
|
||||
def __copy_build_resources_from_dir__(self, docker: Image):
|
||||
def __copy_build_resources_from_dir__(self, devops: Devops):
|
||||
image = devops.specialized_builds[BuildType.IMAGE]
|
||||
self.file_api.cp_force(
|
||||
docker.docker_build_commons_path(), docker.devops.build_path()
|
||||
image.build_commons_path(), devops.build_path()
|
||||
)
|
||||
|
||||
def initialize_build_dir(self, docker: Image):
|
||||
build_path = docker.devops.build_path()
|
||||
def initialize_build_dir(self, devops: Devops):
|
||||
image = devops.specialized_builds[BuildType.IMAGE]
|
||||
build_path = devops.build_path()
|
||||
self.file_api.clean_dir(f"{build_path}/image/resources")
|
||||
if docker.use_package_common_files:
|
||||
self.__copy_build_resources_from_package__(docker)
|
||||
if image.image_use_package_common_files:
|
||||
self.__copy_build_resources_from_package__(devops)
|
||||
else:
|
||||
self.__copy_build_resources_from_dir__(docker)
|
||||
self.__copy_build_resources_from_dir__(devops)
|
||||
self.file_api.cp_recursive("image", build_path)
|
||||
self.file_api.cp_recursive("test", build_path)
|
||||
|
||||
def image(self, docker: Image):
|
||||
self.docker_api.image(docker.devops.name, docker.devops.build_path())
|
||||
def image(self, devops: Devops):
|
||||
self.image_api.image(devops.name, devops.build_path())
|
||||
|
||||
def drun(self, docker: Image):
|
||||
self.docker_api.drun(docker.devops.name)
|
||||
def drun(self, devops: Devops):
|
||||
self.image_api.drun(devops.name)
|
||||
|
||||
def dockerhub_login(self, docker: Image):
|
||||
self.docker_api.dockerhub_login(
|
||||
docker.dockerhub_user, docker.dockerhub_password
|
||||
def dockerhub_login(self, devops: Devops):
|
||||
image = devops.specialized_builds[BuildType.IMAGE]
|
||||
self.image_api.dockerhub_login(
|
||||
image.image_dockerhub_user, image.image_dockerhub_password
|
||||
)
|
||||
|
||||
def dockerhub_publish(self, docker: Image):
|
||||
self.docker_api.dockerhub_publish(
|
||||
docker.devops.name, docker.dockerhub_user, docker.docker_publish_tag
|
||||
def dockerhub_publish(self, devops: Devops):
|
||||
image = devops.specialized_builds[BuildType.IMAGE]
|
||||
self.image_api.dockerhub_publish(
|
||||
devops.name, image.image_dockerhub_user, image.image_tag
|
||||
)
|
||||
|
||||
def test(self, docker: Image):
|
||||
self.docker_api.test(docker.devops.name, docker.devops.build_path())
|
||||
def test(self, devops: Devops):
|
||||
self.image_api.test(devops.name, devops.build_path())
|
||||
|
|
|
@ -1,35 +1,61 @@
|
|||
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseContextRepository, VersionRepository, GitApi
|
||||
from src.main.python.ddadevops.domain import Version, Release
|
||||
from typing import Optional, List
|
||||
from pathlib import Path
|
||||
from ..infrastructure import GitApi, BuildFileRepository
|
||||
from ..domain import Version, Release, ReleaseType
|
||||
|
||||
|
||||
class PrepareReleaseService():
|
||||
|
||||
def __init__(self):
|
||||
self.git_api = GitApi()
|
||||
|
||||
def __write_and_commit_version(self, release: Release, version_repository: VersionRepository, version: Version, commit_message: str):
|
||||
release.is_valid()
|
||||
|
||||
version_repository.write_file(version.get_version_string())
|
||||
self.git_api.add_file(version_repository.file)
|
||||
self.git_api.commit(commit_message)
|
||||
|
||||
def write_and_commit_release(self, release: Release, version_repository: VersionRepository):
|
||||
self.__write_and_commit_version(release, version_repository, release.release_version(), commit_message=f'Release v{release.release_version().get_version_string()}')
|
||||
|
||||
def write_and_commit_bump(self, release: Release, version_repository: VersionRepository):
|
||||
self.__write_and_commit_version(release, version_repository, release.bump_version(), commit_message='Version bump')
|
||||
|
||||
class TagAndPushReleaseService():
|
||||
|
||||
def __init__(self, git_api: GitApi, main_branch: str):
|
||||
class ReleaseService:
|
||||
def __init__(self, git_api: GitApi, build_file_repository: BuildFileRepository):
|
||||
self.git_api = git_api
|
||||
self.main_branch = main_branch
|
||||
self.build_file_repository = build_file_repository
|
||||
|
||||
def tag_release(self, release_repo: ReleaseContextRepository):
|
||||
annotation = 'v' + release_repo.get_release(self.main_branch).version.get_version_string()
|
||||
message = 'Release ' + annotation
|
||||
self.git_api.tag_annotated_second_last(annotation, message)
|
||||
@classmethod
|
||||
def prod(cls, base_dir: str):
|
||||
return cls(
|
||||
GitApi(),
|
||||
BuildFileRepository(base_dir),
|
||||
)
|
||||
|
||||
def push_release(self):
|
||||
def prepare_release(self, release: Release):
|
||||
match release.release_type:
|
||||
case ReleaseType.MAJOR:
|
||||
version = release.version.create_major()
|
||||
case ReleaseType.MINOR:
|
||||
version = release.version.create_minor()
|
||||
case ReleaseType.PATCH:
|
||||
version = release.version.create_patch()
|
||||
case ReleaseType.NONE:
|
||||
return
|
||||
message = f"release: {version.to_string()}"
|
||||
self.__set_version_and_commit__(version, release.build_files(), message)
|
||||
|
||||
def tag_bump_and_push_release(self, release: Release):
|
||||
match release.release_type:
|
||||
case ReleaseType.MAJOR:
|
||||
release_version = release.version.create_major()
|
||||
case ReleaseType.MINOR:
|
||||
release_version = release.version.create_minor()
|
||||
case ReleaseType.PATCH:
|
||||
release_version = release.version.create_patch()
|
||||
case ReleaseType.NONE:
|
||||
return
|
||||
bump_version = release_version.create_bump("SNAPSHOT")
|
||||
release_message = f"release: {release_version.to_string()}"
|
||||
bump_message = f"bump version to: {bump_version.to_string()}"
|
||||
self.git_api.tag_annotated(release_version.to_string(), release_message, 0)
|
||||
self.__set_version_and_commit__(
|
||||
bump_version,
|
||||
release.build_files(),
|
||||
bump_message,
|
||||
)
|
||||
self.git_api.push()
|
||||
|
||||
def __set_version_and_commit__(
|
||||
self, version: Version, build_file_ids: List[str], message: str
|
||||
):
|
||||
for id in build_file_ids:
|
||||
build_file = self.build_file_repository.get(Path(id))
|
||||
build_file.set_version(version)
|
||||
self.build_file_repository.write(build_file)
|
||||
self.git_api.add_file(build_file.file_path)
|
||||
self.git_api.commit(message)
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
import deprecation
|
||||
from .domain import C4k, DnsRecord
|
||||
from .domain import BuildType, DnsRecord
|
||||
from .devops_build import DevopsBuild
|
||||
from .credential import gopass_field_from_path, gopass_password_from_path
|
||||
from .infrastructure import ExecutionApi
|
||||
|
||||
|
||||
@deprecation.deprecated(deprecated_in="3.2")
|
||||
# create objects direct instead
|
||||
@deprecation.deprecated(deprecated_in="3.2", details="use direct dict instead")
|
||||
def add_c4k_mixin_config(
|
||||
config,
|
||||
c4k_config_dict,
|
||||
c4k_auth_dict,
|
||||
executabel_name=None,
|
||||
executable_name=None,
|
||||
grafana_cloud_user=None,
|
||||
grafana_cloud_password=None,
|
||||
grafana_cloud_url="https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push",
|
||||
|
@ -36,7 +35,7 @@ def add_c4k_mixin_config(
|
|||
config.update(
|
||||
{
|
||||
"C4kMixin": {
|
||||
"executabel_name": executabel_name,
|
||||
"executable_name": executable_name,
|
||||
"config": c4k_config_dict,
|
||||
"auth": c4k_auth_dict,
|
||||
}
|
||||
|
@ -48,27 +47,31 @@ class C4kBuild(DevopsBuild):
|
|||
def __init__(self, project, config):
|
||||
super().__init__(project, config)
|
||||
self.execution_api = ExecutionApi()
|
||||
c4k_build = C4k(config)
|
||||
self.repo.set_c4k(self.project, c4k_build)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
if BuildType.C4K not in devops.specialized_builds:
|
||||
raise ValueError(f"C4kBuild requires BuildType.C4K")
|
||||
|
||||
def update_runtime_config(self, dns_record: DnsRecord):
|
||||
c4k_build = self.repo.get_c4k(self.project)
|
||||
c4k_build.update_runtime_config(dns_record)
|
||||
self.repo.set_c4k(self.project, c4k_build)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
devops.specialized_builds[BuildType.C4K].update_runtime_config(dns_record)
|
||||
self.devops_repo.set_devops(self.project, devops)
|
||||
|
||||
def write_c4k_config(self):
|
||||
build = self.repo.get_devops(self.project)
|
||||
c4k_build = self.repo.get_c4k(self.project)
|
||||
path = build.build_path() + "/out_c4k_config.yaml"
|
||||
self.file_api.write_yaml_to_file(path, c4k_build.config())
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
path = devops.build_path() + "/out_c4k_config.yaml"
|
||||
self.file_api.write_yaml_to_file(
|
||||
path, devops.specialized_builds[BuildType.C4K].config()
|
||||
)
|
||||
|
||||
def write_c4k_auth(self):
|
||||
build = self.repo.get_devops(self.project)
|
||||
c4k_build = self.repo.get_c4k(self.project)
|
||||
path = build.build_path() + "/out_c4k_auth.yaml"
|
||||
self.file_api.write_yaml_to_file(path, c4k_build.c4k_mixin_auth)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
path = devops.build_path() + "/out_c4k_auth.yaml"
|
||||
self.file_api.write_yaml_to_file(
|
||||
path, devops.specialized_builds[BuildType.C4K].auth()
|
||||
)
|
||||
|
||||
def c4k_apply(self, dry_run=False):
|
||||
build = self.repo.get_devops(self.project)
|
||||
c4k_build = self.repo.get_c4k(self.project)
|
||||
return self.execution_api.execute(c4k_build.command(build), dry_run)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
return self.execution_api.execute(
|
||||
devops.specialized_builds[BuildType.C4K].command(devops), dry_run
|
||||
)
|
|
@ -1,15 +1,24 @@
|
|||
import deprecation
|
||||
from .python_util import execute
|
||||
|
||||
|
||||
@deprecation.deprecated(
|
||||
deprecated_in="3.2", details="use infrastructure.CredentialsApi instead"
|
||||
)
|
||||
def gopass_field_from_path(path, field):
|
||||
credential = None
|
||||
if path and field:
|
||||
print('get field for: ' + path + ', ' + field)
|
||||
credential = execute(['gopass', 'show', path, field])
|
||||
print("get field for: " + path + ", " + field)
|
||||
credential = execute(["gopass", "show", path, field])
|
||||
return credential
|
||||
|
||||
|
||||
@deprecation.deprecated(
|
||||
deprecated_in="3.2", details="use infrastructure.CredentialsApi instead"
|
||||
)
|
||||
def gopass_password_from_path(path):
|
||||
credential = None
|
||||
if path:
|
||||
print('get password for: ' + path)
|
||||
credential = execute(['gopass', 'show', '--password', path])
|
||||
print("get password for: " + path)
|
||||
credential = execute(["gopass", "show", "--password", path])
|
||||
return credential
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Optional
|
||||
import deprecation
|
||||
from .domain import Devops
|
||||
from .infrastructure import ProjectRepository, FileApi
|
||||
from .domain import Devops, InitService
|
||||
from .infrastructure import DevopsRepository, FileApi
|
||||
|
||||
|
||||
@deprecation.deprecated(deprecated_in="3.2", details="create objects direct instead")
|
||||
|
@ -15,36 +15,29 @@ def create_devops_build_config(
|
|||
"build_dir_name": build_dir_name,
|
||||
}
|
||||
|
||||
|
||||
def get_devops_build(project):
|
||||
return project.get_property("devops_build")
|
||||
return project.get_property("build")
|
||||
|
||||
|
||||
class DevopsBuild:
|
||||
def __init__(self, project, config: Optional[dict] = None, devops: Optional[Devops] = None):
|
||||
def __init__(self, project, input: dict):
|
||||
self.project = project
|
||||
self.file_api = FileApi()
|
||||
self.repo = ProjectRepository()
|
||||
if not devops:
|
||||
if not config:
|
||||
raise ValueError("Build parameters could not be set!")
|
||||
devops = Devops(
|
||||
stage=config["stage"],
|
||||
project_root_path=config["project_root_path"],
|
||||
module=config["module"],
|
||||
name=project.name,
|
||||
build_dir_name=config["build_dir_name"],
|
||||
)
|
||||
|
||||
self.repo.set_devops(self.project, devops)
|
||||
self.repo.set_build(self.project, self)
|
||||
self.init_service = InitService.prod(project.basedir)
|
||||
self.devops_repo = DevopsRepository()
|
||||
devops = self.init_service.initialize(input)
|
||||
self.devops_repo.set_devops(self.project, devops)
|
||||
self.project.set_property("build", self)
|
||||
|
||||
def name(self):
|
||||
devops = self.repo.get_devops(self.project)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
return devops.name
|
||||
|
||||
def build_path(self):
|
||||
devops = self.repo.get_devops(self.project)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
return devops.build_path()
|
||||
|
||||
def initialize_build_dir(self):
|
||||
devops = self.repo.get_devops(self.project)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
self.file_api.clean_dir(devops.build_path())
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from typing import Optional
|
||||
import deprecation
|
||||
from .domain import Image
|
||||
from .domain import BuildType
|
||||
from .application import ImageBuildService
|
||||
from .devops_build import DevopsBuild, create_devops_build_config
|
||||
|
||||
|
||||
@deprecation.deprecated(deprecated_in="3.2", details="create objects direct instead")
|
||||
@deprecation.deprecated(deprecated_in="3.2", details="use direct dict instead")
|
||||
def create_devops_docker_build_config(
|
||||
stage,
|
||||
project_root_path,
|
||||
|
@ -33,46 +33,34 @@ def create_devops_docker_build_config(
|
|||
|
||||
|
||||
class DevopsImageBuild(DevopsBuild):
|
||||
def __init__(self, project, config: Optional[dict] = None, image: Optional[Image] = None):
|
||||
self.image_build_service = ImageBuildService()
|
||||
if not image:
|
||||
if not config:
|
||||
raise ValueError("Image parameters could not be set.")
|
||||
super().__init__(project, config=config)
|
||||
image = Image(
|
||||
dockerhub_user=config["dockerhub_user"],
|
||||
dockerhub_password=config["dockerhub_password"],
|
||||
devops=self.repo.get_devops(project),
|
||||
use_package_common_files=config["use_package_common_files"],
|
||||
build_commons_path=config["build_commons_path"],
|
||||
docker_build_commons_dir_name=config["docker_build_commons_dir_name"],
|
||||
docker_publish_tag=config["docker_publish_tag"],
|
||||
)
|
||||
else:
|
||||
super().__init__(project, devops=image.devops)
|
||||
self.repo.set_docker(self.project, image)
|
||||
def __init__(self, project, input: dict):
|
||||
super().__init__(project, input)
|
||||
self.image_build_service = ImageBuildService.prod()
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
if BuildType.IMAGE not in devops.specialized_builds:
|
||||
raise ValueError(f"ImageBuild requires BuildType.IMAGE")
|
||||
|
||||
def initialize_build_dir(self):
|
||||
super().initialize_build_dir()
|
||||
image = self.repo.get_docker(self.project)
|
||||
self.image_build_service.initialize_build_dir(image)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
self.image_build_service.initialize_build_dir(devops)
|
||||
|
||||
def image(self):
|
||||
image = self.repo.get_docker(self.project)
|
||||
self.image_build_service.image(image)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
self.image_build_service.image(devops)
|
||||
|
||||
def drun(self):
|
||||
image = self.repo.get_docker(self.project)
|
||||
self.image_build_service.drun(image)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
self.image_build_service.drun(devops)
|
||||
|
||||
def dockerhub_login(self):
|
||||
image = self.repo.get_docker(self.project)
|
||||
self.image_build_service.dockerhub_login(image)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
self.image_build_service.dockerhub_login(devops)
|
||||
|
||||
def dockerhub_publish(self):
|
||||
image = self.repo.get_docker(self.project)
|
||||
self.image_build_service.dockerhub_publish(image)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
self.image_build_service.dockerhub_publish(devops)
|
||||
|
||||
def test(self):
|
||||
image = self.repo.get_docker(self.project)
|
||||
self.image_build_service.test(image)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
self.image_build_service.test(devops)
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
from .common import Validateable, DnsRecord, Devops
|
||||
from .common import Validateable, DnsRecord, Devops, BuildType, MixinType, ReleaseType
|
||||
from .devops_factory import DevopsFactory
|
||||
from .image import Image
|
||||
from .c4k import C4k
|
||||
from .release import Release, ReleaseContext, ReleaseType, Version, EnvironmentKeys
|
||||
from .release import Release
|
||||
from .credentials import Credentials, CredentialMapping, GopassType
|
||||
from .version import Version
|
||||
from .build_file import BuildFileType, BuildFile
|
||||
from .init_service import InitService
|
128
src/main/python/ddadevops/domain/build_file.py
Normal file
128
src/main/python/ddadevops/domain/build_file.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
from enum import Enum
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
import re
|
||||
import json
|
||||
from .common import (
|
||||
Validateable,
|
||||
Devops,
|
||||
ReleaseType,
|
||||
)
|
||||
from .version import (
|
||||
Version,
|
||||
)
|
||||
|
||||
|
||||
class BuildFileType(Enum):
|
||||
JS = ".json"
|
||||
JAVA_GRADLE = ".gradle"
|
||||
JAVA_CLOJURE = ".clj"
|
||||
PYTHON = ".py"
|
||||
|
||||
|
||||
class BuildFile(Validateable):
|
||||
def __init__(self, file_path: Path, content: str):
|
||||
self.file_path = file_path
|
||||
self.content = content
|
||||
|
||||
def validate(self):
|
||||
result = []
|
||||
result += self.__validate_is_not_empty__("file_path")
|
||||
result += self.__validate_is_not_empty__("content")
|
||||
if not self.build_file_type():
|
||||
result += [f"Suffix {self.file_path} is unknown."]
|
||||
return result
|
||||
|
||||
def build_file_type(self):
|
||||
if not self.file_path:
|
||||
return None
|
||||
config_file_type = self.file_path.suffix
|
||||
match config_file_type:
|
||||
case ".json":
|
||||
result = BuildFileType.JS
|
||||
case ".gradle":
|
||||
result = BuildFileType.JAVA_GRADLE
|
||||
case ".clj":
|
||||
result = BuildFileType.JAVA_CLOJURE
|
||||
case ".py":
|
||||
result = BuildFileType.PYTHON
|
||||
case _:
|
||||
result = None
|
||||
return result
|
||||
|
||||
def get_version(self) -> Version:
|
||||
try:
|
||||
match self.build_file_type():
|
||||
case BuildFileType.JS:
|
||||
version_str = json.loads(self.content)["version"]
|
||||
case BuildFileType.JAVA_GRADLE:
|
||||
# TODO: '\nversion = ' will not parse all ?!
|
||||
version_line = re.search("\nversion = .*", self.content)
|
||||
version_line_group = version_line.group()
|
||||
version_string = re.search(
|
||||
"[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?", version_line_group
|
||||
)
|
||||
version_str = version_string.group()
|
||||
case BuildFileType.PYTHON:
|
||||
# TODO: '\nversion = ' will not parse all ?!
|
||||
version_line = re.search("\nversion = .*\n", self.content)
|
||||
version_line_group = version_line.group()
|
||||
version_string = re.search(
|
||||
"[0-9]*\\.[0-9]*\\.[0-9]*(-dev)?[0-9]*", version_line_group
|
||||
)
|
||||
version_str = version_string.group()
|
||||
case BuildFileType.JAVA_CLOJURE:
|
||||
# TODO: unsure about the trailing '\n' !
|
||||
version_line = re.search("\\(defproject .*\n", self.content)
|
||||
version_line_group = version_line.group()
|
||||
version_string = re.search(
|
||||
"[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?", version_line_group
|
||||
)
|
||||
version_str = version_string.group()
|
||||
except:
|
||||
raise Exception(f"Version not found in file {self.file_path}")
|
||||
|
||||
result = Version.from_str(version_str)
|
||||
result.throw_if_invalid()
|
||||
|
||||
return result
|
||||
|
||||
def set_version(self, new_version: Version):
|
||||
# TODO: How can we create regex-pattern constants to use them at both places?
|
||||
try:
|
||||
match self.build_file_type():
|
||||
case BuildFileType.JS:
|
||||
json_data = json.loads(self.content)
|
||||
json_data["version"] = new_version.to_string()
|
||||
self.content = json.dumps(json_data, indent=4)
|
||||
case BuildFileType.JAVA_GRADLE:
|
||||
substitute = re.sub(
|
||||
'\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"',
|
||||
f'\nversion = "{new_version.to_string()}"',
|
||||
self.content,
|
||||
)
|
||||
self.content = substitute
|
||||
case BuildFileType.PYTHON:
|
||||
substitute = re.sub(
|
||||
'\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-dev)?[0-9]*"',
|
||||
f'\nversion = "{new_version.to_string()}"',
|
||||
self.content,
|
||||
)
|
||||
self.content = substitute
|
||||
case BuildFileType.JAVA_CLOJURE:
|
||||
version_line = re.search("\\(defproject .*\n", self.content)
|
||||
version_line_group = version_line.group()
|
||||
# TODO: we should stick here on defproject instead of first line!
|
||||
version_string = re.search(
|
||||
"[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?", version_line_group
|
||||
)
|
||||
version_str = version_string.group()
|
||||
substitute = re.sub(
|
||||
'"[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"',
|
||||
f'"{new_version.to_string()}"',
|
||||
self.content,
|
||||
1
|
||||
)
|
||||
self.content = substitute
|
||||
except:
|
||||
raise Exception(f"Version not found in file {self.file_path}")
|
|
@ -5,17 +5,20 @@ from .common import (
|
|||
Devops,
|
||||
)
|
||||
|
||||
|
||||
class C4k(Validateable):
|
||||
def __init__(self, config: dict):
|
||||
tmp_executabel_name = config["C4kMixin"]["executabel_name"]
|
||||
if not tmp_executabel_name:
|
||||
tmp_executabel_name = config["module"]
|
||||
self.executabel_name = tmp_executabel_name
|
||||
self.c4k_mixin_config = config["C4kMixin"]["config"]
|
||||
self.c4k_mixin_auth = config["C4kMixin"]["auth"]
|
||||
tmp = self.c4k_mixin_config["mon-cfg"]
|
||||
tmp.update({"cluster-name": config["module"], "cluster-stage": config["stage"]})
|
||||
self.c4k_mixin_config.update({"mon-cfg": tmp})
|
||||
def __init__(self, input: dict):
|
||||
self.module = input.get("module")
|
||||
self.stage = input.get("stage")
|
||||
self.c4k_executable_name = input.get("c4k_executable_name", input.get("module"))
|
||||
self.c4k_config = input.get("c4k_config", {})
|
||||
self.c4k_grafana_cloud_url = input.get(
|
||||
"c4k_grafana_cloud_url",
|
||||
"https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push",
|
||||
)
|
||||
self.c4k_auth = input.get("c4k_auth", {})
|
||||
self.c4k_grafana_cloud_user = input.get('c4k_grafana_cloud_user')
|
||||
self.c4k_grafana_cloud_password = input.get('c4k_grafana_cloud_password')
|
||||
self.dns_record: Optional[DnsRecord] = None
|
||||
|
||||
# TODO: these functions should be located at TerraformBuild later on.
|
||||
|
@ -24,20 +27,39 @@ class C4k(Validateable):
|
|||
|
||||
def validate(self) -> List[str]:
|
||||
result = []
|
||||
result += self.__validate_is_not_empty__("fqdn")
|
||||
result += self.__validate_is_not_empty__("module")
|
||||
result += self.__validate_is_not_empty__("stage")
|
||||
result += self.__validate_is_not_empty__("c4k_executable_name")
|
||||
result += self.__validate_is_not_empty__("c4k_grafana_cloud_user")
|
||||
result += self.__validate_is_not_empty__("c4k_grafana_cloud_password")
|
||||
if self.dns_record:
|
||||
result += self.dns_record.validate()
|
||||
return result
|
||||
|
||||
def config(self):
|
||||
fqdn = self.dns_record.fqdn
|
||||
self.c4k_mixin_config.update({"fqdn": fqdn})
|
||||
return self.c4k_mixin_config
|
||||
if not self.dns_record:
|
||||
raise ValueError("dns_reqord was not set.")
|
||||
result = self.c4k_config.copy()
|
||||
result["fqdn"] = self.dns_record.fqdn
|
||||
result["mon-cfg"] = {
|
||||
"cluster-name": self.module,
|
||||
"cluster-stage": self.stage,
|
||||
"grafana-cloud-url": self.c4k_grafana_cloud_url,
|
||||
}
|
||||
return result
|
||||
|
||||
def command(self, build: Devops):
|
||||
module = build.module
|
||||
build_path = build.build_path()
|
||||
def auth(self):
|
||||
result = self.c4k_auth.copy()
|
||||
result["mon-auth"] = {
|
||||
"grafana-cloud-user": self.c4k_grafana_cloud_user,
|
||||
"grafana-cloud-password": self.c4k_grafana_cloud_password,
|
||||
}
|
||||
return result
|
||||
|
||||
def command(self, devops: Devops):
|
||||
module = devops.module
|
||||
build_path = devops.build_path()
|
||||
config_path = f"{build_path}/out_c4k_config.yaml"
|
||||
auth_path = f"{build_path}/out_c4k_auth.yaml"
|
||||
output_path = f"{build_path}/out_{module}.yaml"
|
||||
return f"c4k-{self.executabel_name}-standalone.jar {config_path} {auth_path} > {output_path}"
|
||||
return f"c4k-{self.c4k_executable_name}-standalone.jar {config_path} {auth_path} > {output_path}"
|
||||
|
|
|
@ -1,17 +1,46 @@
|
|||
from typing import List
|
||||
import deprecation
|
||||
from enum import Enum
|
||||
from typing import List, TypedDict
|
||||
import deprecation
|
||||
|
||||
|
||||
def filter_none(list_to_filter):
|
||||
return [x for x in list_to_filter if x is not None]
|
||||
|
||||
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}' must not be empty."]
|
||||
|
||||
class BuildType(Enum):
|
||||
IMAGE = 0
|
||||
C4K = 1
|
||||
|
||||
|
||||
class MixinType(Enum):
|
||||
RELEASE = 0
|
||||
|
||||
|
||||
class ReleaseType(Enum):
|
||||
MAJOR = 3
|
||||
MINOR = 2
|
||||
PATCH = 1
|
||||
NONE = None
|
||||
|
||||
|
||||
class Validateable:
|
||||
def __validate_is_not_none__(self, field_name: str) -> List[str]:
|
||||
value = self.__dict__[field_name]
|
||||
if value is None:
|
||||
return [f"Field '{field_name}' must not be None."]
|
||||
return []
|
||||
|
||||
def __validate_is_not_empty__(self, field_name: str) -> List[str]:
|
||||
result = self.__validate_is_not_none__(field_name)
|
||||
if len(result) == 0:
|
||||
value = self.__dict__[field_name]
|
||||
if type(value) is str and value == "":
|
||||
result += [f"Field '{field_name}' must not be empty."]
|
||||
elif type(value) is list and len(value) == 0:
|
||||
result += [f"Field '{field_name}' must not be empty."]
|
||||
return result
|
||||
|
||||
def validate(self) -> List[str]:
|
||||
return []
|
||||
|
||||
|
@ -20,9 +49,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
|
||||
|
@ -39,28 +69,37 @@ class DnsRecord(Validateable):
|
|||
|
||||
class Devops(Validateable):
|
||||
def __init__(
|
||||
self, stage: str, project_root_path: str, module: str, name: str | None =None, build_dir_name: str="target"
|
||||
self,
|
||||
input: dict,
|
||||
specialized_builds: dict[BuildType, Validateable],
|
||||
mixins: dict[MixinType, Validateable],
|
||||
):
|
||||
self.stage = stage
|
||||
self.name = name
|
||||
self.project_root_path = project_root_path
|
||||
self.module = module
|
||||
if not name:
|
||||
self.name = module
|
||||
self.build_dir_name = build_dir_name
|
||||
# Deprecated - no longer use generic stack ...
|
||||
self.stack : dict = {}
|
||||
|
||||
@deprecation.deprecated(deprecated_in="3.2")
|
||||
# use .name instead
|
||||
#pylint: disable=method-hidden
|
||||
def name(self):
|
||||
return self.name
|
||||
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]
|
||||
return "/".join(filter_none(path))
|
||||
|
||||
def validate(self) -> List[str]:
|
||||
result = []
|
||||
result += self.__validate_is_not_empty__("stage")
|
||||
result += self.__validate_is_not_empty__("project_root_path")
|
||||
result += self.__validate_is_not_empty__("module")
|
||||
result += self.__validate_is_not_none__("specialized_builds")
|
||||
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):
|
||||
self.stack[key] = value
|
||||
|
||||
|
|
63
src/main/python/ddadevops/domain/credentials.py
Normal file
63
src/main/python/ddadevops/domain/credentials.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
import deprecation
|
||||
from enum import Enum
|
||||
from typing import List, TypedDict
|
||||
from inflection import underscore
|
||||
import deprecation
|
||||
from .common import (
|
||||
Validateable,
|
||||
)
|
||||
|
||||
|
||||
class GopassType(Enum):
|
||||
FIELD = 0
|
||||
PASSWORD = 1
|
||||
|
||||
|
||||
class CredentialMapping(Validateable):
|
||||
def __init__(self, mapping: dict):
|
||||
self.name = mapping.get("name", None)
|
||||
self.gopass_field = mapping.get("gopass_field", None)
|
||||
self.gopass_path = mapping.get("gopass_path", None)
|
||||
|
||||
def validate(self) -> List[str]:
|
||||
result = []
|
||||
result += self.__validate_is_not_empty__("gopass_path")
|
||||
if not self.name and not self.gopass_field:
|
||||
result.append(f"Either name or gopass field has to be defined.")
|
||||
return result
|
||||
|
||||
def gopass_type(self):
|
||||
if self.gopass_field:
|
||||
return GopassType.FIELD
|
||||
else:
|
||||
return GopassType.PASSWORD
|
||||
|
||||
def name_for_input(self):
|
||||
if self.name:
|
||||
result = self.name
|
||||
elif self.gopass_field:
|
||||
result = underscore(self.gopass_field)
|
||||
else:
|
||||
result = ""
|
||||
return result
|
||||
|
||||
def name_for_environment(self):
|
||||
return self.name_for_input().upper()
|
||||
|
||||
|
||||
class Credentials(Validateable):
|
||||
def __init__(self, input: dict, default_mappings: list = []):
|
||||
input_mappings = input.get("credentials_mapping", [])
|
||||
self.mappings = {}
|
||||
for input_mapping in default_mappings:
|
||||
mapping = CredentialMapping(input_mapping)
|
||||
self.mappings[mapping.name_for_input()] = mapping
|
||||
for input_mapping in input_mappings:
|
||||
mapping = CredentialMapping(input_mapping)
|
||||
self.mappings[mapping.name_for_input()] = mapping
|
||||
|
||||
def validate(self) -> List[str]:
|
||||
result = []
|
||||
for mapping in self.mappings.values():
|
||||
result += mapping.validate()
|
||||
return result
|
48
src/main/python/ddadevops/domain/devops_factory.py
Normal file
48
src/main/python/ddadevops/domain/devops_factory.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
import deprecation
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
from .common import Devops, BuildType, MixinType
|
||||
from .image import Image
|
||||
from .c4k import C4k
|
||||
from .release import Release
|
||||
from .version import Version
|
||||
|
||||
|
||||
class DevopsFactory:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def build_devops(self, input: dict, version: Version = None) -> 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)
|
||||
|
||||
mixins = {}
|
||||
if MixinType.RELEASE in mixin_types:
|
||||
mixins[MixinType.RELEASE] = Release(input, version)
|
||||
|
||||
devops = Devops(input, specialized_builds=specialized_builds, mixins=mixins)
|
||||
|
||||
devops.throw_if_invalid()
|
||||
|
||||
return devops
|
||||
|
||||
def merge(self, input: dict, context: dict, authorization: dict) -> dict:
|
||||
return {} | context | authorization | input
|
||||
|
||||
def __parse_build_types__(self, build_types: List[str]) -> List[BuildType]:
|
||||
result = []
|
||||
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
|
|
@ -1,28 +1,38 @@
|
|||
from typing import Optional, List
|
||||
from .common import (
|
||||
filter_none,
|
||||
Validateable,
|
||||
Devops,
|
||||
)
|
||||
|
||||
|
||||
class Image(Validateable):
|
||||
def __init__(
|
||||
self,
|
||||
dockerhub_user,
|
||||
dockerhub_password,
|
||||
devops: Devops,
|
||||
use_package_common_files=True,
|
||||
build_commons_path=None,
|
||||
docker_build_commons_dir_name="docker",
|
||||
docker_publish_tag=None,
|
||||
input: dict,
|
||||
):
|
||||
self.dockerhub_user = dockerhub_user
|
||||
self.dockerhub_password = dockerhub_password
|
||||
self.use_package_common_files = use_package_common_files
|
||||
self.build_commons_path = build_commons_path
|
||||
self.docker_build_commons_dir_name = docker_build_commons_dir_name
|
||||
self.docker_publish_tag = docker_publish_tag
|
||||
self.devops = devops
|
||||
self.image_dockerhub_user = input.get("image_dockerhub_user")
|
||||
self.image_dockerhub_password = input.get("image_dockerhub_password")
|
||||
self.image_tag = input.get("image_tag")
|
||||
self.image_build_commons_path = input.get("image_build_commons_path")
|
||||
self.image_use_package_common_files = input.get(
|
||||
"image_use_package_common_files", True
|
||||
)
|
||||
self.image_build_commons_dir_name = input.get(
|
||||
"image_build_commons_dir_name", "docker"
|
||||
)
|
||||
|
||||
def docker_build_commons_path(self):
|
||||
commons_path = [self.build_commons_path, self.docker_build_commons_dir_name]
|
||||
def validate(self) -> List[str]:
|
||||
result = []
|
||||
result += self.__validate_is_not_empty__("image_dockerhub_user")
|
||||
result += self.__validate_is_not_empty__("image_dockerhub_password")
|
||||
if not self.image_use_package_common_files:
|
||||
result += self.__validate_is_not_empty__("image_build_commons_path")
|
||||
result += self.__validate_is_not_empty__("image_build_commons_dir_name")
|
||||
return result
|
||||
|
||||
def build_commons_path(self):
|
||||
commons_path = [
|
||||
self.image_build_commons_path,
|
||||
self.image_build_commons_dir_name,
|
||||
]
|
||||
return "/".join(filter_none(commons_path)) + "/"
|
||||
|
|
129
src/main/python/ddadevops/domain/init_service.py
Normal file
129
src/main/python/ddadevops/domain/init_service.py
Normal file
|
@ -0,0 +1,129 @@
|
|||
from pathlib import Path
|
||||
from typing import List
|
||||
from .common import Devops, MixinType, BuildType
|
||||
from .credentials import Credentials, GopassType
|
||||
from .devops_factory import DevopsFactory
|
||||
from .version import Version
|
||||
from .release import ReleaseType
|
||||
from ..infrastructure import BuildFileRepository, CredentialsApi, EnvironmentApi, GitApi
|
||||
|
||||
|
||||
class InitService:
|
||||
def __init__(
|
||||
self,
|
||||
devops_factory,
|
||||
build_file_repository,
|
||||
credentials_api,
|
||||
environment_api,
|
||||
git_api,
|
||||
):
|
||||
self.devops_factory = devops_factory
|
||||
self.build_file_repository = build_file_repository
|
||||
self.credentials_api = credentials_api
|
||||
self.environment_api = environment_api
|
||||
self.git_api = git_api
|
||||
|
||||
@classmethod
|
||||
def prod(cls, base_dir: str):
|
||||
return cls(
|
||||
DevopsFactory(),
|
||||
BuildFileRepository(base_dir),
|
||||
CredentialsApi(),
|
||||
EnvironmentApi(),
|
||||
GitApi(),
|
||||
)
|
||||
|
||||
def initialize(self, input: dict) -> Devops:
|
||||
build_types = self.devops_factory.__parse_build_types__(input["build_types"])
|
||||
mixin_types = self.devops_factory.__parse_mixin_types__(input["mixin_types"])
|
||||
|
||||
version = None
|
||||
default_mappings = []
|
||||
|
||||
if BuildType.C4K in build_types:
|
||||
default_mappings += [
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
"name": "c4k_grafana_cloud_user",
|
||||
},
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"name": "c4k_grafana_cloud_password",
|
||||
},
|
||||
]
|
||||
if BuildType.IMAGE in build_types:
|
||||
default_mappings += [
|
||||
{
|
||||
"gopass_path": "meissa/web/docker.com",
|
||||
"gopass_field": "login",
|
||||
"name": "image_dockerhub_user",
|
||||
},
|
||||
{
|
||||
"gopass_path": "meissa/web/docker.com",
|
||||
"name": "image_dockerhub_password",
|
||||
},
|
||||
]
|
||||
|
||||
if MixinType.RELEASE in mixin_types:
|
||||
primary_build_file_id = input.get(
|
||||
"release_primary_build_file", "./project.clj"
|
||||
)
|
||||
primary_build_file = self.build_file_repository.get(
|
||||
Path(primary_build_file_id)
|
||||
)
|
||||
version = primary_build_file.get_version()
|
||||
|
||||
credentials = Credentials(input, default_mappings)
|
||||
authorization = self.authorization(credentials)
|
||||
|
||||
context = self.context(mixin_types, version)
|
||||
|
||||
merged = self.devops_factory.merge(input, context, authorization)
|
||||
|
||||
return self.devops_factory.build_devops(merged, version=version)
|
||||
|
||||
def context(self, mixin_types, version) -> dict:
|
||||
result = {}
|
||||
|
||||
tag = self.environment_api.get("IMAGE_TAG")
|
||||
|
||||
if MixinType.RELEASE in mixin_types:
|
||||
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()
|
||||
|
||||
if not tag:
|
||||
tag = version.to_string()
|
||||
|
||||
if tag:
|
||||
result["image_tag"] = tag
|
||||
|
||||
return result
|
||||
|
||||
def authorization(self, credentials: Credentials) -> List[str]:
|
||||
result = {}
|
||||
for name in credentials.mappings.keys():
|
||||
mapping = credentials.mappings[name]
|
||||
env_value = self.environment_api.get(mapping.name_for_environment())
|
||||
if env_value:
|
||||
result[name] = env_value
|
||||
else:
|
||||
if mapping.gopass_type() == GopassType.FIELD:
|
||||
result[name] = self.credentials_api.gopass_field_from_path(
|
||||
mapping.gopass_path, mapping.gopass_field
|
||||
)
|
||||
if mapping.gopass_type() == GopassType.PASSWORD:
|
||||
result[name] = self.credentials_api.gopass_password_from_path(
|
||||
mapping.gopass_path
|
||||
)
|
||||
return result
|
|
@ -1,121 +1,60 @@
|
|||
from enum import Enum
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
from pathlib import Path
|
||||
from .common import (
|
||||
Validateable,
|
||||
Devops,
|
||||
ReleaseType,
|
||||
)
|
||||
from .version import (
|
||||
Version,
|
||||
)
|
||||
|
||||
class ReleaseType(Enum):
|
||||
MAJOR = 0
|
||||
MINOR = 1
|
||||
PATCH = 2
|
||||
SNAPSHOT = 3
|
||||
BUMP = None
|
||||
|
||||
class EnvironmentKeys(Enum):
|
||||
DDADEVOPS_RELEASE_TYPE = 0
|
||||
|
||||
class Version():
|
||||
|
||||
def __init__(self, path: Path, version_list: list):
|
||||
self.path = path
|
||||
self.version_list = version_list
|
||||
self.version_string: Optional[str | None] = None
|
||||
self.is_snapshot: Optional[bool | None] = None
|
||||
|
||||
def increment(self, release_type: ReleaseType | None):
|
||||
self.is_snapshot = False
|
||||
match release_type:
|
||||
case ReleaseType.BUMP:
|
||||
self.is_snapshot = True
|
||||
self.version_list[ReleaseType.PATCH.value] += 1
|
||||
case ReleaseType.SNAPSHOT:
|
||||
self.is_snapshot = True
|
||||
case ReleaseType.PATCH:
|
||||
self.version_list[ReleaseType.PATCH.value] += 1
|
||||
case ReleaseType.MINOR:
|
||||
self.version_list[ReleaseType.PATCH.value] = 0
|
||||
self.version_list[ReleaseType.MINOR.value] += 1
|
||||
case ReleaseType.MAJOR:
|
||||
self.version_list[ReleaseType.PATCH.value] = 0
|
||||
self.version_list[ReleaseType.MINOR.value] = 0
|
||||
self.version_list[ReleaseType.MAJOR.value] += 1
|
||||
case None:
|
||||
raise RuntimeError("Release Type was not set!")
|
||||
|
||||
def get_version_string(self) -> str:
|
||||
self.version_string = ".".join([str(x) for x in self.version_list])
|
||||
if self.is_snapshot:
|
||||
self.version_string += "-SNAPSHOT"
|
||||
return self.version_string
|
||||
|
||||
def create_release_version(self, release_type: ReleaseType | None):
|
||||
release_version = Version(self.path, self.version_list.copy())
|
||||
release_version.is_snapshot = self.is_snapshot
|
||||
release_version.increment(release_type)
|
||||
return release_version
|
||||
|
||||
def create_bump_version(self):
|
||||
bump_version = Version(self.path, self.version_list.copy())
|
||||
bump_version.is_snapshot = self.is_snapshot
|
||||
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
|
||||
class Release(Validateable):
|
||||
def __init__(self, input: dict, version: Version):
|
||||
self.release_type = ReleaseType[input.get("release_type", "NONE")]
|
||||
self.release_main_branch = input.get("release_main_branch", "main")
|
||||
self.release_current_branch = input.get("release_current_branch")
|
||||
self.release_primary_build_file = input.get(
|
||||
"release_primary_build_file", "./project.clj"
|
||||
)
|
||||
self.release_secondary_build_files = input.get(
|
||||
"release_secondary_build_files", []
|
||||
)
|
||||
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__("release_main_branch")
|
||||
result += self.__validate_is_not_empty__("release_current_branch")
|
||||
result += self.__validate_is_not_empty__("release_primary_build_file")
|
||||
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,
|
||||
try:
|
||||
Path(self.release_primary_build_file)
|
||||
except Exception as e:
|
||||
result.append(
|
||||
f"release_primary_build_file must be a valid path but was {e}"
|
||||
)
|
||||
for path in self.release_secondary_build_files:
|
||||
try:
|
||||
Path(path)
|
||||
except Exception as e:
|
||||
result.append(
|
||||
f"release_secondary_build_file must be contain valid paths but was {e}"
|
||||
)
|
||||
if self.version:
|
||||
result += self.version.validate()
|
||||
if (
|
||||
self.release_type is not None
|
||||
and self.release_type != ReleaseType.NONE
|
||||
and self.release_main_branch != self.release_current_branch
|
||||
):
|
||||
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
|
||||
|
||||
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)
|
||||
result.append(f"Releases are allowed only on {self.release_main_branch}")
|
||||
return result
|
||||
|
||||
def build_files(self) -> List[str]:
|
||||
result = [self.release_primary_build_file]
|
||||
result += self.release_secondary_build_files
|
||||
return result
|
||||
|
|
100
src/main/python/ddadevops/domain/version.py
Normal file
100
src/main/python/ddadevops/domain/version.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
from enum import Enum
|
||||
from typing import Optional
|
||||
from .common import (
|
||||
Validateable,
|
||||
)
|
||||
|
||||
|
||||
class Version(Validateable):
|
||||
@classmethod
|
||||
def from_str(cls, input_str: str):
|
||||
snapshot_parsed = input_str.split("-")
|
||||
version_str = snapshot_parsed[0]
|
||||
suffix_str = None
|
||||
if len(snapshot_parsed) > 1:
|
||||
suffix_str = snapshot_parsed[1]
|
||||
version_no_parsed = [int(x) for x in version_str.split(".")]
|
||||
return cls(
|
||||
version_no_parsed,
|
||||
suffix_str,
|
||||
input_str,
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
version_list: list,
|
||||
snapshot_suffix: Optional[str] = None,
|
||||
version_str: Optional[str] = None,
|
||||
):
|
||||
self.version_list = version_list
|
||||
self.snapshot_suffix = snapshot_suffix
|
||||
self.version_string = version_str
|
||||
|
||||
def __eq__(self, other):
|
||||
return other and self.to_string() == other.to_string()
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return self.to_string().__hash__()
|
||||
|
||||
def is_snapshot(self):
|
||||
return not self.snapshot_suffix == None
|
||||
|
||||
def to_string(self) -> str:
|
||||
version_no = ".".join([str(x) for x in self.version_list])
|
||||
if self.is_snapshot():
|
||||
return f"{version_no}-{self.snapshot_suffix}"
|
||||
return version_no
|
||||
|
||||
def validate(self):
|
||||
result = []
|
||||
result += self.__validate_is_not_empty__("version_list")
|
||||
if self.version_list and len(self.version_list) < 3:
|
||||
result += [f"version_list must have at least 3 levels."]
|
||||
if (
|
||||
self.version_list
|
||||
and self.version_string
|
||||
and self.to_string() != self.version_string
|
||||
):
|
||||
result += [
|
||||
f"version_string not parsed correct. Input was {self.version_string} parsed was {self.to_string()}"
|
||||
]
|
||||
return result
|
||||
|
||||
def create_bump(self, snapshot_suffix: str = None):
|
||||
new_version_list = self.version_list.copy()
|
||||
if self.is_snapshot():
|
||||
return Version(
|
||||
new_version_list, snapshot_suffix=self.snapshot_suffix, version_str=None
|
||||
)
|
||||
else:
|
||||
new_version_list[2] += 1
|
||||
return Version(
|
||||
new_version_list, snapshot_suffix=snapshot_suffix, version_str=None
|
||||
)
|
||||
|
||||
def create_patch(self):
|
||||
new_version_list = self.version_list.copy()
|
||||
if self.is_snapshot():
|
||||
return Version(new_version_list, snapshot_suffix=None, version_str=None)
|
||||
else:
|
||||
new_version_list[2] += 1
|
||||
return Version(new_version_list, snapshot_suffix=None, version_str=None)
|
||||
|
||||
def create_minor(self):
|
||||
new_version_list = self.version_list.copy()
|
||||
if self.is_snapshot() and new_version_list[2] == 0:
|
||||
return Version(new_version_list, snapshot_suffix=None, version_str=None)
|
||||
else:
|
||||
new_version_list[2] = 0
|
||||
new_version_list[1] += 1
|
||||
return Version(new_version_list, snapshot_suffix=None, version_str=None)
|
||||
|
||||
def create_major(self):
|
||||
new_version_list = self.version_list.copy()
|
||||
if self.is_snapshot() and new_version_list[2] == 0 and new_version_list[1] == 0:
|
||||
return Version(new_version_list, snapshot_suffix=None, version_str=None)
|
||||
else:
|
||||
new_version_list[2] = 0
|
||||
new_version_list[1] = 0
|
||||
new_version_list[0] += 1
|
||||
return Version(new_version_list, snapshot_suffix=None, version_str=None)
|
|
@ -1 +1,10 @@
|
|||
from .infrastructure import FileApi, ImageApi, ResourceApi, ExecutionApi, ProjectRepository
|
||||
from .infrastructure import (
|
||||
FileApi,
|
||||
ImageApi,
|
||||
ResourceApi,
|
||||
ExecutionApi,
|
||||
EnvironmentApi,
|
||||
CredentialsApi,
|
||||
GitApi,
|
||||
)
|
||||
from .repository import DevopsRepository, BuildFileRepository
|
||||
|
|
|
@ -1,58 +1,30 @@
|
|||
from pathlib import Path
|
||||
from sys import stdout
|
||||
from os import chmod
|
||||
from subprocess import run
|
||||
from os import chmod, environ
|
||||
from pkg_resources import resource_string
|
||||
import yaml
|
||||
from ..domain import Devops, Image, C4k, Release
|
||||
from ..python_util import execute
|
||||
|
||||
|
||||
class ProjectRepository:
|
||||
def set_build(self, project, build):
|
||||
project.set_property("devops_build", build)
|
||||
|
||||
def get_devops(self, project) -> Devops:
|
||||
return project.get_property("build")
|
||||
|
||||
def set_devops(self, project, build: Devops):
|
||||
project.set_property("build", build)
|
||||
|
||||
def get_docker(self, project) -> Image:
|
||||
return project.get_property("docker_build")
|
||||
|
||||
def set_docker(self, project, build: Image):
|
||||
project.set_property("docker_build", build)
|
||||
|
||||
def get_c4k(self, project) -> C4k:
|
||||
return project.get_property("c4k_build")
|
||||
|
||||
def set_c4k(self, project, build: C4k):
|
||||
project.set_property("c4k_build", build)
|
||||
|
||||
def get_release(self, project) -> Release:
|
||||
return project.get_property("release_build")
|
||||
|
||||
def set_release(self, project, build: Release):
|
||||
project.set_property("release_build", build)
|
||||
|
||||
from subprocess import check_output, Popen, PIPE, run
|
||||
from ..domain import Devops, Image, C4k, Release, BuildFile
|
||||
|
||||
|
||||
class ResourceApi:
|
||||
def read_resource(self, path: str) -> bytes:
|
||||
return resource_string(__name__, path)
|
||||
return resource_string('ddadevops', path)
|
||||
|
||||
|
||||
class FileApi:
|
||||
def __init__(self):
|
||||
self.execution_api = ExecutionApi()
|
||||
|
||||
def clean_dir(self, directory: str):
|
||||
execute("rm -rf " + directory, shell=True)
|
||||
execute("mkdir -p " + directory, shell=True)
|
||||
self.execution_api.execute("rm -rf " + directory)
|
||||
self.execution_api.execute("mkdir -p " + directory)
|
||||
|
||||
def cp_force(self, src: str, target_dir: str):
|
||||
execute("cp -f " + src + "* " + target_dir, shell=True)
|
||||
self.execution_api.execute("cp -f " + src + "* " + target_dir)
|
||||
|
||||
def cp_recursive(self, src: str, target_dir: str):
|
||||
execute("cp -r " + src + " " + target_dir, shell=True)
|
||||
self.execution_api.execute("cp -r " + src + " " + target_dir)
|
||||
|
||||
def write_data_to_file(self, path: Path, data: bytes):
|
||||
with open(path, "w", encoding="utf-8") as output_file:
|
||||
|
@ -74,7 +46,7 @@ class ImageApi:
|
|||
|
||||
def drun(self, name: str):
|
||||
run(
|
||||
f"docker run -it --entrypoint=\"\" {name} /bin/bash",
|
||||
f'docker run -it --entrypoint="" {name} /bin/bash',
|
||||
shell=True,
|
||||
check=True,
|
||||
)
|
||||
|
@ -118,11 +90,93 @@ class ImageApi:
|
|||
|
||||
|
||||
class ExecutionApi:
|
||||
def execute(self, command: str, dry_run=False):
|
||||
def execute(self, command: str, dry_run=False, shell=True):
|
||||
output = ""
|
||||
if dry_run:
|
||||
print(command)
|
||||
else:
|
||||
output = execute(command, True)
|
||||
print(output)
|
||||
output = check_output(command, encoding="UTF-8", shell=shell)
|
||||
output = output.rstrip()
|
||||
return output
|
||||
|
||||
def execute_live(command):
|
||||
process = Popen(command, stdout=PIPE)
|
||||
for line in iter(process.stdout.readline, b""):
|
||||
print(line.decode("utf-8"), end="")
|
||||
process.stdout.close()
|
||||
process.wait()
|
||||
|
||||
|
||||
class EnvironmentApi:
|
||||
def get(self, key):
|
||||
return environ.get(key)
|
||||
|
||||
|
||||
class CredentialsApi:
|
||||
def __init__(self):
|
||||
self.execution_api = ExecutionApi()
|
||||
|
||||
def gopass_field_from_path(self, path, field):
|
||||
credential = None
|
||||
if path and field:
|
||||
print("get field for: " + path + ", " + field)
|
||||
credential = self.execution_api.execute(["gopass", "show", path, field], shell=False)
|
||||
return credential
|
||||
|
||||
def gopass_password_from_path(self, path):
|
||||
credential = None
|
||||
if path:
|
||||
print("get password for: " + path)
|
||||
credential = self.execution_api.execute(
|
||||
["gopass", "show", "--password", path], shell=False
|
||||
)
|
||||
return credential
|
||||
|
||||
|
||||
class GitApi:
|
||||
def __init__(self):
|
||||
self.execution_api = ExecutionApi()
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def get_latest_n_commits(self, n: int):
|
||||
return self.execution_api.execute(f'git log --oneline --format="%s %b" -n {n}')
|
||||
|
||||
def get_latest_commit(self):
|
||||
return self.get_latest_n_commits(1)
|
||||
|
||||
def tag_annotated(self, annotation: str, message: str, count: int):
|
||||
return self.execution_api.execute(
|
||||
f"git tag -a {annotation} -m '{message}' HEAD~{count}"
|
||||
)
|
||||
|
||||
def tag_annotated_second_last(self, annotation: str, message: str):
|
||||
return self.tag_annotated(annotation, message, 1)
|
||||
|
||||
def get_latest_tag(self):
|
||||
return self.execution_api.execute("git describe --tags --abbrev=0")
|
||||
|
||||
def get_current_branch(self):
|
||||
return "".join(self.execution_api.execute("git branch --show-current")).rstrip()
|
||||
|
||||
def init(self, default_branch: str = "main"):
|
||||
self.execution_api.execute("git init")
|
||||
self.execution_api.execute(f"git checkout -b {default_branch}")
|
||||
|
||||
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.name {name}")
|
||||
|
||||
def add_file(self, file_path: Path):
|
||||
return self.execution_api.execute(f"git add {file_path}")
|
||||
|
||||
def add_remote(self, origin: str, url: str):
|
||||
return self.execution_api.execute(f"git remote add {origin} {url}")
|
||||
|
||||
def commit(self, commit_message: str):
|
||||
return self.execution_api.execute(f'git commit -m "{commit_message}"')
|
||||
|
||||
def push(self):
|
||||
return self.execution_api.execute("git push")
|
||||
|
||||
def checkout(self, branch: str):
|
||||
return self.execution_api.execute(f"git checkout {branch}")
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
from .infrastructure_api import FileHandler, EnvironmentApi, GitApi, JsonFileHandler, GradleFileHandler, PythonFileHandler, ClojureFileHandler
|
||||
from .repo import VersionRepository, ReleaseContextRepository, ReleaseTypeRepository
|
|
@ -1,242 +0,0 @@
|
|||
import json
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
from os import environ
|
||||
from ..infrastructure import ExecutionApi
|
||||
|
||||
# TODO: jem, zam - 2023_04_18: Discuss if we can move more functionality to domain?
|
||||
class FileHandler(ABC):
|
||||
def __init__(self) -> None:
|
||||
self.config_file_path: Optional[Path | None] = None
|
||||
self.config_file_type: Optional[Path | None] = None
|
||||
|
||||
@classmethod
|
||||
def from_file_path(cls, file_path):
|
||||
config_file_type = file_path.suffix
|
||||
match config_file_type:
|
||||
case '.json':
|
||||
file_handler = JsonFileHandler()
|
||||
case '.gradle':
|
||||
file_handler = GradleFileHandler()
|
||||
case '.clj':
|
||||
file_handler = ClojureFileHandler()
|
||||
case '.py':
|
||||
file_handler = PythonFileHandler()
|
||||
case _:
|
||||
raise RuntimeError(
|
||||
f'The file type "{config_file_type}" is not implemented')
|
||||
# TODO: Attribute is only set in classmethod. Should this be initialized outside of this class?
|
||||
file_handler.config_file_path = file_path
|
||||
file_handler.config_file_type = config_file_type
|
||||
return file_handler
|
||||
|
||||
@abstractmethod
|
||||
def parse(self) -> tuple[list[int], bool]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def write(self, version_string):
|
||||
pass
|
||||
|
||||
|
||||
class JsonFileHandler(FileHandler):
|
||||
|
||||
def parse(self) -> tuple[list[int], bool]:
|
||||
if self.config_file_path is None:
|
||||
raise ValueError("No file name given.")
|
||||
with open(self.config_file_path, 'r', encoding='utf-8') as json_file:
|
||||
json_version = json.load(json_file)['version']
|
||||
is_snapshot = False
|
||||
if '-SNAPSHOT' in json_version:
|
||||
is_snapshot = True
|
||||
json_version = json_version.replace('-SNAPSHOT', '')
|
||||
version = [int(x) for x in json_version.split('.')]
|
||||
return version, is_snapshot
|
||||
|
||||
def write(self, version_string):
|
||||
with open(self.config_file_path, 'r+', encoding='utf-8') as json_file:
|
||||
json_data = json.load(json_file)
|
||||
json_data['version'] = version_string
|
||||
json_file.seek(0)
|
||||
json.dump(json_data, json_file, indent=4)
|
||||
json_file.truncate()
|
||||
|
||||
|
||||
class GradleFileHandler(FileHandler):
|
||||
|
||||
def parse(self) -> tuple[list[int], bool]:
|
||||
if self.config_file_path is None:
|
||||
raise ValueError("No file name given.")
|
||||
with open(self.config_file_path, 'r', encoding='utf-8') as gradle_file:
|
||||
contents = gradle_file.read()
|
||||
version_line = re.search("\nversion = .*", contents)
|
||||
exception = Exception("Version not found in gradle file")
|
||||
if version_line is None:
|
||||
raise exception
|
||||
|
||||
version_line_group = version_line.group()
|
||||
version_string = re.search(
|
||||
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line_group)
|
||||
if version_string is None:
|
||||
raise exception
|
||||
|
||||
version_string_group = version_string.group()
|
||||
is_snapshot = False
|
||||
if '-SNAPSHOT' in version_string_group:
|
||||
is_snapshot = True
|
||||
version_string_group = version_string_group.replace('-SNAPSHOT', '')
|
||||
|
||||
version = [int(x) for x in version_string_group.split('.')]
|
||||
|
||||
return version, is_snapshot
|
||||
|
||||
def write(self, version_string):
|
||||
with open(self.config_file_path, 'r+', encoding='utf-8') as gradle_file:
|
||||
contents = gradle_file.read()
|
||||
version_substitute = re.sub(
|
||||
'\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents)
|
||||
gradle_file.seek(0)
|
||||
gradle_file.write(version_substitute)
|
||||
gradle_file.truncate()
|
||||
|
||||
|
||||
class PythonFileHandler(FileHandler):
|
||||
|
||||
def parse(self) -> tuple[list[int], bool]:
|
||||
if self.config_file_path is None:
|
||||
raise ValueError("No file name given.")
|
||||
with open(self.config_file_path, 'r', encoding='utf-8') as python_file:
|
||||
contents = python_file.read()
|
||||
version_line = re.search("\nversion = .*\n", contents)
|
||||
exception = Exception("Version not found in gradle file")
|
||||
if version_line is None:
|
||||
raise exception
|
||||
|
||||
version_line_group = version_line.group()
|
||||
version_string = re.search(
|
||||
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line_group)
|
||||
if version_string is None:
|
||||
raise exception
|
||||
|
||||
version_string_group = version_string.group()
|
||||
is_snapshot = False
|
||||
if '-SNAPSHOT' in version_string_group:
|
||||
is_snapshot = True
|
||||
version_string_group = version_string_group.replace('-SNAPSHOT', '')
|
||||
|
||||
version = [int(x) for x in version_string_group.split('.')]
|
||||
|
||||
return version, is_snapshot
|
||||
|
||||
def write(self, version_string):
|
||||
with open(self.config_file_path, 'r+', encoding='utf-8') as python_file:
|
||||
contents = python_file.read()
|
||||
version_substitute = re.sub(
|
||||
'\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents)
|
||||
python_file.seek(0)
|
||||
python_file.write(version_substitute)
|
||||
python_file.truncate()
|
||||
|
||||
|
||||
class ClojureFileHandler(FileHandler):
|
||||
|
||||
def parse(self) -> tuple[list[int], bool]:
|
||||
if self.config_file_path is None:
|
||||
raise ValueError("No file name given.")
|
||||
with open(self.config_file_path, 'r', encoding='utf-8') as clj_file:
|
||||
contents = clj_file.read()
|
||||
version_line = re.search("^\\(defproject .*\n", contents)
|
||||
exception = Exception("Version not found in clj file")
|
||||
if version_line is None:
|
||||
raise exception
|
||||
|
||||
version_line_group = version_line.group()
|
||||
version_string = re.search(
|
||||
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line_group)
|
||||
if version_string is None:
|
||||
raise exception
|
||||
|
||||
version_string_group = version_string.group()
|
||||
is_snapshot = False
|
||||
if '-SNAPSHOT' in version_string_group:
|
||||
is_snapshot = True
|
||||
version_string_group = version_string_group.replace('-SNAPSHOT', '')
|
||||
|
||||
version = [int(x) for x in version_string_group.split('.')]
|
||||
|
||||
return version, is_snapshot
|
||||
|
||||
def write(self, version_string):
|
||||
with open(self.config_file_path, 'r+', encoding='utf-8') as clj_file:
|
||||
clj_first = clj_file.readline()
|
||||
clj_rest = clj_file.read()
|
||||
version_substitute = re.sub(
|
||||
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', f'"{version_string}"\n', clj_first)
|
||||
clj_file.seek(0)
|
||||
clj_file.write(version_substitute)
|
||||
clj_file.write(clj_rest)
|
||||
clj_file.truncate()
|
||||
|
||||
|
||||
class GitApi():
|
||||
|
||||
def __init__(self):
|
||||
self.execution_api = ExecutionApi()
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
def get_latest_n_commits(self, n: int):
|
||||
return self.execution_api.execute(
|
||||
f'git log --oneline --format="%s %b" -n {n}')
|
||||
|
||||
def get_latest_commit(self):
|
||||
return self.get_latest_n_commits(1)
|
||||
|
||||
def tag_annotated(self, annotation: str, message: str, count: int):
|
||||
return self.execution_api.execute(
|
||||
f'git tag -a {annotation} -m {message} HEAD~{count}')
|
||||
|
||||
def tag_annotated_second_last(self, annotation: str, message:str):
|
||||
return self.tag_annotated(annotation, message, 1)
|
||||
|
||||
def get_latest_tag(self):
|
||||
return self.execution_api.execute('git describe --tags --abbrev=0')
|
||||
|
||||
def get_current_branch(self):
|
||||
return ''.join(self.execution_api.execute('git branch --show-current')).rstrip()
|
||||
|
||||
def init(self, default_branch: str = "main"):
|
||||
self.execution_api.execute('git init')
|
||||
self.execution_api.execute(f'git checkout -b {default_branch}')
|
||||
|
||||
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.name {name}')
|
||||
|
||||
def add_file(self, file_path: Path):
|
||||
return self.execution_api.execute(f'git add {file_path}')
|
||||
|
||||
def add_remote(self, origin: str, url: str):
|
||||
return self.execution_api.execute(f'git remote add {origin} {url}')
|
||||
|
||||
def commit(self, commit_message: str):
|
||||
return self.execution_api.execute(
|
||||
f'git commit -m "{commit_message}"')
|
||||
|
||||
def push(self):
|
||||
return self.execution_api.execute('git push')
|
||||
|
||||
def checkout(self, branch: str):
|
||||
return self.execution_api.execute(f'git checkout {branch}')
|
||||
|
||||
class EnvironmentApi():
|
||||
|
||||
def __init__(self):
|
||||
self.environ = environ
|
||||
|
||||
def get(self, key):
|
||||
return self.environ.get(key)
|
||||
|
||||
def set(self, key, value):
|
||||
self.environ[key] = value
|
|
@ -1,121 +0,0 @@
|
|||
|
||||
from src.main.python.ddadevops.domain import (
|
||||
ReleaseContext,
|
||||
Version,
|
||||
ReleaseType,
|
||||
EnvironmentKeys,
|
||||
)
|
||||
from src.main.python.ddadevops.infrastructure.release_mixin.infrastructure_api import (
|
||||
FileHandler,
|
||||
GitApi,
|
||||
EnvironmentApi,
|
||||
)
|
||||
|
||||
|
||||
class VersionRepository:
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
self.file_handler = None
|
||||
|
||||
def load_file(self):
|
||||
self.file_handler = FileHandler.from_file_path(self.file)
|
||||
return self.file_handler
|
||||
|
||||
def write_file(self, version_string):
|
||||
if self.file_handler is None:
|
||||
raise RuntimeError("Version was not created by load_file method.")
|
||||
self.file_handler.write(version_string)
|
||||
|
||||
def parse_file(self):
|
||||
version_list, is_snapshot = self.file_handler.parse()
|
||||
return version_list, is_snapshot
|
||||
|
||||
def get_version(self) -> Version:
|
||||
self.file_handler = self.load_file()
|
||||
version_list, is_snapshot = self.parse_file()
|
||||
version = Version(self.file, version_list)
|
||||
version.is_snapshot = is_snapshot
|
||||
|
||||
return version
|
||||
|
||||
|
||||
class ReleaseTypeRepository:
|
||||
def __init__(
|
||||
self,
|
||||
git_api: GitApi = GitApi(),
|
||||
environment_api: EnvironmentApi = EnvironmentApi(),
|
||||
):
|
||||
self.git_api: GitApi = git_api
|
||||
self.environment_api: EnvironmentApi = environment_api
|
||||
self.get_from_git: bool = False
|
||||
self.get_from_env: bool = False
|
||||
|
||||
@classmethod
|
||||
def from_git(cls, git_api: GitApi):
|
||||
release_type_repo = cls(git_api=git_api)
|
||||
release_type_repo.get_from_git = True
|
||||
return release_type_repo
|
||||
|
||||
@classmethod
|
||||
def from_environment(cls, environment_api: EnvironmentApi):
|
||||
release_type_repo = cls(environment_api=environment_api)
|
||||
release_type_repo.get_from_env = True
|
||||
return release_type_repo
|
||||
|
||||
def __get_release_type_git(self) -> ReleaseType | None:
|
||||
latest_commit = self.git_api.get_latest_commit().upper()
|
||||
if ReleaseType.MAJOR.name in latest_commit:
|
||||
return ReleaseType.MAJOR
|
||||
if ReleaseType.MINOR.name in latest_commit:
|
||||
return ReleaseType.MINOR
|
||||
if ReleaseType.PATCH.name in latest_commit:
|
||||
return ReleaseType.PATCH
|
||||
if ReleaseType.SNAPSHOT.name in latest_commit:
|
||||
return ReleaseType.SNAPSHOT
|
||||
return None
|
||||
|
||||
def __get_release_type_environment(self) -> ReleaseType | None:
|
||||
release_name = self.environment_api.get(
|
||||
EnvironmentKeys.DDADEVOPS_RELEASE_TYPE.name
|
||||
).upper()
|
||||
|
||||
if release_name is None:
|
||||
raise ValueError(
|
||||
"Release Name not found. Is the Environment correctly configured?"
|
||||
)
|
||||
|
||||
if ReleaseType.MAJOR.name in release_name:
|
||||
return ReleaseType.MAJOR
|
||||
if ReleaseType.MINOR.name in release_name:
|
||||
return ReleaseType.MINOR
|
||||
if ReleaseType.PATCH.name in release_name:
|
||||
return ReleaseType.PATCH
|
||||
if ReleaseType.SNAPSHOT.name in release_name:
|
||||
return ReleaseType.SNAPSHOT
|
||||
return None
|
||||
|
||||
def get_release_type(self) -> ReleaseType | None:
|
||||
if self.get_from_git:
|
||||
return self.__get_release_type_git()
|
||||
if self.get_from_env:
|
||||
return self.__get_release_type_environment()
|
||||
raise ValueError("No valid api passed to ReleaseTypeRepository")
|
||||
|
||||
|
||||
class ReleaseContextRepository:
|
||||
def __init__(
|
||||
self,
|
||||
version_repository: VersionRepository,
|
||||
release_type_repository: ReleaseTypeRepository,
|
||||
):
|
||||
self.version_repository = version_repository
|
||||
self.release_type_repository = release_type_repository
|
||||
|
||||
def get_release(self, main_branch: str) -> ReleaseContext:
|
||||
result = ReleaseContext(
|
||||
self.release_type_repository.get_release_type(),
|
||||
self.version_repository.get_version(),
|
||||
main_branch,
|
||||
)
|
||||
result.throw_if_invalid()
|
||||
return result
|
43
src/main/python/ddadevops/infrastructure/repository.py
Normal file
43
src/main/python/ddadevops/infrastructure/repository.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
from pathlib import Path
|
||||
from sys import stdout
|
||||
from os import chmod
|
||||
from subprocess import run
|
||||
from pkg_resources import resource_string
|
||||
import yaml
|
||||
import deprecation
|
||||
from ..domain import Devops, Image, C4k, Release, BuildFile
|
||||
from ..python_util import execute
|
||||
|
||||
|
||||
class DevopsRepository:
|
||||
def get_devops(self, project) -> Devops:
|
||||
devops = project.get_property("devops")
|
||||
devops.throw_if_invalid()
|
||||
return devops
|
||||
|
||||
def set_devops(self, project, devops: Devops):
|
||||
devops.throw_if_invalid()
|
||||
project.set_property("devops", devops)
|
||||
|
||||
|
||||
class BuildFileRepository:
|
||||
def __init__(self, base_dir: str):
|
||||
self.base_dir = Path(base_dir)
|
||||
|
||||
def get(self, path: Path) -> BuildFile:
|
||||
with open(self.base_dir.joinpath(path), "r", encoding="utf-8") as file:
|
||||
content = file.read()
|
||||
result = BuildFile(path, content)
|
||||
result.throw_if_invalid()
|
||||
return result
|
||||
|
||||
def write(self, build_file: BuildFile):
|
||||
build_file.throw_if_invalid()
|
||||
with open(
|
||||
self.base_dir.joinpath(build_file.file_path),
|
||||
"r+",
|
||||
encoding="utf-8",
|
||||
) as file:
|
||||
file.seek(0)
|
||||
file.write(build_file.content)
|
||||
file.truncate()
|
|
@ -1,15 +1,22 @@
|
|||
from subprocess import check_output, Popen, PIPE
|
||||
import deprecation
|
||||
|
||||
|
||||
@deprecation.deprecated(deprecated_in="3.2", details="use ExecutionApi instead")
|
||||
def execute(cmd, shell=False):
|
||||
output = check_output(cmd, encoding='UTF-8', shell=shell)
|
||||
output = check_output(cmd, encoding="UTF-8", shell=shell)
|
||||
return output.rstrip()
|
||||
|
||||
|
||||
@deprecation.deprecated(deprecated_in="3.2", details="use ExecutionApi instead")
|
||||
def execute_live(cmd):
|
||||
process = Popen(cmd, stdout=PIPE)
|
||||
for line in iter(process.stdout.readline, b''):
|
||||
print(line.decode('utf-8'), end='')
|
||||
for line in iter(process.stdout.readline, b""):
|
||||
print(line.decode("utf-8"), end="")
|
||||
process.stdout.close()
|
||||
process.wait()
|
||||
|
||||
|
||||
@deprecation.deprecated(deprecated_in="3.2", details="use domain.filter_none instead")
|
||||
def filter_none(list_to_filter):
|
||||
return [x for x in list_to_filter if x is not None]
|
||||
|
|
|
@ -1,36 +1,23 @@
|
|||
from pybuilder.core import Project
|
||||
from src.main.python.ddadevops.devops_build import DevopsBuild
|
||||
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 Release, EnvironmentKeys
|
||||
from .devops_build import DevopsBuild
|
||||
from .application import ReleaseService
|
||||
from .domain import MixinType
|
||||
|
||||
|
||||
class ReleaseMixin(DevopsBuild):
|
||||
def __init__(self, project: Project, release: Release):
|
||||
super().__init__(project, devops=release.devops)
|
||||
self.repo.set_release(self.project, release)
|
||||
self.main_branch = release.main_branch
|
||||
|
||||
git_api = GitApi()
|
||||
environment_api = EnvironmentApi()
|
||||
env_key = EnvironmentKeys.DDADEVOPS_RELEASE_TYPE.name
|
||||
environment_val_set = environment_api.get(env_key) != "" and environment_api.get(env_key) is not None
|
||||
|
||||
if environment_val_set:
|
||||
release_type_repo = ReleaseTypeRepository.from_environment(environment_api)
|
||||
else:
|
||||
release_type_repo = ReleaseTypeRepository.from_git(git_api)
|
||||
|
||||
version_repo = VersionRepository(release.config_file)
|
||||
self.release_repo = ReleaseContextRepository(version_repo, release_type_repo)
|
||||
|
||||
self.prepare_release_service = PrepareReleaseService()
|
||||
self.tag_and_push_release_service = TagAndPushReleaseService(git_api, self.main_branch)
|
||||
def __init__(self, project: Project, input: dict):
|
||||
super().__init__(project, input)
|
||||
self.release_service = ReleaseService.prod(project.basedir)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
if MixinType.RELEASE not in devops.mixins:
|
||||
raise ValueError(f"ReleaseMixin requires MixinType.RELEASE")
|
||||
|
||||
def prepare_release(self):
|
||||
release = self.release_repo.get_release(self.main_branch)
|
||||
self.prepare_release_service.write_and_commit_release(release, self.release_repo.version_repository)
|
||||
self.prepare_release_service.write_and_commit_bump(release, self.release_repo.version_repository)
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
release = devops.mixins[MixinType.RELEASE]
|
||||
self.release_service.prepare_release(release)
|
||||
|
||||
def tag_and_push_release(self):
|
||||
self.tag_and_push_release_service.tag_release(self.release_repo)
|
||||
self.tag_and_push_release_service.push_release()
|
||||
def tag_bump_and_push_release(self):
|
||||
devops = self.devops_repo.get_devops(self.project)
|
||||
release = devops.mixins[MixinType.RELEASE]
|
||||
self.release_service.tag_bump_and_push_release(release)
|
||||
|
|
117
src/test/python/domain/helper.py
Normal file
117
src/test/python/domain/helper.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import DevopsFactory, Devops, Version, BuildFile
|
||||
|
||||
|
||||
def devops_config(overrides: dict) -> dict:
|
||||
default = {
|
||||
"name": "name",
|
||||
"module": "module",
|
||||
"stage": "test",
|
||||
"project_root_path": "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_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": {},
|
||||
"release_type": "NONE",
|
||||
"release_main_branch": "main",
|
||||
"release_current_branch": "my_feature",
|
||||
"release_primary_build_file": "./package.json",
|
||||
"release_secondary_build_file": [],
|
||||
"credentials_mappings": [
|
||||
{
|
||||
"gopass_path": "a/path",
|
||||
"gopass_field": "a-field",
|
||||
},
|
||||
],
|
||||
}
|
||||
input = default.copy()
|
||||
input.update(overrides)
|
||||
return input
|
||||
|
||||
|
||||
def build_devops(
|
||||
overrides: dict, version: Version = Version.from_str("1.0.0-SNAPSHOT")
|
||||
) -> Devops:
|
||||
return DevopsFactory().build_devops(devops_config(overrides), version=version)
|
||||
|
||||
|
||||
class BuildFileRepositoryMock:
|
||||
def get(self, path: Path) -> BuildFile:
|
||||
return BuildFile(
|
||||
Path("./package.json"),
|
||||
"""
|
||||
{
|
||||
"version": "1.1.5-SNAPSHOT"
|
||||
}
|
||||
""",
|
||||
)
|
||||
|
||||
def write(self, build_file: BuildFile):
|
||||
pass
|
||||
|
||||
|
||||
class EnvironmentApiMock:
|
||||
def __init__(self, mappings):
|
||||
self.mappings = mappings
|
||||
|
||||
def get(self, key):
|
||||
return self.mappings.get(key, None)
|
||||
|
||||
|
||||
class CredentialsApiMock:
|
||||
def __init__(self, mappings):
|
||||
self.mappings = mappings
|
||||
|
||||
def gopass_field_from_path(self, path, field):
|
||||
return self.mappings.get(f"{path}:{field}", None)
|
||||
|
||||
def gopass_password_from_path(self, path):
|
||||
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
|
151
src/test/python/domain/test_build_file.py
Normal file
151
src/test/python/domain/test_build_file.py
Normal file
|
@ -0,0 +1,151 @@
|
|||
import pytest
|
||||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import (
|
||||
BuildFileType,
|
||||
BuildFile,
|
||||
Version,
|
||||
)
|
||||
|
||||
|
||||
def test_sould_validate_build_file():
|
||||
sut = BuildFile(Path("./project.clj"), "content")
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = BuildFile(None, "")
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = BuildFile(Path("./unknown.extension"), "content")
|
||||
assert not sut.is_valid()
|
||||
|
||||
|
||||
def test_sould_calculate_build_type():
|
||||
sut = BuildFile(Path("./project.clj"), "content")
|
||||
assert sut.build_file_type() == BuildFileType.JAVA_CLOJURE
|
||||
|
||||
sut = BuildFile(Path("./build.gradle"), "content")
|
||||
assert sut.build_file_type() == BuildFileType.JAVA_GRADLE
|
||||
|
||||
sut = BuildFile(Path("./package.json"), "content")
|
||||
assert sut.build_file_type() == BuildFileType.JS
|
||||
|
||||
|
||||
def test_sould_parse_and_set_js():
|
||||
sut = BuildFile(
|
||||
Path("./package.json"),
|
||||
"""
|
||||
{
|
||||
"name":"c4k-jira",
|
||||
"description": "Generate c4k yaml for a jira deployment.",
|
||||
"author": "meissa GmbH",
|
||||
"version": "1.1.5-SNAPSHOT",
|
||||
"homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-jira#readme",
|
||||
"bin":{
|
||||
"c4k-jira": "./c4k-jira.js"
|
||||
}
|
||||
}
|
||||
""",
|
||||
)
|
||||
assert sut.get_version() == Version.from_str("1.1.5-SNAPSHOT")
|
||||
|
||||
sut = BuildFile(
|
||||
Path("./package.json"),
|
||||
"""
|
||||
{
|
||||
"name":"c4k-jira",
|
||||
}
|
||||
""",
|
||||
)
|
||||
with pytest.raises(Exception):
|
||||
sut.get_version()
|
||||
|
||||
sut = BuildFile(
|
||||
Path("./package.json"),
|
||||
"""
|
||||
{
|
||||
"name":"c4k-jira",
|
||||
"version": "1.1.5-SNAPSHOT"
|
||||
}
|
||||
""",
|
||||
)
|
||||
sut.set_version(Version.from_str("1.1.5-SNAPSHOT").create_major())
|
||||
assert """{
|
||||
"name": "c4k-jira",
|
||||
"version": "2.0.0"
|
||||
}""" == sut.content
|
||||
|
||||
|
||||
def test_sould_parse_and_set_version_for_gradle():
|
||||
sut = BuildFile(
|
||||
Path("./build.gradle"),
|
||||
"""
|
||||
version = "1.1.5-SNAPSHOT"
|
||||
|
||||
""",
|
||||
)
|
||||
assert sut.get_version() == Version.from_str("1.1.5-SNAPSHOT")
|
||||
|
||||
sut = BuildFile(
|
||||
Path("./build.gradle"),
|
||||
"""
|
||||
version = "1.1.5-SNAPSHOT"
|
||||
""",
|
||||
)
|
||||
sut.set_version(Version.from_str("1.1.5-SNAPSHOT").create_major())
|
||||
assert '\nversion = "2.0.0"\n' == sut.content
|
||||
|
||||
def test_sould_parse_and_set_version_for_py():
|
||||
sut = BuildFile(
|
||||
Path("./build.py"),
|
||||
"""
|
||||
from pybuilder.core import init, use_plugin, Author
|
||||
use_plugin("python.core")
|
||||
|
||||
name = "ddadevops"
|
||||
version = "1.1.5-dev"
|
||||
""",
|
||||
)
|
||||
assert sut.get_version() == Version.from_str("1.1.5-dev")
|
||||
|
||||
sut = BuildFile(
|
||||
Path("./build.py"),
|
||||
"""
|
||||
version = "1.1.5-dev1"
|
||||
""",
|
||||
)
|
||||
sut.set_version(Version.from_str("1.1.5-dev1").create_major())
|
||||
assert '\nversion = "2.0.0"\n' == sut.content
|
||||
|
||||
|
||||
def test_sould_parse_and_set_version_for_clj():
|
||||
sut = BuildFile(
|
||||
Path("./project.clj"),
|
||||
"""
|
||||
(defproject org.domaindrivenarchitecture/c4k-jira "1.1.5-SNAPSHOT"
|
||||
:description "jira c4k-installation package"
|
||||
:url "https://domaindrivenarchitecture.org"
|
||||
)
|
||||
""",
|
||||
)
|
||||
assert sut.get_version() == Version.from_str("1.1.5-SNAPSHOT")
|
||||
|
||||
sut = BuildFile(
|
||||
Path("./project.clj"),
|
||||
"""
|
||||
(defproject org.domaindrivenarchitecture/c4k-jira "1.1.5-SNAPSHOT"
|
||||
:description "jira c4k-installation package"
|
||||
)
|
||||
""",
|
||||
)
|
||||
sut.set_version(Version.from_str("1.1.5-SNAPSHOT").create_major())
|
||||
assert '\n(defproject org.domaindrivenarchitecture/c4k-jira "2.0.0"\n :description "jira c4k-installation package"\n)\n' == sut.content
|
||||
|
||||
sut = BuildFile(
|
||||
Path("./project.clj"),
|
||||
"""
|
||||
(defproject org.domaindrivenarchitecture/c4k-jira "1.1.5-SNAPSHOT"
|
||||
:dependencies [[org.clojure/clojure "1.11.0"]]
|
||||
)
|
||||
""",
|
||||
)
|
||||
sut.set_version(Version.from_str("1.1.5-SNAPSHOT").create_major())
|
||||
assert '\n(defproject org.domaindrivenarchitecture/c4k-jira "2.0.0"\n:dependencies [[org.clojure/clojure "1.11.0"]]\n)\n ' == sut.content
|
99
src/test/python/domain/test_c4k.py
Normal file
99
src/test/python/domain/test_c4k.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
import pytest
|
||||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import (
|
||||
DnsRecord,
|
||||
BuildType,
|
||||
C4k
|
||||
)
|
||||
from .helper import build_devops
|
||||
|
||||
|
||||
def test_creation():
|
||||
sut = build_devops({})
|
||||
assert BuildType.C4K in sut.specialized_builds
|
||||
|
||||
|
||||
def test_c4k_should_calculate_config():
|
||||
sut = build_devops({})
|
||||
with pytest.raises(Exception):
|
||||
sut.specialized_builds[BuildType.C4K].config()
|
||||
|
||||
sut = build_devops({})
|
||||
c4k = sut.specialized_builds[BuildType.C4K]
|
||||
c4k.update_runtime_config(DnsRecord("fqdn"))
|
||||
assert {
|
||||
"fqdn": "fqdn",
|
||||
"mon-cfg": {
|
||||
"cluster-name": "module",
|
||||
"cluster-stage": "test",
|
||||
"grafana-cloud-url": "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push",
|
||||
},
|
||||
} == c4k.config()
|
||||
|
||||
sut = build_devops(
|
||||
{
|
||||
"c4k_config": {"test": "test"},
|
||||
}
|
||||
)
|
||||
c4k = sut.specialized_builds[BuildType.C4K]
|
||||
c4k.update_runtime_config(DnsRecord("fqdn"))
|
||||
assert {
|
||||
"test": "test",
|
||||
"fqdn": "fqdn",
|
||||
"mon-cfg": {
|
||||
"cluster-name": "module",
|
||||
"cluster-stage": "test",
|
||||
"grafana-cloud-url": "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push",
|
||||
},
|
||||
} == c4k.config()
|
||||
|
||||
|
||||
def test_c4k_should_calculate_auth():
|
||||
sut = build_devops({})
|
||||
c4k = sut.specialized_builds[BuildType.C4K]
|
||||
assert {
|
||||
"mon-auth": {"grafana-cloud-password": "password", "grafana-cloud-user": "user"}
|
||||
} == c4k.auth()
|
||||
|
||||
sut = build_devops(
|
||||
{
|
||||
"c4k_auth": {"test": "test"},
|
||||
}
|
||||
)
|
||||
c4k = sut.specialized_builds[BuildType.C4K]
|
||||
assert {
|
||||
"test": "test",
|
||||
"mon-auth": {
|
||||
"grafana-cloud-password": "password",
|
||||
"grafana-cloud-user": "user",
|
||||
},
|
||||
} == c4k.auth()
|
||||
|
||||
|
||||
def test_c4k_build_should_calculate_command():
|
||||
sut = build_devops(
|
||||
{
|
||||
"project_root_path": ".",
|
||||
}
|
||||
)
|
||||
assert (
|
||||
"c4k-module-standalone.jar "
|
||||
+ "./target/name/module/out_c4k_config.yaml "
|
||||
+ "./target/name/module/out_c4k_auth.yaml > "
|
||||
+ "./target/name/module/out_module.yaml"
|
||||
== sut.specialized_builds[BuildType.C4K].command(sut)
|
||||
)
|
||||
|
||||
sut = build_devops(
|
||||
{
|
||||
"project_root_path": ".",
|
||||
"c4k_executable_name": "executable_name",
|
||||
}
|
||||
)
|
||||
assert (
|
||||
"c4k-executable_name-standalone.jar "
|
||||
+ "./target/name/module/out_c4k_config.yaml "
|
||||
+ "./target/name/module/out_c4k_auth.yaml > "
|
||||
+ "./target/name/module/out_module.yaml"
|
||||
== sut.specialized_builds[BuildType.C4K].command(sut)
|
||||
)
|
65
src/test/python/domain/test_common.py
Normal file
65
src/test/python/domain/test_common.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
from pybuilder.core import Project
|
||||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import (
|
||||
Validateable,
|
||||
DnsRecord,
|
||||
Devops,
|
||||
BuildType,
|
||||
Version,
|
||||
ReleaseType,
|
||||
Release,
|
||||
)
|
||||
from src.main.python.ddadevops.domain.image import Image
|
||||
from .helper import build_devops
|
||||
|
||||
|
||||
class MockValidateable(Validateable):
|
||||
def __init__(self, value):
|
||||
self.field = value
|
||||
|
||||
def validate(self):
|
||||
return self.__validate_is_not_empty__("field")
|
||||
|
||||
|
||||
def test_should_validate_non_empty_strings():
|
||||
sut = MockValidateable("content")
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(None)
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = MockValidateable("")
|
||||
assert not sut.is_valid()
|
||||
|
||||
|
||||
def test_should_validate_non_empty_others():
|
||||
sut = MockValidateable(1)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(1.0)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(True)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(None)
|
||||
assert not sut.is_valid()
|
||||
|
||||
|
||||
def test_validate_with_reason():
|
||||
sut = MockValidateable(None)
|
||||
assert sut.validate()[0] == "Field 'field' must not be None."
|
||||
|
||||
|
||||
def test_should_validate_DnsRecord():
|
||||
sut = DnsRecord(None)
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = DnsRecord("name")
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = DnsRecord("name", ipv4="1.2.3.4")
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = DnsRecord("name", ipv6="1::")
|
||||
assert sut.is_valid()
|
161
src/test/python/domain/test_crededntials.py
Normal file
161
src/test/python/domain/test_crededntials.py
Normal file
|
@ -0,0 +1,161 @@
|
|||
import pytest
|
||||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import (
|
||||
CredentialMapping,
|
||||
Credentials,
|
||||
GopassType,
|
||||
MixinType,
|
||||
)
|
||||
from .helper import build_devops
|
||||
|
||||
|
||||
def test_should_create_mapping():
|
||||
sut = CredentialMapping(
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
}
|
||||
)
|
||||
assert "grafana_cloud_user" == sut.name_for_input()
|
||||
assert "GRAFANA_CLOUD_USER" == sut.name_for_environment()
|
||||
assert GopassType.FIELD == sut.gopass_type()
|
||||
|
||||
sut = CredentialMapping(
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"name": "grafana_cloud_password",
|
||||
}
|
||||
)
|
||||
assert "grafana_cloud_password" == sut.name_for_input()
|
||||
assert "GRAFANA_CLOUD_PASSWORD" == sut.name_for_environment()
|
||||
assert GopassType.PASSWORD == sut.gopass_type()
|
||||
|
||||
sut = CredentialMapping(
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
"name": "gfc_user",
|
||||
}
|
||||
)
|
||||
assert "gfc_user" == sut.name_for_input()
|
||||
assert "GFC_USER" == sut.name_for_environment()
|
||||
assert GopassType.FIELD == sut.gopass_type()
|
||||
|
||||
|
||||
def test_should_validate_CredentialMapping():
|
||||
sut = CredentialMapping(
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
}
|
||||
)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = CredentialMapping(
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"name": "grafana_cloud_user",
|
||||
}
|
||||
)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = CredentialMapping(
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
}
|
||||
)
|
||||
assert not sut.is_valid()
|
||||
|
||||
|
||||
def test_should_create_credentials():
|
||||
sut = Credentials(
|
||||
{
|
||||
"credentials_mapping": [
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
},
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"name": "grafana_cloud_password",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
assert sut
|
||||
assert 2 == len(sut.mappings)
|
||||
|
||||
sut = Credentials(
|
||||
{},
|
||||
default_mappings=[
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
},
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"name": "grafana_cloud_password",
|
||||
},
|
||||
],
|
||||
)
|
||||
assert sut
|
||||
assert 2 == len(sut.mappings)
|
||||
|
||||
sut = Credentials(
|
||||
{
|
||||
"credentials_mapping": [
|
||||
{
|
||||
"gopass_path": "dome/path",
|
||||
"gopass_field": "some-field",
|
||||
},
|
||||
{
|
||||
"gopass_path": "another_path",
|
||||
"name": "grafana_cloud_password",
|
||||
},
|
||||
],
|
||||
},
|
||||
default_mappings=[
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
},
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"name": "grafana_cloud_password",
|
||||
},
|
||||
],
|
||||
)
|
||||
assert sut
|
||||
assert 3 == len(sut.mappings)
|
||||
assert sut.mappings["grafana_cloud_password"].gopass_path == "another_path"
|
||||
|
||||
|
||||
def test_should_validate_credentials():
|
||||
sut = Credentials(
|
||||
{
|
||||
"credentials_mapping": [
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
},
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"name": "grafana_cloud_password",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = Credentials(
|
||||
{
|
||||
"credentials_mapping": [
|
||||
{
|
||||
"gopass_path": "server/meissa/grafana-cloud",
|
||||
"gopass_field": "grafana-cloud-user",
|
||||
},
|
||||
{"gopass_path": "server/meissa/grafana-cloud"},
|
||||
],
|
||||
}
|
||||
)
|
||||
assert not sut.is_valid()
|
9
src/test/python/domain/test_devops.py
Normal file
9
src/test/python/domain/test_devops.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
import pytest
|
||||
from src.main.python.ddadevops.domain import (
|
||||
Devops,
|
||||
)
|
||||
from .helper import build_devops
|
||||
|
||||
def test_devops_buildpath():
|
||||
sut = build_devops({'module': "cloud", 'name': "meissa"})
|
||||
assert "root_path/target/meissa/cloud" == sut.build_path()
|
63
src/test/python/domain/test_devops_factory.py
Normal file
63
src/test/python/domain/test_devops_factory.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
import pytest
|
||||
from src.main.python.ddadevops.domain import (
|
||||
DevopsFactory, Version
|
||||
)
|
||||
|
||||
|
||||
def test_devops_factory():
|
||||
with pytest.raises(Exception):
|
||||
DevopsFactory().build_devops({"build_types": ["NOTEXISTING"]})
|
||||
|
||||
with pytest.raises(Exception):
|
||||
DevopsFactory().build_devops(
|
||||
{
|
||||
"build_types": ["IMAGE"],
|
||||
}
|
||||
)
|
||||
|
||||
sut = DevopsFactory().build_devops(
|
||||
{
|
||||
"stage": "test",
|
||||
"name": "mybuild",
|
||||
"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 is not None
|
||||
|
||||
sut = DevopsFactory().build_devops(
|
||||
{
|
||||
"stage": "test",
|
||||
"name": "mybuild",
|
||||
"module": "test_image",
|
||||
"project_root_path": "../../..",
|
||||
"build_types": ["C4K"],
|
||||
"mixin_types": [],
|
||||
"c4k_grafana_cloud_user": "user",
|
||||
"c4k_grafana_cloud_password": "password",
|
||||
},
|
||||
Version.from_str("1.0.0")
|
||||
)
|
||||
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_type": "NONE",
|
||||
"release_main_branch": "main",
|
||||
"release_current_branch": "my_feature",
|
||||
"release_config_file": "project.clj",
|
||||
},
|
||||
Version.from_str("1.0.0")
|
||||
)
|
||||
assert sut is not None
|
|
@ -1,226 +0,0 @@
|
|||
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
|
||||
|
||||
|
||||
class MockValidateable(Validateable):
|
||||
def __init__(self, value):
|
||||
self.field = value
|
||||
|
||||
def validate(self):
|
||||
return self.__validate_is_not_empty__("field")
|
||||
|
||||
|
||||
def test_should_validate_non_empty_strings():
|
||||
sut = MockValidateable("content")
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(None)
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = MockValidateable("")
|
||||
assert not sut.is_valid()
|
||||
|
||||
|
||||
def test_should_validate_non_empty_others():
|
||||
sut = MockValidateable(1)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(1.0)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(True)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = MockValidateable(None)
|
||||
assert not sut.is_valid()
|
||||
|
||||
|
||||
def test_validate_with_reason():
|
||||
sut = MockValidateable(None)
|
||||
assert sut.validate()[0] == "Field 'field' must not be empty."
|
||||
|
||||
|
||||
def test_should_validate_DnsRecord():
|
||||
sut = DnsRecord(None)
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = DnsRecord("name")
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = DnsRecord("name", ipv4="1.2.3.4")
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = DnsRecord("name", ipv6="1::")
|
||||
assert sut.is_valid()
|
||||
|
||||
|
||||
def test_devops_buildpath():
|
||||
sut = Devops(
|
||||
stage="test", project_root_path="../../..", module="cloud", name="meissa"
|
||||
)
|
||||
assert "../../../target/meissa/cloud" == sut.build_path()
|
||||
|
||||
|
||||
def test_devops_build_commons_path():
|
||||
devops = Devops(
|
||||
stage="test", project_root_path="../../..", module="cloud", name="meissa"
|
||||
)
|
||||
sut = Image(
|
||||
dockerhub_user="user",
|
||||
dockerhub_password="password",
|
||||
devops = devops,
|
||||
)
|
||||
assert "docker/" == sut.docker_build_commons_path()
|
||||
|
||||
|
||||
def test_c4k_build_should_update_fqdn():
|
||||
project_config = {
|
||||
"stage": "test",
|
||||
"name": "name",
|
||||
"project_root_path": "mypath",
|
||||
"module": "module",
|
||||
"build_dir_name": "target",
|
||||
}
|
||||
config = {"issuer": "staging"}
|
||||
auth = {
|
||||
"jvb-auth-password": "pw1",
|
||||
"jicofo-auth-password": "pw2",
|
||||
"jicofo-component-secret": "pw3",
|
||||
}
|
||||
add_c4k_mixin_config(
|
||||
project_config,
|
||||
config,
|
||||
auth,
|
||||
grafana_cloud_user="user",
|
||||
grafana_cloud_password="password",
|
||||
)
|
||||
|
||||
sut = C4k(project_config)
|
||||
sut.update_runtime_config(DnsRecord("test.de", ipv6="1::"))
|
||||
|
||||
assert {
|
||||
"issuer": "staging",
|
||||
"fqdn": "test.de",
|
||||
"mon-cfg": {
|
||||
"cluster-name": "module",
|
||||
"cluster-stage": "test",
|
||||
"grafana-cloud-url": "https://prometheus-prod-01-eu-west-0.grafana.net/api/prom/push",
|
||||
},
|
||||
} == sut.config()
|
||||
assert {
|
||||
"jicofo-auth-password": "pw2",
|
||||
"jicofo-component-secret": "pw3",
|
||||
"jvb-auth-password": "pw1",
|
||||
"mon-auth": {
|
||||
"grafana-cloud-password": "password",
|
||||
"grafana-cloud-user": "user",
|
||||
},
|
||||
} == sut.c4k_mixin_auth
|
||||
|
||||
|
||||
def test_c4k_build_should_calculate_command():
|
||||
devops = Devops(stage="test", project_root_path="", module="module", name="name")
|
||||
project_config = {
|
||||
"stage": "test",
|
||||
"name": "name",
|
||||
"project_root_path": "",
|
||||
"module": "module",
|
||||
"build_dir_name": "target",
|
||||
}
|
||||
add_c4k_mixin_config(
|
||||
project_config,
|
||||
{},
|
||||
{},
|
||||
grafana_cloud_user="user",
|
||||
grafana_cloud_password="password",
|
||||
)
|
||||
sut = C4k(project_config)
|
||||
assert (
|
||||
"c4k-module-standalone.jar "
|
||||
+ "/target/name/module/out_c4k_config.yaml "
|
||||
+ "/target/name/module/out_c4k_auth.yaml > "
|
||||
+ "/target/name/module/out_module.yaml"
|
||||
== sut.command(devops)
|
||||
)
|
||||
|
||||
project_config = {
|
||||
"stage": "test",
|
||||
"name": "name",
|
||||
"project_root_path": "",
|
||||
"module": "module",
|
||||
"build_dir_name": "target",
|
||||
}
|
||||
add_c4k_mixin_config(
|
||||
project_config,
|
||||
{},
|
||||
{},
|
||||
executabel_name="executabel_name",
|
||||
grafana_cloud_user="user",
|
||||
grafana_cloud_password="password",
|
||||
)
|
||||
sut = C4k(project_config)
|
||||
assert (
|
||||
"c4k-executabel_name-standalone.jar "
|
||||
+ "/target/name/module/out_c4k_config.yaml "
|
||||
+ "/target/name/module/out_c4k_auth.yaml > "
|
||||
+ "/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()
|
14
src/test/python/domain/test_image.py
Normal file
14
src/test/python/domain/test_image.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from pybuilder.core import Project
|
||||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import (
|
||||
BuildType,
|
||||
)
|
||||
from .helper import build_devops
|
||||
|
||||
|
||||
def test_devops_build_commons_path():
|
||||
sut = build_devops({})
|
||||
image = sut.specialized_builds[BuildType.IMAGE]
|
||||
assert image is not None
|
||||
assert image.is_valid()
|
||||
assert "docker/" == image.build_commons_path()
|
58
src/test/python/domain/test_init_service.py
Normal file
58
src/test/python/domain/test_init_service.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
import pytest
|
||||
from src.main.python.ddadevops.domain import (
|
||||
InitService,
|
||||
DevopsFactory,
|
||||
Version,
|
||||
MixinType,
|
||||
BuildType,
|
||||
)
|
||||
from .helper import (
|
||||
BuildFileRepositoryMock,
|
||||
EnvironmentApiMock,
|
||||
CredentialsApiMock,
|
||||
GitApiMock,
|
||||
devops_config,
|
||||
)
|
||||
|
||||
|
||||
def test_should_load_build_file():
|
||||
sut = InitService(
|
||||
DevopsFactory(),
|
||||
BuildFileRepositoryMock(),
|
||||
CredentialsApiMock({
|
||||
"server/meissa/grafana-cloud:grafana-cloud-user": "gopass-gfc-user",
|
||||
"server/meissa/grafana-cloud": "gopass-gfc-password",
|
||||
}),
|
||||
EnvironmentApiMock({}),
|
||||
GitApiMock(),
|
||||
)
|
||||
assert (
|
||||
Version.from_str("1.1.5-SNAPSHOT")
|
||||
== sut.initialize(devops_config({})).mixins[MixinType.RELEASE].version
|
||||
)
|
||||
|
||||
|
||||
def test_should_resolve_passwords():
|
||||
sut = InitService(
|
||||
DevopsFactory(),
|
||||
BuildFileRepositoryMock(),
|
||||
CredentialsApiMock(
|
||||
{
|
||||
"server/meissa/grafana-cloud:grafana-cloud-user": "gopass-gfc-user",
|
||||
"server/meissa/grafana-cloud": "gopass-gfc-password",
|
||||
}
|
||||
),
|
||||
EnvironmentApiMock({"C4K_GRAFANA_CLOUD_USER": "env-gfc-user"}),
|
||||
GitApiMock(),
|
||||
)
|
||||
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]
|
||||
assert {
|
||||
"mon-auth": {
|
||||
"grafana-cloud-password": "gopass-gfc-password",
|
||||
"grafana-cloud-user": "env-gfc-user",
|
||||
}
|
||||
} == c4k.auth()
|
63
src/test/python/domain/test_release.py
Normal file
63
src/test/python/domain/test_release.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
from pybuilder.core import Project
|
||||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import (
|
||||
Validateable,
|
||||
DnsRecord,
|
||||
Devops,
|
||||
BuildType,
|
||||
MixinType,
|
||||
Version,
|
||||
ReleaseType,
|
||||
Release,
|
||||
Image,
|
||||
)
|
||||
from .helper import build_devops, devops_config
|
||||
|
||||
|
||||
def test_sould_validate_release():
|
||||
sut = Release(
|
||||
devops_config(
|
||||
{
|
||||
"release_type": "MINOR",
|
||||
"release_current_branch": "main",
|
||||
}
|
||||
),
|
||||
Version.from_str("1.3.1-SNAPSHOT"),
|
||||
)
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = Release(
|
||||
devops_config(
|
||||
{
|
||||
"release_type": "MINOR",
|
||||
"release_current_branch": "some-feature-branch",
|
||||
}
|
||||
),
|
||||
Version.from_str("1.3.1-SNAPSHOT"),
|
||||
)
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = Release(
|
||||
devops_config(
|
||||
{
|
||||
"release_primary_build_file": 1,
|
||||
}
|
||||
),
|
||||
Version.from_str("1.3.1-SNAPSHOT"),
|
||||
)
|
||||
assert not sut.is_valid()
|
||||
|
||||
|
||||
def test_sould_validate_release():
|
||||
sut = Release(
|
||||
devops_config(
|
||||
{
|
||||
"release_type": "MINOR",
|
||||
"release_current_branch": "main",
|
||||
"release_primary_build_file": "project.clj",
|
||||
"release_secondary_build_files": ["package.json"],
|
||||
}
|
||||
),
|
||||
Version.from_str("1.3.1-SNAPSHOT"),
|
||||
)
|
||||
assert ["project.clj", "package.json"] == sut.build_files()
|
114
src/test/python/domain/test_version.py
Normal file
114
src/test/python/domain/test_version.py
Normal file
|
@ -0,0 +1,114 @@
|
|||
from pybuilder.core import Project
|
||||
from pathlib import Path
|
||||
from src.main.python.ddadevops.domain import (
|
||||
Version,
|
||||
ReleaseType,
|
||||
Image,
|
||||
)
|
||||
from .helper import build_devops, devops_config
|
||||
|
||||
|
||||
def test_version_creation():
|
||||
sut = Version.from_str("1.2.3")
|
||||
assert sut.to_string() == "1.2.3"
|
||||
assert sut.version_list == [1, 2, 3]
|
||||
assert sut.is_snapshot() == False
|
||||
|
||||
sut = Version.from_str("1.2.3-SNAPSHOT")
|
||||
assert sut.to_string() == "1.2.3-SNAPSHOT"
|
||||
assert sut.version_list == [1, 2, 3]
|
||||
assert sut.is_snapshot() == True
|
||||
|
||||
|
||||
def test_should_validate_version_list():
|
||||
sut = Version(None)
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = Version([])
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = Version([1, 2])
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = Version([1, 2, 3])
|
||||
assert sut.is_valid()
|
||||
|
||||
|
||||
def test_should_validate_parsing():
|
||||
sut = Version.from_str("1.2")
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = Version.from_str("1.2.3")
|
||||
sut.version_list = [2, 2, 2]
|
||||
assert not sut.is_valid()
|
||||
|
||||
sut = Version.from_str("1.2.3")
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = Version.from_str("1.2.3-SNAPSHOT")
|
||||
assert sut.is_valid()
|
||||
|
||||
sut = Version.from_str("1.2.3-dev")
|
||||
assert sut.is_valid()
|
||||
|
||||
|
||||
def test_should_create_patch():
|
||||
version = Version.from_str("1.2.3-SNAPSHOT")
|
||||
sut = version.create_patch()
|
||||
assert sut.to_string() == "1.2.3"
|
||||
assert version.to_string() == "1.2.3-SNAPSHOT"
|
||||
|
||||
version = Version.from_str("1.2.3")
|
||||
sut = version.create_patch()
|
||||
assert sut.to_string() == "1.2.4"
|
||||
assert version.to_string() == "1.2.3"
|
||||
|
||||
|
||||
def test_should_create_minor():
|
||||
version = Version.from_str("1.2.3-SNAPSHOT")
|
||||
sut = version.create_minor()
|
||||
assert sut.to_string() == "1.3.0"
|
||||
|
||||
version = Version.from_str("1.2.3")
|
||||
sut = version.create_minor()
|
||||
assert sut.to_string() == "1.3.0"
|
||||
|
||||
version = Version.from_str("1.3.0-SNAPSHOT")
|
||||
sut = version.create_minor()
|
||||
assert sut.to_string() == "1.3.0"
|
||||
|
||||
version = Version.from_str("1.3.0")
|
||||
sut = version.create_minor()
|
||||
assert sut.to_string() == "1.4.0"
|
||||
|
||||
|
||||
def test_should_create_major():
|
||||
version = Version.from_str("1.2.3-SNAPSHOT")
|
||||
sut = version.create_major()
|
||||
assert sut.to_string() == "2.0.0"
|
||||
|
||||
version = Version.from_str("1.2.3")
|
||||
sut = version.create_major()
|
||||
assert sut.to_string() == "2.0.0"
|
||||
|
||||
version = Version.from_str("1.0.0-SNAPSHOT")
|
||||
sut = version.create_major()
|
||||
assert sut.to_string() == "1.0.0"
|
||||
|
||||
version = Version.from_str("1.0.0")
|
||||
sut = version.create_major()
|
||||
assert sut.to_string() == "2.0.0"
|
||||
|
||||
|
||||
def test_should_create_bump():
|
||||
version = Version.from_str("1.2.3-SNAPSHOT")
|
||||
sut = version.create_bump()
|
||||
assert sut.to_string() == "1.2.3-SNAPSHOT"
|
||||
|
||||
version = Version.from_str("1.2.3")
|
||||
sut = version.create_bump("SNAPSHOT")
|
||||
assert sut.to_string() == "1.2.4-SNAPSHOT"
|
||||
|
||||
version = Version.from_str("1.0.0")
|
||||
sut = version.create_bump("SNAPSHOT")
|
||||
assert sut.to_string() == "1.0.1-SNAPSHOT"
|
|
@ -1,2 +0,0 @@
|
|||
from .mock_infrastructure import MockReleaseRepository, MockReleaseTypeRepository, MockVersionRepository
|
||||
from .mock_infrastructure_api import MockGitApi
|
|
@ -1,12 +0,0 @@
|
|||
from pathlib import Path
|
||||
from src.main.python.ddadevops.infrastructure import ExecutionApi
|
||||
|
||||
class Helper():
|
||||
def __init__(self, file_name = 'config.json'):
|
||||
self.TEST_FILE_NAME = file_name
|
||||
self.TEST_FILE_ROOT = Path('src/test/resources/')
|
||||
self.TEST_FILE_PATH = self.TEST_FILE_ROOT / self.TEST_FILE_NAME
|
||||
|
||||
def copy_files(self, source: Path, target: Path):
|
||||
api = ExecutionApi()
|
||||
api.execute(f"cp {source} {target}")
|
|
@ -1,42 +0,0 @@
|
|||
from pathlib import Path
|
||||
|
||||
from src.main.python.ddadevops.domain import ReleaseType, Version, ReleaseContext
|
||||
|
||||
from .mock_infrastructure_api import MockGitApi
|
||||
|
||||
class MockVersionRepository():
|
||||
|
||||
def __init__(self):
|
||||
self.file = None
|
||||
self.file_handler = None
|
||||
self.write_file_count = 0
|
||||
|
||||
def load_file(self):
|
||||
pass
|
||||
|
||||
def write_file(self, version_string):
|
||||
self.write_file_count += 1
|
||||
pass
|
||||
|
||||
def parse_file(self):
|
||||
pass
|
||||
|
||||
def get_version(self) -> Version:
|
||||
return Version(Path(), [0,0,0])
|
||||
|
||||
class MockReleaseTypeRepository():
|
||||
def __init__(self, mock_git_api: MockGitApi):
|
||||
self.git_api = mock_git_api
|
||||
|
||||
def get_release_type(self):
|
||||
return ReleaseType.MINOR
|
||||
|
||||
class MockReleaseRepository():
|
||||
def __init__(self, version_repository: MockVersionRepository, release_type_repository: MockReleaseTypeRepository):
|
||||
self.version_repository = version_repository
|
||||
self.release_type_repository = release_type_repository
|
||||
self.get_release_count = 0
|
||||
|
||||
def get_release(self, main_branch) -> ReleaseContext:
|
||||
self.get_release_count += 1
|
||||
return ReleaseContext(self.release_type_repository.get_release_type(), self.version_repository.get_version(), main_branch)
|
|
@ -1,73 +0,0 @@
|
|||
class MockSystemApi():
|
||||
|
||||
def __init__(self):
|
||||
self.stdout = [""]
|
||||
self.stderr = [""]
|
||||
|
||||
def run(self, args):
|
||||
pass
|
||||
|
||||
def run_checked(self, *args):
|
||||
self.run(args)
|
||||
pass
|
||||
|
||||
class MockGitApi():
|
||||
|
||||
def __init__(self, commit_string = ""):
|
||||
self.system_api = MockSystemApi()
|
||||
self.get_latest_commit_count = 0
|
||||
self.commit_string = commit_string
|
||||
self.tag_annotated_count = 0
|
||||
self.add_file_count = 0
|
||||
self.commit_count = 0
|
||||
self.push_count = 0
|
||||
|
||||
def get_latest_n_commits(self, n: int):
|
||||
return " "
|
||||
|
||||
def get_latest_commit(self):
|
||||
self.get_latest_commit_count += 1
|
||||
return self.commit_string
|
||||
|
||||
def tag_annotated(self, annotation: str, message: str, count: int):
|
||||
self.tag_annotated_count += 1
|
||||
return " "
|
||||
|
||||
def tag_annotated_second_last(self, annotation: str, message: str):
|
||||
self.tag_annotated(annotation, message, 1)
|
||||
return " "
|
||||
|
||||
def get_latest_tag(self):
|
||||
return " "
|
||||
|
||||
def get_current_branch(self):
|
||||
return " "
|
||||
|
||||
def init(self):
|
||||
pass
|
||||
|
||||
def add_file(self, file_path):
|
||||
self.add_file_count += 1
|
||||
return " "
|
||||
|
||||
def commit(self, commit_message: str):
|
||||
self.commit_count += 1
|
||||
return commit_message
|
||||
|
||||
def push(self):
|
||||
self.push_count += 1
|
||||
return " "
|
||||
|
||||
def checkout(self, branch: str):
|
||||
return " "
|
||||
|
||||
class MockEnvironmentApi():
|
||||
|
||||
def __init__(self, environ_map):
|
||||
self.environ = environ_map
|
||||
|
||||
def get(self, name):
|
||||
return self.environ.get(name)
|
||||
|
||||
def set(self, name, value):
|
||||
self.environ[name] = value
|
|
@ -1,86 +0,0 @@
|
|||
from src.main.python.ddadevops.domain import ReleaseType
|
||||
from src.main.python.ddadevops.infrastructure.release_mixin import ReleaseTypeRepository, VersionRepository, ReleaseContextRepository
|
||||
from .mock_infrastructure_api import MockGitApi, MockEnvironmentApi
|
||||
from .helper import Helper
|
||||
|
||||
def test_version_repository(tmp_path):
|
||||
# init
|
||||
th = Helper()
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
sut = VersionRepository(th.TEST_FILE_PATH)
|
||||
version = sut.get_version()
|
||||
|
||||
#test
|
||||
assert version is not None
|
||||
|
||||
|
||||
def test_release_repository(tmp_path):
|
||||
# init
|
||||
th = Helper()
|
||||
th.copy_files( th.TEST_FILE_PATH, tmp_path)
|
||||
version_repo = VersionRepository(th.TEST_FILE_PATH)
|
||||
release_type_repo = ReleaseTypeRepository.from_git(MockGitApi('MINOR test'))
|
||||
|
||||
# test
|
||||
sut = ReleaseContextRepository(version_repo, release_type_repo)
|
||||
|
||||
release = sut.get_release('main')
|
||||
|
||||
assert release is not None
|
||||
|
||||
|
||||
def test_release_type_repository_git():
|
||||
sut = ReleaseTypeRepository.from_git(MockGitApi('MINOR test'))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type is ReleaseType.MINOR
|
||||
|
||||
sut = ReleaseTypeRepository.from_git(MockGitApi('MINOR bla'))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type is ReleaseType.MINOR
|
||||
|
||||
sut = ReleaseTypeRepository.from_git(MockGitApi('Major bla'))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == ReleaseType.MAJOR
|
||||
|
||||
sut = ReleaseTypeRepository.from_git(MockGitApi('PATCH bla'))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == ReleaseType.PATCH
|
||||
|
||||
sut = ReleaseTypeRepository.from_git(MockGitApi('SNAPSHOT bla'))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == ReleaseType.SNAPSHOT
|
||||
|
||||
sut = ReleaseTypeRepository.from_git(MockGitApi('bla'))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == None
|
||||
|
||||
def test_release_type_repository_env():
|
||||
sut = ReleaseTypeRepository.from_environment(MockEnvironmentApi({'DDADEVOPS_RELEASE_TYPE': 'MINOR test'}))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type is ReleaseType.MINOR
|
||||
|
||||
sut = ReleaseTypeRepository.from_environment(MockEnvironmentApi({'DDADEVOPS_RELEASE_TYPE': 'MINOR'}))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type is ReleaseType.MINOR
|
||||
|
||||
sut = ReleaseTypeRepository.from_environment(MockEnvironmentApi({'DDADEVOPS_RELEASE_TYPE': 'Major bla'}))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == ReleaseType.MAJOR
|
||||
|
||||
sut = ReleaseTypeRepository.from_environment(MockEnvironmentApi({'DDADEVOPS_RELEASE_TYPE': 'Patch bla'}))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == ReleaseType.PATCH
|
||||
|
||||
sut = ReleaseTypeRepository.from_environment(MockEnvironmentApi({'DDADEVOPS_RELEASE_TYPE': 'Snapshot bla'}))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == ReleaseType.SNAPSHOT
|
||||
|
||||
sut = ReleaseTypeRepository.from_environment(MockEnvironmentApi({'DDADEVOPS_RELEASE_TYPE': 'Random text'}))
|
||||
release_type = sut.get_release_type()
|
||||
assert release_type == None
|
||||
|
||||
sut = ReleaseTypeRepository.from_environment(MockEnvironmentApi({'REL_TYPE': 'Not the right variable'}))
|
||||
try:
|
||||
release_type = sut.get_release_type()
|
||||
except:
|
||||
assert release_type == None
|
|
@ -1,103 +0,0 @@
|
|||
from pathlib import Path
|
||||
import pytest as pt
|
||||
|
||||
from src.main.python.ddadevops.infrastructure.release_mixin import GitApi, EnvironmentApi, JsonFileHandler
|
||||
from src.main.python.ddadevops.infrastructure.release_mixin import VersionRepository
|
||||
from src.main.python.ddadevops.domain.release import ReleaseType
|
||||
|
||||
from .helper import Helper
|
||||
|
||||
def change_test_dir( tmp_path: Path, monkeypatch: pt.MonkeyPatch):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
|
||||
def test_environment_api():
|
||||
# init
|
||||
env_api = EnvironmentApi()
|
||||
key = "TEST_ENV_KEY"
|
||||
value = "data"
|
||||
env_api.set(key, value)
|
||||
|
||||
#test
|
||||
assert env_api.get(key) == value
|
||||
|
||||
def test_git_api(tmp_path: Path, monkeypatch: pt.MonkeyPatch):
|
||||
# init
|
||||
th = Helper()
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
|
||||
# change the context of the script execution to tmp_path
|
||||
change_test_dir(tmp_path, monkeypatch)
|
||||
|
||||
git_api = GitApi()
|
||||
git_api.init()
|
||||
git_api.set_user_config("ex.ample@mail.com", "Ex Ample")
|
||||
git_api.add_file(th.TEST_FILE_NAME)
|
||||
git_api.commit("MINOR release")
|
||||
|
||||
# test
|
||||
latest_commit = git_api.get_latest_commit()
|
||||
assert "MINOR release" in latest_commit
|
||||
|
||||
# file handler tests
|
||||
def test_gradle(tmp_path):
|
||||
# init
|
||||
th = Helper('config.gradle')
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
# test
|
||||
repo = VersionRepository(th.TEST_FILE_PATH)
|
||||
version = repo.get_version()
|
||||
version = version.create_release_version(ReleaseType.SNAPSHOT)
|
||||
repo.write_file(version.get_version_string())
|
||||
|
||||
# check
|
||||
assert 'version = "12.4.678-SNAPSHOT"' in th.TEST_FILE_PATH.read_text()
|
||||
|
||||
|
||||
def test_json(tmp_path):
|
||||
# init
|
||||
th = Helper('config.json')
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
# test
|
||||
repo = VersionRepository(th.TEST_FILE_PATH)
|
||||
version = repo.get_version()
|
||||
version = version.create_release_version(ReleaseType.SNAPSHOT)
|
||||
repo.write_file(version.get_version_string())
|
||||
|
||||
# check
|
||||
assert '"version": "123.123.456-SNAPSHOT"' in th.TEST_FILE_PATH.read_text()
|
||||
|
||||
|
||||
def test_clojure(tmp_path):
|
||||
# init
|
||||
th = Helper('config.clj')
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
# test
|
||||
repo = VersionRepository(th.TEST_FILE_PATH)
|
||||
version = repo.get_version()
|
||||
version = version.create_release_version(ReleaseType.SNAPSHOT)
|
||||
repo.write_file(version.get_version_string())
|
||||
|
||||
# check
|
||||
assert '1.1.3-SNAPSHOT' in th.TEST_FILE_PATH.read_text()
|
||||
|
||||
|
||||
def test_python(tmp_path):
|
||||
# init
|
||||
th = Helper('config.py')
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
# test
|
||||
repo = VersionRepository(th.TEST_FILE_PATH)
|
||||
version = repo.get_version()
|
||||
version = version.create_release_version(ReleaseType.SNAPSHOT)
|
||||
repo.write_file(version.get_version_string())
|
||||
|
||||
# check
|
||||
assert '3.1.3-SNAPSHOT' in th.TEST_FILE_PATH.read_text()
|
|
@ -1,80 +0,0 @@
|
|||
import pytest as pt
|
||||
from pathlib import Path
|
||||
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, ReleaseContext, Release
|
||||
|
||||
from .helper import Helper
|
||||
|
||||
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)
|
||||
|
||||
class MyBuild(ReleaseMixin):
|
||||
pass
|
||||
|
||||
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 = Release(devops, MAIN_BRANCH, CONFIG_FILE)
|
||||
build = MyBuild(project, release)
|
||||
return build
|
||||
|
||||
def test_release_mixin_git(tmp_path: Path, monkeypatch: pt.MonkeyPatch):
|
||||
|
||||
# init
|
||||
th = Helper()
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
change_test_dir(tmp_path, monkeypatch)
|
||||
project = Project(tmp_path)
|
||||
|
||||
git_api = GitApi()
|
||||
git_api.init()
|
||||
git_api.set_user_config("ex.ample@mail.com", "Ex Ample")
|
||||
git_api.add_file(th.TEST_FILE_NAME)
|
||||
git_api.commit("MAJOR release")
|
||||
|
||||
build = initialize_with_object(project, th.TEST_FILE_PATH)
|
||||
build.prepare_release()
|
||||
release_version = build.release_repo.version_repository.get_version()
|
||||
|
||||
# test
|
||||
assert "124.0.1-SNAPSHOT" in release_version.get_version_string()
|
||||
|
||||
def test_release_mixin_environment(tmp_path: Path, monkeypatch: pt.MonkeyPatch):
|
||||
|
||||
# init
|
||||
th = Helper()
|
||||
th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
change_test_dir(tmp_path, monkeypatch)
|
||||
project = Project(tmp_path)
|
||||
|
||||
git_api = GitApi()
|
||||
git_api.init()
|
||||
git_api.set_user_config("ex.ample@mail.com", "Ex Ample")
|
||||
git_api.add_file(th.TEST_FILE_NAME)
|
||||
git_api.commit("Commit Message")
|
||||
|
||||
environment_api = EnvironmentApi()
|
||||
environment_api.set("DDADEVOPS_RELEASE_TYPE", "MAJOR")
|
||||
|
||||
build = initialize_with_object(project, th.TEST_FILE_PATH)
|
||||
build.prepare_release()
|
||||
release_version = build.release_repo.version_repository.get_version()
|
||||
|
||||
# test
|
||||
assert "124.0.1-SNAPSHOT" in release_version.get_version_string()
|
||||
|
||||
# tear down
|
||||
environment_api.set("DDADEVOPS_RELEASE_TYPE", "")
|
|
@ -1,32 +0,0 @@
|
|||
from src.main.python.ddadevops.application import PrepareReleaseService, TagAndPushReleaseService
|
||||
from src.test.python.release_mixin import MockReleaseRepository, MockReleaseTypeRepository, MockVersionRepository
|
||||
from src.test.python.release_mixin import MockGitApi
|
||||
|
||||
def test_prepare_release_service():
|
||||
# init
|
||||
mock_release_repo = MockReleaseRepository(MockVersionRepository(), MockReleaseTypeRepository(MockGitApi()))
|
||||
prepare_release_service = PrepareReleaseService()
|
||||
prepare_release_service.git_api = MockGitApi()
|
||||
prepare_release_service.write_and_commit_release(mock_release_repo.get_release("main"), mock_release_repo.version_repository)
|
||||
|
||||
#test
|
||||
assert prepare_release_service.git_api.add_file_count == 1
|
||||
assert prepare_release_service.git_api.commit_count == 1
|
||||
|
||||
# init
|
||||
prepare_release_service.write_and_commit_bump(mock_release_repo.get_release("main"), mock_release_repo.version_repository)
|
||||
|
||||
# test
|
||||
assert prepare_release_service.git_api.add_file_count == 2
|
||||
assert prepare_release_service.git_api.commit_count == 2
|
||||
|
||||
def test_tag_and_push_release_service():
|
||||
# init
|
||||
mock_release_repo = MockReleaseRepository(MockVersionRepository(), MockReleaseTypeRepository(MockGitApi()))
|
||||
tag_and_push_release_service = TagAndPushReleaseService(MockGitApi(), "main")
|
||||
tag_and_push_release_service.tag_release(mock_release_repo)
|
||||
tag_and_push_release_service.push_release()
|
||||
|
||||
#test
|
||||
assert tag_and_push_release_service.git_api.tag_annotated_count == 1
|
||||
assert tag_and_push_release_service.git_api.push_count == 1
|
8
src/test/python/resource_helper.py
Normal file
8
src/test/python/resource_helper.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from pathlib import Path
|
||||
from src.main.python.ddadevops.infrastructure import ExecutionApi
|
||||
|
||||
|
||||
def copy_resource(source: Path, target: Path):
|
||||
api = ExecutionApi()
|
||||
res_source = Path('src/test/resources/').joinpath(source)
|
||||
api.execute(f"cp {str(res_source)} {str(target)}")
|
42
src/test/python/test_c4k_build.py
Normal file
42
src/test/python/test_c4k_build.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
import os
|
||||
from pybuilder.core import Project
|
||||
from src.main.python.ddadevops.domain import DnsRecord
|
||||
from src.main.python.ddadevops.c4k_build import C4kBuild, add_c4k_mixin_config
|
||||
from .domain.helper import (
|
||||
CredentialsApiMock,
|
||||
devops_config,
|
||||
)
|
||||
|
||||
|
||||
def test_c4k_build(tmp_path):
|
||||
str_tmp_path = str(tmp_path)
|
||||
project = Project(str_tmp_path, name="name")
|
||||
|
||||
os.environ["C4K_GRAFANA_CLOUD_USER"] = "user"
|
||||
os.environ["C4K_GRAFANA_CLOUD_PASSWORD"] = "password"
|
||||
|
||||
sut = C4kBuild(
|
||||
project,
|
||||
devops_config(
|
||||
{
|
||||
"project_root_path": str_tmp_path,
|
||||
"mixin_types": [],
|
||||
"build_types": ["C4K"],
|
||||
"module": "c4k-test",
|
||||
"c4k_config": {"a": 1, "b": 2},
|
||||
"c4k_auth": {"c": 3, "d": 4},
|
||||
"c4k_grafana_cloud_user": "user",
|
||||
"c4k_grafana_cloud_password": "password",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
sut.initialize_build_dir()
|
||||
assert sut.build_path() == f"{str_tmp_path}/target/name/c4k-test"
|
||||
|
||||
sut.update_runtime_config(DnsRecord("test.de", ipv6="::1"))
|
||||
sut.write_c4k_config()
|
||||
assert os.path.exists(f"{sut.build_path()}/out_c4k_config.yaml")
|
||||
|
||||
sut.write_c4k_auth()
|
||||
assert os.path.exists(f"{sut.build_path()}/out_c4k_auth.yaml")
|
|
@ -1,46 +0,0 @@
|
|||
import os
|
||||
from pybuilder.core import Project
|
||||
from src.main.python.ddadevops.domain import DnsRecord
|
||||
from src.main.python.ddadevops.c4k_mixin import C4kBuild, add_c4k_mixin_config
|
||||
|
||||
class MyC4kBuild(C4kBuild):
|
||||
pass
|
||||
|
||||
def test_c4k_mixin(tmp_path):
|
||||
|
||||
build_dir = 'build'
|
||||
project_name = 'testing-project'
|
||||
module_name = 'c4k-test'
|
||||
tmp_path_str = str(tmp_path)
|
||||
|
||||
project = Project(tmp_path_str, name=project_name)
|
||||
|
||||
project_config = {
|
||||
'stage': 'test',
|
||||
'project_root_path': tmp_path_str,
|
||||
'module': module_name,
|
||||
'build_dir_name': build_dir
|
||||
}
|
||||
|
||||
config = {'a': 1, 'b': 2}
|
||||
auth = {'c': 3, 'd': 4}
|
||||
|
||||
add_c4k_mixin_config(project_config, config, auth, grafana_cloud_user='user', grafana_cloud_password='password')
|
||||
|
||||
assert project_config.get('C4kMixin') is not None
|
||||
|
||||
mixin = MyC4kBuild(project, project_config)
|
||||
mixin.initialize_build_dir()
|
||||
assert mixin.build_path() == f'{tmp_path_str}/{build_dir}/{project_name}/{module_name}'
|
||||
|
||||
mixin.update_runtime_config(DnsRecord('test.de', ipv6="1::"))
|
||||
sut = mixin.repo.get_c4k(mixin.project)
|
||||
assert 'fqdn' in sut.config()
|
||||
assert 'mon-cfg' in sut.config()
|
||||
assert 'mon-auth' in sut.c4k_mixin_auth
|
||||
|
||||
mixin.write_c4k_config()
|
||||
assert os.path.exists(f'{mixin.build_path()}/out_c4k_config.yaml')
|
||||
|
||||
mixin.write_c4k_auth()
|
||||
assert os.path.exists(f'{mixin.build_path()}/out_c4k_auth.yaml')
|
|
@ -1,27 +1,25 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
from pybuilder.core import Project
|
||||
from src.main.python.ddadevops.domain.common import Devops
|
||||
from src.main.python.ddadevops.devops_build import DevopsBuild
|
||||
|
||||
|
||||
class MyDevopsBuild(DevopsBuild):
|
||||
pass
|
||||
from src.main.python.ddadevops import DevopsBuild
|
||||
from .domain.helper import devops_config
|
||||
from .resource_helper import copy_resource
|
||||
|
||||
|
||||
def test_devops_build(tmp_path):
|
||||
build_dir = "build"
|
||||
project_name = "testing-project"
|
||||
module_name = "c4k-test"
|
||||
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=project_name)
|
||||
devops = Devops(
|
||||
stage="test",
|
||||
project_root_path=tmp_path_str,
|
||||
module=module_name,
|
||||
build_dir_name=build_dir,
|
||||
devops_build = DevopsBuild(
|
||||
project,
|
||||
devops_config(
|
||||
{
|
||||
"project_root_path": str_tmp_path,
|
||||
"build_types": [],
|
||||
"mixin_types": [],
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
devops_build = DevopsBuild(project, devops=devops)
|
||||
devops_build.initialize_build_dir()
|
||||
assert os.path.exists(f"{devops_build.build_path()}")
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
import os
|
||||
from pybuilder.core import Project
|
||||
from src.main.python.ddadevops.domain import Image, Devops
|
||||
from src.main.python.ddadevops.devops_image_build import DevopsImageBuild
|
||||
from src.main.python.ddadevops import DevopsImageBuild
|
||||
from .domain.helper import devops_config
|
||||
|
||||
|
||||
def test_devops_docker_build(tmp_path):
|
||||
build_dir = "build"
|
||||
project_name = "testing-project"
|
||||
module_name = "docker-test"
|
||||
tmp_path_str = str(tmp_path)
|
||||
str_tmp_path = str(tmp_path)
|
||||
project = Project(str_tmp_path, name="name")
|
||||
|
||||
project = Project(tmp_path_str, name=project_name)
|
||||
devops = Devops(
|
||||
stage="test",
|
||||
project_root_path=tmp_path_str,
|
||||
module=module_name,
|
||||
name=project_name,
|
||||
build_dir_name=build_dir
|
||||
os.environ["IMAGE_DOCKERHUB_USER"] = "user"
|
||||
os.environ["IMAGE_DOCKERHUB_PASSWORD"] = "password"
|
||||
|
||||
image_build = DevopsImageBuild(
|
||||
project,
|
||||
devops_config(
|
||||
{
|
||||
"project_root_path": str_tmp_path,
|
||||
"build_types": ["IMAGE"],
|
||||
"mixin_types": [],
|
||||
}
|
||||
),
|
||||
)
|
||||
image = Image(dockerhub_user="user", dockerhub_password="password", devops=devops)
|
||||
|
||||
docker_build = DevopsImageBuild(project, image=image)
|
||||
# docker_build.initialize_build_dir()
|
||||
# assert os.path.exists(f"{docker_build.build_path()}")
|
||||
assert image_build
|
||||
|
|
82
src/test/python/test_release_mixin.py
Normal file
82
src/test/python/test_release_mixin.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
import pytest as pt
|
||||
import os
|
||||
from pathlib import Path
|
||||
from pybuilder.core import Project
|
||||
|
||||
from src.main.python.ddadevops.release_mixin import ReleaseMixin
|
||||
from src.main.python.ddadevops.domain import Devops, Release
|
||||
from .domain.helper import devops_config
|
||||
from .resource_helper import copy_resource
|
||||
|
||||
def test_release_mixin(tmp_path):
|
||||
str_tmp_path = str(tmp_path)
|
||||
copy_resource(Path('package.json'), tmp_path)
|
||||
project = Project(str_tmp_path, name="name")
|
||||
|
||||
sut = ReleaseMixin(
|
||||
project,
|
||||
devops_config(
|
||||
{
|
||||
"project_root_path": str_tmp_path,
|
||||
"mixin_types": ["RELEASE"],
|
||||
"build_types": [],
|
||||
"module": "release-test",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
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):
|
||||
# # init
|
||||
# th = ResourceHelper()
|
||||
# th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
# th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
# change_test_dir(tmp_path, monkeypatch)
|
||||
# project = Project(tmp_path)
|
||||
|
||||
# git_api = GitApi()
|
||||
# git_api.init()
|
||||
# git_api.set_user_config("ex.ample@mail.com", "Ex Ample")
|
||||
# git_api.add_file(th.TEST_FILE_NAME)
|
||||
# git_api.commit("MAJOR release")
|
||||
|
||||
# build = initialize_with_object(project, th.TEST_FILE_PATH)
|
||||
# build.prepare_release()
|
||||
# release_version = build.release_repo.version_repository.get_version()
|
||||
|
||||
# # test
|
||||
# assert "124.0.1-SNAPSHOT" in release_version.get_version_string()
|
||||
|
||||
|
||||
# def test_release_mixin_environment(tmp_path: Path, monkeypatch: pt.MonkeyPatch):
|
||||
|
||||
# # init
|
||||
# th = Helper()
|
||||
# th.copy_files(th.TEST_FILE_PATH, tmp_path)
|
||||
# th.TEST_FILE_PATH = tmp_path / th.TEST_FILE_NAME
|
||||
|
||||
# change_test_dir(tmp_path, monkeypatch)
|
||||
# project = Project(tmp_path)
|
||||
|
||||
# git_api = GitApi()
|
||||
# git_api.init()
|
||||
# git_api.set_user_config("ex.ample@mail.com", "Ex Ample")
|
||||
# git_api.add_file(th.TEST_FILE_NAME)
|
||||
# git_api.commit("Commit Message")
|
||||
|
||||
# environment_api = EnvironmentApi()
|
||||
# environment_api.set("DDADEVOPS_RELEASE_TYPE", "MAJOR")
|
||||
|
||||
# build = initialize_with_object(project, th.TEST_FILE_PATH)
|
||||
# build.prepare_release()
|
||||
# release_version = build.release_repo.version_repository.get_version()
|
||||
|
||||
# # test
|
||||
# assert "124.0.1-SNAPSHOT" in release_version.get_version_string()
|
||||
|
||||
# # tear down
|
||||
# environment_api.set("DDADEVOPS_RELEASE_TYPE", "")
|
33
src/test/resources/package.json
Normal file
33
src/test/resources/package.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "c4k-jira",
|
||||
"description": "Generate c4k yaml for a jira deployment.",
|
||||
"author": "meissa GmbH",
|
||||
"version": "1.1.5-SNAPSHOT",
|
||||
"homepage": "https://gitlab.com/domaindrivenarchitecture/c4k-jira#readme",
|
||||
"repository": "https://www.npmjs.com/package/c4k-jira",
|
||||
"license": "APACHE2",
|
||||
"main": "c4k-jira.js",
|
||||
"bin": {
|
||||
"c4k-jira": "./c4k-jira.js"
|
||||
},
|
||||
"keywords": [
|
||||
"cljs",
|
||||
"jira",
|
||||
"k8s",
|
||||
"c4k",
|
||||
"deployment",
|
||||
"yaml",
|
||||
"convention4kubernetes"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://gitlab.com/domaindrivenarchitecture/c4k-jira/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"js-base64": "^3.6.1",
|
||||
"js-yaml": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"shadow-cljs": "^2.11.18",
|
||||
"source-map-support": "^0.5.19"
|
||||
}
|
||||
}
|
47
src/test/resources/project.clj
Normal file
47
src/test/resources/project.clj
Normal file
|
@ -0,0 +1,47 @@
|
|||
(defproject org.domaindrivenarchitecture/c4k-jira "1.1.5-SNAPSHOT"
|
||||
:description "jira c4k-installation package"
|
||||
:url "https://domaindrivenarchitecture.org"
|
||||
:license {:name "Apache License, Version 2.0"
|
||||
:url "https://www.apache.org/licenses/LICENSE-2.0.html"}
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[org.clojure/tools.reader "1.3.6"]
|
||||
[org.domaindrivenarchitecture/c4k-common-clj "2.0.3"]
|
||||
[hickory "0.7.1"]]
|
||||
:target-path "target/%s/"
|
||||
:source-paths ["src/main/cljc"
|
||||
"src/main/clj"]
|
||||
:resource-paths ["src/main/resources"]
|
||||
:repositories [["snapshots" :clojars]
|
||||
["releases" :clojars]]
|
||||
:deploy-repositories [["snapshots" {:sign-releases false :url "https://clojars.org/repo"}]
|
||||
["releases" {:sign-releases false :url "https://clojars.org/repo"}]]
|
||||
:profiles {:test {:test-paths ["src/test/cljc"]
|
||||
:resource-paths ["src/test/resources"]
|
||||
:dependencies [[dda/data-test "0.1.1"]]}
|
||||
:dev {:plugins [[lein-shell "0.5.0"]]}
|
||||
:uberjar {:aot :all
|
||||
:main dda.c4k-jira.uberjar
|
||||
:uberjar-name "c4k-jira-standalone.jar"
|
||||
:dependencies [[org.clojure/tools.cli "1.0.214"]
|
||||
[ch.qos.logback/logback-classic "1.4.5"
|
||||
:exclusions [com.sun.mail/javax.mail]]
|
||||
[org.slf4j/jcl-over-slf4j "2.0.6"]]}}
|
||||
:release-tasks [["test"]
|
||||
["vcs" "assert-committed"]
|
||||
["change" "version" "leiningen.release/bump-version" "release"]
|
||||
["vcs" "commit"]
|
||||
["vcs" "tag" "v" "--no-sign"]
|
||||
["change" "version" "leiningen.release/bump-version"]]
|
||||
:aliases {"native" ["shell"
|
||||
"native-image"
|
||||
"--report-unsupported-elements-at-runtime"
|
||||
"--initialize-at-build-time"
|
||||
"-jar" "target/uberjar/c4k-jira-standalone.jar"
|
||||
"-H:ResourceConfigurationFiles=graalvm-resource-config.json"
|
||||
"-H:Log=registerResource"
|
||||
"-H:Name=target/graalvm/${:name}"]
|
||||
"inst" ["shell" "sudo"
|
||||
"install"
|
||||
"-m=755"
|
||||
"target/uberjar/c4k-jira-standalone.jar"
|
||||
"/usr/local/bin/c4k-jira-standalone.jar"]})
|
Loading…
Reference in a new issue