219 lines
7.7 KiB
Python
219 lines
7.7 KiB
Python
from typing import List, Union
|
|
from pathlib import Path
|
|
import re
|
|
|
|
import toml
|
|
|
|
from semver.logger import logger
|
|
from semver.scm import SCM
|
|
from semver.version_type import VersionType
|
|
from semver.exceptions import (
|
|
NoMergeFoundException,
|
|
NotMainBranchException,
|
|
NoGitFlowException,
|
|
)
|
|
|
|
|
|
class SemVer:
|
|
"""Primary class for handling auto-semver processing"""
|
|
|
|
def __init__(
|
|
self,
|
|
scm: SCM,
|
|
main_branches: Union[List[str], None] = None,
|
|
major_branches: Union[List[str], None] = None,
|
|
minor_branches: Union[List[str], None] = None,
|
|
patch_branches: Union[List[str], None] = None,
|
|
):
|
|
"""
|
|
Initialize the SemVer object
|
|
:param global_user: Toggles git user at a global level, useful for build servers
|
|
:param scm: The source control manager to use
|
|
:param main_branches: Branches to run versioning on
|
|
:param major_branches: List of prefixes for major branches
|
|
:param minor_branches: List of prefixes for minor branches
|
|
:param patch_branches: List of prefixes for patch branches
|
|
"""
|
|
self._merged_branch: Union[str, None] = None
|
|
self._branch: Union[str, None] = None
|
|
self._version_type: Union[VersionType, None] = None
|
|
|
|
self._main_branches: List[str] = main_branches if main_branches else []
|
|
self._major_branches: List[str] = major_branches if major_branches else []
|
|
self._minor_branches: List[str] = minor_branches if minor_branches else []
|
|
self._patch_branches: List[str] = patch_branches if patch_branches else []
|
|
|
|
self._scm: SCM = scm
|
|
|
|
def _version_repo(self) -> str:
|
|
"""
|
|
Use bump_version to update the repo version
|
|
:return: The new version
|
|
"""
|
|
version = self._scm.get_tag_version()
|
|
if not self._version_type:
|
|
raise NoMergeFoundException()
|
|
|
|
logger.debug(f"Running bumpversion of type: {self._version_type.name}")
|
|
return self._bump_version(version, self._version_type)
|
|
|
|
def _process_config_string(self, cfg_string, new_version, version):
|
|
return cfg_string.replace("{new_version}", new_version).replace(
|
|
"{current_version}", version
|
|
)
|
|
|
|
def _bump_version(
|
|
self,
|
|
version: str,
|
|
index: VersionType = VersionType.MINOR,
|
|
tag_repo: bool = True,
|
|
update_files: bool = True,
|
|
) -> str:
|
|
"""
|
|
Bump the version of the repo
|
|
:param version: The current version
|
|
:param index: The index of the version to bump
|
|
:param tag_repo: Whether or not to tag the repo
|
|
:param update_files: Whether or not to update the files
|
|
:return: The new version
|
|
"""
|
|
v: List[str] = version.split(".")
|
|
|
|
# Bump version
|
|
v[index] = str(int(v[index]) + 1)
|
|
|
|
# Reset subversions
|
|
i = len(v) - 1
|
|
while i > index:
|
|
v[i] = "0"
|
|
i = i - 1
|
|
|
|
# Get new version
|
|
new_version = ".".join(v)
|
|
|
|
# Tag new version
|
|
if tag_repo and version != new_version:
|
|
self._scm.tag_version(new_version)
|
|
|
|
# Update local files
|
|
if update_files:
|
|
self._update_file_version(new_version, version)
|
|
|
|
return new_version
|
|
|
|
def _update_file_version(self, new_version: str, version: str = "0.0.0"):
|
|
"""
|
|
Update the version in the config file
|
|
:param new_version: The new version
|
|
:param version: The current version
|
|
"""
|
|
# Open up config file
|
|
config = toml.load("./.bumpversion.cfg")
|
|
|
|
bump_version_file_prefix = "bumpversion:file:"
|
|
bump_version_file_prefix_len = len(bump_version_file_prefix)
|
|
for section in config:
|
|
if section.startswith(bump_version_file_prefix):
|
|
file_path = Path(section[bump_version_file_prefix_len:])
|
|
section_data = config[section]
|
|
if file_path.is_file():
|
|
# Get search val from config
|
|
search_val = section_data["search"]
|
|
search_val = self._process_config_string(
|
|
search_val, new_version, version
|
|
)
|
|
|
|
# Get replace val from config
|
|
replace_val = section_data["replace"]
|
|
replace_val = self._process_config_string(
|
|
replace_val, new_version, version
|
|
)
|
|
|
|
# Update replace values in file
|
|
with open(file_path, "r") as file:
|
|
filedata = file.read()
|
|
filedata = filedata.replace(search_val, replace_val)
|
|
with open(file_path, "w") as file:
|
|
file.write(filedata)
|
|
else:
|
|
logger.warning(
|
|
f"Tried to version file: '{file_path}' but it doesn't exist!"
|
|
)
|
|
|
|
def get_version(
|
|
self, build: int = 0, version_format: Union[str, None] = None, dot: bool = False
|
|
):
|
|
"""
|
|
Get the version of the repo
|
|
:param build: The build number
|
|
:param version_format: The format of the version
|
|
:param dot: Whether or not to replace / with .
|
|
:return: The version
|
|
"""
|
|
version = self._scm.get_tag_version()
|
|
|
|
# Get the commit hash of the version
|
|
v_hash = self._scm.get_version_hash(version)
|
|
# Get the current commit hash
|
|
c_hash = self._scm.get_hash()
|
|
|
|
# If the version commit hash and current commit hash
|
|
# do not match return the branch name else return the version
|
|
if v_hash != c_hash:
|
|
logger.debug("v_hash and c_hash do not match!")
|
|
branch = self._scm.get_branch()
|
|
logger.debug("merged branch is: {}".format(branch))
|
|
version_type = self._scm.get_version_type(
|
|
branch, self._major_branches, self._minor_branches, self._patch_branches
|
|
)
|
|
logger.debug("version type is: {}".format(version_type))
|
|
if version_type:
|
|
next_version = self._bump_version(
|
|
self._scm.get_tag_version(), version_type, False, False
|
|
)
|
|
|
|
if version_format in ("npm", "docker"):
|
|
return "{}-{}.{}".format(
|
|
next_version, re.sub(r"[/_]", "-", branch), build
|
|
)
|
|
if version_format == "maven":
|
|
qualifier = "SNAPSHOT" if build == 0 else build
|
|
return "{}-{}-{}".format(
|
|
next_version, re.sub(r"[/_]", "-", branch), qualifier
|
|
)
|
|
if dot:
|
|
branch = branch.replace("/", ".")
|
|
return branch
|
|
return version
|
|
|
|
def run(self, push=True):
|
|
"""
|
|
Run the versioning process
|
|
1) get branches from last commit message
|
|
2) see if we're merging into a main branch
|
|
3) see what type of versioning we should do
|
|
4) version the repo
|
|
:param push: Whether or not to push the changes
|
|
"""
|
|
self._branch = self._scm.get_branch()
|
|
self._merged_branch = self._scm.get_merge_branch()
|
|
|
|
if not self._merged_branch:
|
|
raise NoMergeFoundException("No merge found")
|
|
if self._branch not in self._main_branches:
|
|
raise NotMainBranchException("Not a main branch")
|
|
|
|
self._version_type = self._scm.get_version_type(
|
|
self._branch,
|
|
self._major_branches,
|
|
self._minor_branches,
|
|
self._patch_branches,
|
|
)
|
|
|
|
if not self._version_type:
|
|
raise NoGitFlowException("Could not determine version type")
|
|
|
|
self._version_repo()
|
|
if push:
|
|
self._scm.commit_and_push(self._branch)
|