Skip to content

Commit

Permalink
Add: Introduce a Project class that abstracts versioning
Browse files Browse the repository at this point in the history
The new Project allows to handle versioning in a multi language project.
  • Loading branch information
bjoernricks committed Mar 2, 2023
1 parent 74efacd commit 4f69b5b
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 0 deletions.
77 changes: 77 additions & 0 deletions pontos/version/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright (C) 2023 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.


from typing import List, Literal, Type, Union

from pontos.version.commands import get_commands
from pontos.version.errors import ProjectError
from pontos.version.version import (
Version,
VersionCalculator,
VersionCommand,
VersionUpdate,
)


class Project:
version_calculator_class: Type[VersionCalculator] = VersionCalculator

def __init__(self, commands: List[VersionCommand]) -> None:
self._commands = commands

@classmethod
def gather_project(cls) -> "Project":
"""
Get the project with the fitting VersionCommands of the current working
directory
Raises:
ProjectError if no fitting VersionCommand could be found
"""
commands = []
for cmd in get_commands():
command = cmd()
if command.project_found():
commands.append(command)

if not commands:
raise ProjectError("No project settings file found")

return cls(commands)

def get_version_calculator(self) -> VersionCalculator:
return self.version_calculator_class()

def update_version(
self, new_version: Version, *, force: bool = False
) -> VersionUpdate:
update = self._commands[0].update_version(new_version, force=force)
for cmd in self._commands[1:]:
next_update = cmd.update_version(new_version, force=force)
update.changed_files.extend(next_update.changed_files)

return update

def get_current_version(self) -> Version:
return self._commands[0].get_current_version()

def verify_version(
self, version: Union[Literal["current"], Version]
) -> None:
for cmd in self._commands:
cmd.verify_version(version)
157 changes: 157 additions & 0 deletions tests/version/test_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Copyright (C) 2023 Greenbone Networks GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import unittest

from pontos.testing import temp_directory, temp_python_module
from pontos.version.errors import ProjectError
from pontos.version.project import Project
from pontos.version.version import Version


class ProjectTestCase(unittest.TestCase):
def test_no_project_found(self):
with temp_directory(change_into=True), self.assertRaisesRegex(
ProjectError, "No project settings file found"
):
Project.gather_project()

def test_python_project(self):
current_version = Version("1.2.3")
new_version = Version("1.2.4")

content = f"__version__ = '{current_version}'"
with temp_python_module(
content, name="foo", change_into=True
) as temp_module:
pyproject_toml = temp_module.parent / "pyproject.toml"
pyproject_toml.write_text(
f'[tool.poetry]\nversion = "{current_version}"\n'
'[tool.pontos.version]\nversion-module-file = "foo.py"',
encoding="utf8",
)

project = Project.gather_project()
self.assertEqual(project.get_current_version(), current_version)

update = project.update_version(new_version)

self.assertEqual(update.previous, current_version)
self.assertEqual(update.new, new_version)

self.assertEqual(len(update.changed_files), 2)

def test_go_project(self):
current_version = Version("1.2.3")
new_version = Version("1.2.4")

with temp_directory(change_into=True) as temp_dir:
project_file = temp_dir / "go.mod"
project_file.touch()
version_file = temp_dir / "version.go"
version_file.write_text(f'var version = "{current_version}"')

project = Project.gather_project()
self.assertEqual(project.get_current_version(), current_version)

update = project.update_version(new_version)

self.assertEqual(update.previous, current_version)
self.assertEqual(update.new, new_version)

self.assertEqual(len(update.changed_files), 1)

def test_javascript_project(self):
current_version = Version("1.2.3")
new_version = Version("1.2.4")

with temp_directory(change_into=True) as temp_dir:
version_file = temp_dir / "package.json"
version_file.write_text(
f'{{"name": "foo", "version": "{current_version}"}}',
encoding="utf8",
)

project = Project.gather_project()
self.assertEqual(project.get_current_version(), current_version)

update = project.update_version(new_version)

self.assertEqual(update.previous, current_version)
self.assertEqual(update.new, new_version)

self.assertEqual(len(update.changed_files), 1)

def test_cmake_project_version(self):
current_version = Version("1.2.3")
new_version = Version("1.2.4")

with temp_directory(change_into=True) as temp_dir:
version_file = temp_dir / "CMakeLists.txt"
version_file.write_text("project(VERSION 1.2.3)", encoding="utf8")

project = Project.gather_project()
self.assertEqual(project.get_current_version(), current_version)

update = project.update_version(new_version)

self.assertEqual(update.previous, current_version)
self.assertEqual(update.new, new_version)

self.assertEqual(len(update.changed_files), 1)

def test_all(self):
current_version = Version("1.2.3")
new_version = Version("1.2.4")

content = f"__version__ = '{current_version}'"
with temp_python_module(
content, name="foo", change_into=True
) as temp_module:
temp_dir = temp_module.parent
pyproject_toml = temp_dir / "pyproject.toml"
pyproject_toml.write_text(
f'[tool.poetry]\nversion = "{current_version}"\n'
'[tool.pontos.version]\nversion-module-file = "foo.py"',
encoding="utf8",
)

go_mod_file = temp_dir / "go.mod"
go_mod_file.touch()
go_version_file = temp_dir / "version.go"
go_version_file.write_text(f'var version = "{current_version}"')

package_json = temp_dir / "package.json"
package_json.write_text(
f'{{"name": "foo", "version": "{current_version}"}}',
encoding="utf8",
)

cmake_lists_txt = temp_dir / "CMakeLists.txt"
cmake_lists_txt.write_text(
"project(VERSION 1.2.3)", encoding="utf8"
)

project = Project.gather_project()
self.assertEqual(project.get_current_version(), current_version)

update = project.update_version(new_version)

self.assertEqual(update.previous, current_version)
self.assertEqual(update.new, new_version)

self.assertEqual(len(update.changed_files), 5)

0 comments on commit 4f69b5b

Please sign in to comment.