This commit is contained in:
jerger 2023-02-24 10:46:43 +01:00
parent 0551fe6bfe
commit a09eaa7521
3 changed files with 118 additions and 97 deletions

View file

@ -1,5 +1,5 @@
from domain import Release, Version, ReleaseType from domain import Release, Version, ReleaseType
from infrastructure_api import FileHandler, SystemAPI from infrastructure_api import FileHandler, SystemAPI, GitApi
from pathlib import Path from pathlib import Path
class VersionRepository(): class VersionRepository():
@ -39,69 +39,19 @@ class ReleaseRepository():
pass pass
class ReleaseTypeRepository(): class ReleaseTypeRepository():
def __init__(self): def __init__(self, git_api, environment_api=None):
pass self.git_api = git_api
class GitRepository(): def get_release_type(self):
latest_commit = self.git_api.get_latest_commit()
def __init__(self): if ReleaseType.MAJOR.name in latest_commit.upper():
self.latest_commit = None
self.system_repository = SystemAPI()
@classmethod
def create_from_commit_string(cls, commit_string):
inst = cls()
inst.latest_commit = commit_string
return inst
def get_latest_n_commits(self, n: int):
self.system_repository.run_checked('git', 'log', '--oneline', '--format="%s %b"', f'-n {n}')
return self.system_repository.stdout
def get_latest_commit(self):
output = self.get_latest_n_commits(1)
self.latest_commit = " ".join(output) # returns a list of strings otherwise
return self.latest_commit
def get_release_type_from_latest_commit(self):
if self.latest_commit is None:
self.get_latest_commit()
if ReleaseType.MAJOR.name in self.latest_commit.upper():
return ReleaseType.MAJOR return ReleaseType.MAJOR
elif ReleaseType.MINOR.name in self.latest_commit.upper(): elif ReleaseType.MINOR.name in latest_commit.upper():
return ReleaseType.MINOR return ReleaseType.MINOR
elif ReleaseType.PATCH.name in self.latest_commit.upper(): elif ReleaseType.PATCH.name in latest_commit.upper():
return ReleaseType.PATCH return ReleaseType.PATCH
elif ReleaseType.SNAPSHOT.name in self.latest_commit.upper(): elif ReleaseType.SNAPSHOT.name in latest_commit.upper():
return ReleaseType.SNAPSHOT return ReleaseType.SNAPSHOT
else: else:
return None return None
def tag_annotated(self, annotation: str, message: str):
self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message)
return self.system_repository.stdout
def tag_annotated(self, annotation: str, message: str, count: int):
self.system_repository.run_checked('git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}')
return self.system_repository.stdout
def get_current_branch(self):
self.system_repository.run_checked('git', 'branch', '--show-current')
return ''.join(self.system_repository.stdout).rstrip()
def add_file(self, file_path: Path):
self.system_repository.run_checked('git', 'add', file_path)
return self.system_repository.stdout
def commit(self, commit_message: str):
self.system_repository.run_checked('git', 'commit', '-m', commit_message)
return self.system_repository.stdout
def push(self):
self.system_repository.run_checked('git', 'push')
return self.system_repository.stdout
def checkout(self, branch: str):
self.system_repository.run_checked('git', 'checkout', branch)
return self.system_repository.stdout

View file

@ -1,8 +1,10 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from pathlib import Path
import json import json
import re import re
import subprocess as sub import subprocess as sub
class FileHandler(ABC): class FileHandler(ABC):
@classmethod @classmethod
@ -18,7 +20,8 @@ class FileHandler(ABC):
case '.py': case '.py':
file_handler = PythonFileHandler() file_handler = PythonFileHandler()
case _: case _:
raise Exception(f'The file type "{config_file_type}" is not implemented') raise Exception(
f'The file type "{config_file_type}" is not implemented')
file_handler.config_file_path = file_path file_handler.config_file_path = file_path
file_handler.config_file_type = config_file_type file_handler.config_file_type = config_file_type
@ -27,11 +30,12 @@ class FileHandler(ABC):
@abstractmethod @abstractmethod
def parse(self) -> tuple[list[int], bool]: def parse(self) -> tuple[list[int], bool]:
pass pass
@abstractmethod @abstractmethod
def write(self, version_string): def write(self, version_string):
pass pass
class JsonFileHandler(FileHandler): class JsonFileHandler(FileHandler):
def parse(self) -> tuple[list[int], bool]: def parse(self) -> tuple[list[int], bool]:
@ -48,7 +52,7 @@ class JsonFileHandler(FileHandler):
with open(self.config_file_path, 'r+') as json_file: with open(self.config_file_path, 'r+') as json_file:
json_data = json.load(json_file) json_data = json.load(json_file)
json_data['version'] = version_string json_data['version'] = version_string
json_file.seek(0) json_file.seek(0)
json.dump(json_data, json_file, indent=4) json.dump(json_data, json_file, indent=4)
json_file.truncate() json_file.truncate()
@ -59,15 +63,16 @@ class GradleFileHandler(FileHandler):
with open(self.config_file_path, 'r') as gradle_file: with open(self.config_file_path, 'r') as gradle_file:
contents = gradle_file.read() contents = gradle_file.read()
version_line = re.search("\nversion = .*", contents) version_line = re.search("\nversion = .*", contents)
exception = Exception("Version not found in gradle file") exception = Exception("Version not found in gradle file")
if version_line is None: if version_line is None:
raise exception raise exception
version_line = version_line.group() version_line = version_line.group()
version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) version_string = re.search(
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line)
if version_string is None: if version_string is None:
raise exception raise exception
version_string = version_string.group() version_string = version_string.group()
is_snapshot = False is_snapshot = False
if '-SNAPSHOT' in version_string: if '-SNAPSHOT' in version_string:
@ -81,7 +86,8 @@ class GradleFileHandler(FileHandler):
def write(self, version_string): def write(self, version_string):
with open(self.config_file_path, 'r+') as gradle_file: with open(self.config_file_path, 'r+') as gradle_file:
contents = gradle_file.read() contents = gradle_file.read()
version_substitute = re.sub('\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) version_substitute = re.sub(
'\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents)
gradle_file.seek(0) gradle_file.seek(0)
gradle_file.write(version_substitute) gradle_file.write(version_substitute)
gradle_file.truncate() gradle_file.truncate()
@ -93,15 +99,16 @@ class PythonFileHandler(FileHandler):
with open(self.config_file_path, 'r') as python_file: with open(self.config_file_path, 'r') as python_file:
contents = python_file.read() contents = python_file.read()
version_line = re.search("\nversion = .*\n", contents) version_line = re.search("\nversion = .*\n", contents)
exception = Exception("Version not found in gradle file") exception = Exception("Version not found in gradle file")
if version_line is None: if version_line is None:
raise exception raise exception
version_line = version_line.group() version_line = version_line.group()
version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) version_string = re.search(
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line)
if version_string is None: if version_string is None:
raise exception raise exception
version_string = version_string.group() version_string = version_string.group()
is_snapshot = False is_snapshot = False
if '-SNAPSHOT' in version_string: if '-SNAPSHOT' in version_string:
@ -115,26 +122,29 @@ class PythonFileHandler(FileHandler):
def write(self, version_string): def write(self, version_string):
with open(self.config_file_path, 'r+') as python_file: with open(self.config_file_path, 'r+') as python_file:
contents = python_file.read() contents = python_file.read()
version_substitute = re.sub('\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents) version_substitute = re.sub(
'\nversion = "[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?"', f'\nversion = "{version_string}"', contents)
python_file.seek(0) python_file.seek(0)
python_file.write(version_substitute) python_file.write(version_substitute)
python_file.truncate() python_file.truncate()
class ClojureFileHandler(FileHandler): class ClojureFileHandler(FileHandler):
def parse(self) -> tuple[list[int], bool]: def parse(self) -> tuple[list[int], bool]:
with open(self.config_file_path, 'r') as clj_file: with open(self.config_file_path, 'r') as clj_file:
contents = clj_file.read() contents = clj_file.read()
version_line = re.search("^\\(defproject .*\n", contents) version_line = re.search("^\\(defproject .*\n", contents)
exception = Exception("Version not found in clj file") exception = Exception("Version not found in clj file")
if version_line is None: if version_line is None:
raise exception raise exception
version_line = version_line.group() version_line = version_line.group()
version_string = re.search('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line) version_string = re.search(
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', version_line)
if version_string is None: if version_string is None:
raise exception raise exception
version_string = version_string.group() version_string = version_string.group()
is_snapshot = False is_snapshot = False
if '-SNAPSHOT' in version_string: if '-SNAPSHOT' in version_string:
@ -149,12 +159,14 @@ class ClojureFileHandler(FileHandler):
with open(self.config_file_path, 'r+') as clj_file: with open(self.config_file_path, 'r+') as clj_file:
clj_first = clj_file.readline() clj_first = clj_file.readline()
clj_rest = clj_file.read() clj_rest = clj_file.read()
version_substitute = re.sub('[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', f'"{version_string}"\n', clj_first) version_substitute = re.sub(
'[0-9]*\\.[0-9]*\\.[0-9]*(-SNAPSHOT)?', f'"{version_string}"\n', clj_first)
clj_file.seek(0) clj_file.seek(0)
clj_file.write(version_substitute) clj_file.write(version_substitute)
clj_file.write(clj_rest) clj_file.write(clj_rest)
clj_file.truncate() clj_file.truncate()
class SystemAPI(): class SystemAPI():
def __init__(self): def __init__(self):
@ -175,3 +187,49 @@ class SystemAPI():
if len(self.stderr) > 0: if len(self.stderr) > 0:
raise Exception(f"Command failed with: {self.stderr}") raise Exception(f"Command failed with: {self.stderr}")
class GitApi():
def __init__(self):
self.system_repository = SystemAPI()
def get_latest_n_commits(self, n: int):
self.system_repository.run_checked(
'git', 'log', '--oneline', '--format="%s %b"', f'-n {n}')
return self.system_repository.stdout
def get_latest_commit(self):
output = self.get_latest_n_commits(1)
return " ".join(output)
def tag_annotated(self, annotation: str, message: str):
self.system_repository.run_checked(
'git', 'tag', '-a', annotation, '-m', message)
return self.system_repository.stdout
def tag_annotated(self, annotation: str, message: str, count: int):
self.system_repository.run_checked(
'git', 'tag', '-a', annotation, '-m', message, f'HEAD~{count}')
return self.system_repository.stdout
def get_current_branch(self):
self.system_repository.run_checked('git', 'branch', '--show-current')
return ''.join(self.system_repository.stdout).rstrip()
def add_file(self, file_path: Path):
self.system_repository.run_checked('git', 'add', file_path)
return self.system_repository.stdout
def commit(self, commit_message: str):
self.system_repository.run_checked(
'git', 'commit', '-m', commit_message)
return self.system_repository.stdout
def push(self):
self.system_repository.run_checked('git', 'push')
return self.system_repository.stdout
def checkout(self, branch: str):
self.system_repository.run_checked('git', 'checkout', branch)
return self.system_repository.stdout

View file

@ -1,3 +1,6 @@
from domain import ReleaseType, Release
from infrastructure import GitRepository, VersionRepository, ReleaseRepository
from infrastructure_api import GitApi
from pathlib import Path from pathlib import Path
import sys import sys
import os import os
@ -5,20 +8,26 @@ import os
# getting the name of the directory # getting the name of the directory
# where the this file is present. # where the this file is present.
current = os.path.dirname(os.path.realpath(__file__)) current = os.path.dirname(os.path.realpath(__file__))
# Getting the parent directory name # Getting the parent directory name
# where the current directory is present. # where the current directory is present.
parent = os.path.dirname(current) parent = os.path.dirname(current)
# adding the parent directory to # adding the parent directory to
# the sys.path. # the sys.path.
sys.path.append(parent) sys.path.append(parent)
# now we can import the module in the parent # now we can import the module in the parent
# directory. # directory.
from infrastructure import GitRepository, VersionRepository, ReleaseRepository
from domain import ReleaseType, Release def TestGitApi(GitApi):
def __init__(self, commit_string):
self.commit_string = commit_string
def get_latest_commit(self):
return self.commit_string
def test_git_repository(): def test_git_repository():
@ -27,16 +36,15 @@ def test_git_repository():
repo = GitRepository.create_from_commit_string(commit_string) repo = GitRepository.create_from_commit_string(commit_string)
release_type = repo.get_release_type_from_latest_commit() release_type = repo.get_release_type_from_latest_commit()
#test # test
assert release_type == ReleaseType.MAJOR assert release_type == ReleaseType.MAJOR
# init # init
commit_string = "MINOR bla" commit_string = "MINOR bla"
repo = GitRepository.create_from_commit_string(commit_string) repo = GitRepository.create_from_commit_string(commit_string)
release_type = repo.get_release_type_from_latest_commit() release_type = repo.get_release_type_from_latest_commit()
#test # test
assert release_type == ReleaseType.MINOR assert release_type == ReleaseType.MINOR
# init # init
@ -52,7 +60,7 @@ def test_git_repository():
repo = GitRepository.create_from_commit_string(commit_string) repo = GitRepository.create_from_commit_string(commit_string)
release_type = repo.get_release_type_from_latest_commit() release_type = repo.get_release_type_from_latest_commit()
#test # test
assert release_type == ReleaseType.SNAPSHOT assert release_type == ReleaseType.SNAPSHOT
# init # init
@ -60,9 +68,10 @@ def test_git_repository():
repo = GitRepository.create_from_commit_string(commit_string) repo = GitRepository.create_from_commit_string(commit_string)
release_type = repo.get_release_type_from_latest_commit() release_type = repo.get_release_type_from_latest_commit()
#test # test
assert release_type == None assert release_type == None
def test_gradle(tmp_path): def test_gradle(tmp_path):
# init # init
file_name = 'config.gradle' file_name = 'config.gradle'
@ -75,12 +84,13 @@ def test_gradle(tmp_path):
# test # test
repo = VersionRepository(f) repo = VersionRepository(f)
version = repo.get_version() version = repo.get_version()
version = version.create_release_version(ReleaseType.SNAPSHOT) version = version.create_release_version(ReleaseType.SNAPSHOT)
repo.write_file(version.get_version_string()) repo.write_file(version.get_version_string())
# check # check
assert 'version = "12.4.678-SNAPSHOT"' in f.read_text() assert 'version = "12.4.678-SNAPSHOT"' in f.read_text()
def test_json(tmp_path): def test_json(tmp_path):
# init # init
file_name = 'config.json' file_name = 'config.json'
@ -89,16 +99,17 @@ def test_json(tmp_path):
f = tmp_path / file_name f = tmp_path / file_name
f.write_text(contents) f.write_text(contents)
# test # test
repo = VersionRepository(f) repo = VersionRepository(f)
version = repo.get_version() version = repo.get_version()
version = version.create_release_version(ReleaseType.SNAPSHOT) version = version.create_release_version(ReleaseType.SNAPSHOT)
repo.write_file(version.get_version_string()) repo.write_file(version.get_version_string())
# check # check
assert '"version": "123.123.456-SNAPSHOT"' in f.read_text() assert '"version": "123.123.456-SNAPSHOT"' in f.read_text()
def test_clojure(tmp_path): def test_clojure(tmp_path):
# init # init
file_name = 'config.clj' file_name = 'config.clj'
@ -113,10 +124,11 @@ def test_clojure(tmp_path):
version = repo.get_version() version = repo.get_version()
version = version.create_release_version(ReleaseType.SNAPSHOT) version = version.create_release_version(ReleaseType.SNAPSHOT)
repo.write_file(version.get_version_string()) repo.write_file(version.get_version_string())
# check # check
assert '1.1.3-SNAPSHOT' in f.read_text() assert '1.1.3-SNAPSHOT' in f.read_text()
def test_python(tmp_path): def test_python(tmp_path):
# init # init
file_name = 'config.py' file_name = 'config.py'
@ -129,12 +141,13 @@ def test_python(tmp_path):
# test # test
repo = VersionRepository(f) repo = VersionRepository(f)
version = repo.get_version() version = repo.get_version()
version = version.create_release_version(ReleaseType.SNAPSHOT) version = version.create_release_version(ReleaseType.SNAPSHOT)
repo.write_file(version.get_version_string()) repo.write_file(version.get_version_string())
# check # check
assert '3.1.3-SNAPSHOT' in f.read_text() assert '3.1.3-SNAPSHOT' in f.read_text()
def test_release_repository(tmp_path): def test_release_repository(tmp_path):
# init # init
file_name = 'config.json' file_name = 'config.json'
@ -148,4 +161,4 @@ def test_release_repository(tmp_path):
sut = ReleaseRepository(VersionRepository(f)) sut = ReleaseRepository(VersionRepository(f))
current_release = sut.get_current_release() current_release = sut.get_current_release()
assert current_release.version is not None assert current_release.version is not None