diff --git a/README.md b/README.md index 20cd417..e87aac4 100644 --- a/README.md +++ b/README.md @@ -104,15 +104,33 @@ current file. Currently, you can only inherit once and from one URL, so it means contains `extends` keyword, it won't be evaluated. -**Proxy** +## 🔀 many use cases at once! (different release kinds) -TODO +valhalla supports different use cases. F.e. you want to have ability to create minor release and hotfix +but these processes have different steps. You can do it! -## 🏴󠁣󠁤󠁥󠁱󠁿 variables +What you have to do is to define 2 files: + +- valhalla.yml +- valhalla-hotfix.yml + +And now, when you want to start one of this process: + +- to create minor release using `valhalla.yml` create branch matching regex: `release-*` +- to create hotfix release using `valhalla-hotfix.yml` create branch matching regex: `release-hotfix-*` + +This is only example, and you can define any suffix, f.e `valhalla-super-release.yml` needs +branch `release-super-release-*` + +*You can only define f.e. `valhalla-minor.yml` and you do not need `valhalla.yml`, but your branches name triggering +release must meet convention* + +## 🔢 variables **Use `{}` to evaluate variable to value f.e. `{FOOBAR}`** **hierarchy (from most important):** + - predefined variables - custom variables - environment variables @@ -148,7 +166,6 @@ merge_request: **It is really useful with `extends` mechanism, f.e. define general template with `variables` which will be overriden in child `valhalla.yml`.** - ### 🐛 environment variables Valhalla allows you to use any variable defined in your environment system, it is useful f.e when you diff --git a/__main__.py b/__main__.py index 98f75b9..c211a88 100644 --- a/__main__.py +++ b/__main__.py @@ -1,4 +1,4 @@ from valhalla.main import start if __name__ == '__main__': - start() \ No newline at end of file + start() diff --git a/test/ci_provider/gitlab/get_version_test.py b/test/ci_provider/gitlab/get_version_test.py index a30cd46..1416d5f 100644 --- a/test/ci_provider/gitlab/get_version_test.py +++ b/test/ci_provider/gitlab/get_version_test.py @@ -1,29 +1,79 @@ import unittest from unittest.mock import patch -from valhalla.ci_provider.gitlab.get_version import get_version_number_to_release +from valhalla.ci_provider.gitlab.get_version import get_version_to_release +from valhalla.version.version_to_release import ReleaseKind class GetVersionTest(unittest.TestCase): @patch('os.environ.get') def test_release_branch(self, mock_env_get): + # given: mock_env_get.return_value = 'release-1.0.0' - result = get_version_number_to_release() - self.assertEqual(result, '1.0.0') + release_kinds = [ReleaseKind("valhalla.yml", "", ".")] + + # when: + result = get_version_to_release(release_kinds) + + # then: + self.assertEqual(result.version_number_to_release, '1.0.0') + self.assertEqual(result.release_kind.filename, "valhalla.yml") + self.assertEqual(result.get_config_file_path(), "./valhalla.yml") @patch('os.environ.get') @patch('valhalla.ci_provider.gitlab.get_version.exit') def test_non_release_branch(self, mock_exit, mock_env_get): + # given: mock_env_get.return_value = 'feature-branch' - result = get_version_number_to_release() + release_kinds = [ReleaseKind("valhalla.yml", "", ".")] + + # when: + result = get_version_to_release(release_kinds) + + # then: self.assertIsNone(result) mock_exit.assert_called_with(-1) @patch('os.environ.get') @patch('valhalla.ci_provider.gitlab.get_version.exit') def test_no_ci_commit_branch(self, mock_exit, mock_env_get): + # given: mock_env_get.return_value = None - result = get_version_number_to_release() + release_kinds = [ReleaseKind("valhalla.yml", "", ".")] + + # when: + result = get_version_to_release(release_kinds) + + # then: + self.assertIsNone(result) + mock_exit.assert_called_with(-1) + + @patch('os.environ.get') + def test_release_hotfix_branch(self, mock_env_get): + # given: + mock_env_get.return_value = 'release-hotfix-1.2.3' + release_kinds = [ReleaseKind("valhalla.yml", "", "."), + ReleaseKind("valhalla-hotfix.yml", "-hotfix", ".")] + + # when: + result = get_version_to_release(release_kinds) + + # then: + self.assertEqual(result.version_number_to_release, '1.2.3') + self.assertEqual(result.release_kind.filename, "valhalla-hotfix.yml") + self.assertEqual(result.get_config_file_path(), "./valhalla-hotfix.yml") + + @patch('os.environ.get') + @patch('valhalla.ci_provider.gitlab.get_version.exit') + def test_no_matching_valhalla_branch(self, mock_exit, mock_env_get): + # given: + mock_env_get.return_value = 'release-1.2.3' + release_kinds = [ReleaseKind("valhalla-hotfix.yml", "-hotfix", ".")] + + # when: + result = get_version_to_release(release_kinds) + + # then: self.assertIsNone(result) mock_exit.assert_called_with(-1) diff --git a/test/version/__init__.py b/test/version/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/version/resources/test_main_and_hotfix_file/valhalla-hotfix.yml b/test/version/resources/test_main_and_hotfix_file/valhalla-hotfix.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/version/resources/test_main_and_hotfix_file/valhalla.yml b/test/version/resources/test_main_and_hotfix_file/valhalla.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/version/resources/test_main_file/valhalla.yml b/test/version/resources/test_main_file/valhalla.yml new file mode 100644 index 0000000..e69de29 diff --git a/test/version/version_to_release_test.py b/test/version/version_to_release_test.py new file mode 100644 index 0000000..8af7e55 --- /dev/null +++ b/test/version/version_to_release_test.py @@ -0,0 +1,34 @@ +import unittest +from unittest.mock import patch + +from valhalla.version.version_to_release import get_release_kinds + + +class GetValhallaReleaseKindsTest(unittest.TestCase): + + def test_main_file(self): + result = get_release_kinds("./resources/test_main_file") + + self.assertEqual(len(result), 1) + + self.assertEqual(result[0].filename, "valhalla.yml") + self.assertEqual(result[0].suffix, "") + self.assertEqual(result[0].path, "./resources/test_main_file") + + def test_main_and_hotfix_file(self): + result = get_release_kinds("./resources/test_main_and_hotfix_file") + + self.assertEqual(len(result), 2) + + self.assertEqual(result[0].filename, "valhalla.yml") + self.assertEqual(result[0].suffix, "") + self.assertEqual(result[0].path, "./resources/test_main_and_hotfix_file") + + self.assertEqual(result[1].filename, "valhalla-hotfix.yml") + self.assertEqual(result[1].suffix, "-hotfix") + self.assertEqual(result[1].path, "./resources/test_main_and_hotfix_file") + + @patch('valhalla.version.version_to_release.exit') + def test_empty(self, mock_exit): + result = get_release_kinds("./resources/empty") + mock_exit.assert_called_with(-1) diff --git a/valhalla/ci_provider/gitlab/get_version.py b/valhalla/ci_provider/gitlab/get_version.py index 5742065..0cac098 100644 --- a/valhalla/ci_provider/gitlab/get_version.py +++ b/valhalla/ci_provider/gitlab/get_version.py @@ -1,23 +1,69 @@ import os +from typing import List from valhalla.common.logger import info, error +from valhalla.version.version_to_release import VersionToRelease, ReleaseKind +BASE_PREFIX = "release-" -def get_version_number_to_release() -> str: + +def get_version_to_release(release_kinds: List[ReleaseKind]) -> VersionToRelease: ci_commit_branch = os.environ.get('CI_COMMIT_BRANCH') if ci_commit_branch: info(f'Name of branch is: {ci_commit_branch}') if ci_commit_branch.startswith('release-'): - project_version = ci_commit_branch[len('release-'):] - info(f'Project version that is going to be released: {project_version}') - return project_version + + # first we search for specific release kinds + for release_kind in release_kinds: + if release_kind.suffix != "": + prefix = __get_branch_prefix(release_kind) + if ci_commit_branch.startswith(prefix): + return __matched(ci_commit_branch, prefix, release_kind) + + # now if specific release kind not found we search for main + for release_kind in release_kinds: + if release_kind.suffix == "": + prefix = BASE_PREFIX + if ci_commit_branch.startswith(prefix): + return __matched(ci_commit_branch, prefix, release_kind) + + __no_matching_release_kind(ci_commit_branch, release_kinds) + else: - error('This is not a release branch! This script should not be run! The name of the branch must be release-X.X.X') + error('This is not a release branch! This script should not be run! The name of the branch must be ' + 'release-X.X.X') error('Check valhalla configration and manual !') exit(-1) else: error('CI_COMMIT_BRANCH environment variable is not set. Are you using GitLab CI? If not change your ' - 'valhalla configration') + 'valhalla configration!') exit(-1) + + +def __get_branch_prefix(release_kind: ReleaseKind) -> str: + prefix = (BASE_PREFIX + release_kind.suffix).replace("--", "-") + if prefix.endswith("-"): + return prefix + else: + return prefix + "-" + + +def __matched(ci_commit_branch: str, prefix: str, release_kind: ReleaseKind) -> VersionToRelease: + info(f"Branch name {ci_commit_branch} has prefix {prefix} and matches with release kind {release_kind}") + project_version = ci_commit_branch[len(prefix):] + info(f'Project version that is going to be released: {project_version}') + return VersionToRelease(project_version, release_kind) + + +def __no_matching_release_kind(ci_commit_branch: str, release_kinds: List[ReleaseKind]): + error('This is a release branch, but valhalla could not find matching valhalla.yml file!') + error(f'Name of branch is: {ci_commit_branch}') + for kind in release_kinds: + error(f'Available release kind: {kind}') + prefix = __get_branch_prefix(kind) + error(f'To match this release kind you branch must starts with {prefix} f.e {prefix}1.5.3') + + error('Create branch name with correct prefix to the release kind that you want to match with and start!') + exit(-1) diff --git a/valhalla/common/get_config.py b/valhalla/common/get_config.py index 10285af..b0e93d4 100644 --- a/valhalla/common/get_config.py +++ b/valhalla/common/get_config.py @@ -118,10 +118,10 @@ def __repr__(self): f" )" -def get_config(path) -> Config: +def get_config(path: str) -> Config: + info(f"Trying to load config from: {path}") try: with open(path) as f: - info(f"Trying to load config from: {path}") yml_dict = safe_load(f) extends_list = get_from_dict(yml_dict, 'extends', False) @@ -192,7 +192,7 @@ def get_release_description_config_part(description_dict: dict) -> ReleaseDescri def get_release_assets_config_part(assets_dict: dict) -> ReleaseAssetsConfig: if assets_dict is None: return ReleaseAssetsConfig([]) - + links_dict = assets_dict['links'] links = get_release_assets_links_config_part(links_dict) diff --git a/valhalla/main.py b/valhalla/main.py index 06d968a..dd1b845 100644 --- a/valhalla/main.py +++ b/valhalla/main.py @@ -1,31 +1,34 @@ from valhalla.ci_provider.get_token import get_valhalla_token +from valhalla.ci_provider.gitlab.get_version import get_version_to_release from valhalla.ci_provider.gitlab.merge_request import GitLabValhallaMergeRequest from valhalla.ci_provider.gitlab.release import GitLabValhallaRelease from valhalla.commit import before -from valhalla.ci_provider.gitlab.get_version import get_version_number_to_release from valhalla.commit.commit import GitRepository -from valhalla.common.get_config import get_config, Config, CommitConfig, MergeRequestConfig +from valhalla.common.get_config import get_config, CommitConfig, MergeRequestConfig, Config from valhalla.common.logger import info, init_logger from valhalla.common.resolver import init_str_resolver, init_str_resolver_custom_variables from valhalla.release.assets import Assets from valhalla.release.description import Description +from valhalla.version.version_to_release import get_release_kinds def start(): print(f'Release the Valhalla!') - version_to_release = get_version_number_to_release() + release_kinds = get_release_kinds(".") + + version_to_release = get_version_to_release(release_kinds) token = get_valhalla_token() init_logger(token) - init_str_resolver(version_to_release, token) + init_str_resolver(version_to_release.version_number_to_release, token) - config = get_config("./valhalla.yml") + config = get_config(version_to_release.get_config_file_path()) init_str_resolver_custom_variables(config.variables) commit(config.commit_before_release, token) - create_release(config, version_to_release) + create_release(config, version_to_release.version_number_to_release) commit(config.commit_after_release, token) @@ -45,7 +48,7 @@ def create_merge_request(merge_request_config: MergeRequestConfig): info("merge_request.enabled is False in valhalla.yml, skipping") -def create_release(config, version_to_release): +def create_release(config: Config, version_to_release: str): info("Preparing to create release") release = GitLabValhallaRelease() description = Description(config.release_config.description_config) diff --git a/valhalla/release/__init__.py b/valhalla/release/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valhalla/version/__init__.py b/valhalla/version/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/valhalla/version/version_to_release.py b/valhalla/version/version_to_release.py new file mode 100644 index 0000000..4f2e248 --- /dev/null +++ b/valhalla/version/version_to_release.py @@ -0,0 +1,50 @@ +import re +import os + +from typing import List + +from valhalla.common.logger import info, error + + +class ReleaseKind: + + def __init__(self, filename: str, suffix: str, path: str): + self.filename = filename + self.suffix = suffix + self.path = path + + def __repr__(self): + return f"filename={self.filename}, suffix={self.suffix}, path={self.path}" + + +class VersionToRelease: + + def __init__(self, version_number_to_release: str, release_kind: ReleaseKind): + self.version_number_to_release = version_number_to_release + self.release_kind = release_kind + + def get_config_file_path(self): + return self.release_kind.path + "/" + self.release_kind.filename + + +def get_release_kinds(path: str) -> List[ReleaseKind]: + info(f"Searching for valhalla*.yml files in: {path}") + pattern = re.compile(r'valhalla(.*)\.yml') + release_kinds = [] + + for root, dirs, files in os.walk(path): + for file in files: + match = pattern.match(file) + if match: + release_kinds.append(ReleaseKind(file, match.group(1), path)) + + for kind in release_kinds: + info(f"Found: {kind}") + + if len(release_kinds) == 0: + error("Cloud not find any valhalla file! You have to have at least one file matching valhalla*.yml f.e " + "valhalla.yml. You can define valhalla-hotfix.yml but remember to use release-hotfix-* branch to start" + "valhalla proces :)") + exit(-1) + + return release_kinds