From 5ff2e25d1fa6f3963d9abcfb71df7a70d8bca278 Mon Sep 17 00:00:00 2001 From: kdmukai <934746+kdmukai@users.noreply.github.com> Date: Sat, 20 Dec 2025 10:39:58 -0600 Subject: [PATCH] Remove hard-coded VERSION --- src/seedsigner/helpers/version.py | 61 ++++++++++++++++++++++--------- tests/test_version.py | 48 ++++++++++++------------ 2 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/seedsigner/helpers/version.py b/src/seedsigner/helpers/version.py index 86dfeeed..76be03a4 100644 --- a/src/seedsigner/helpers/version.py +++ b/src/seedsigner/helpers/version.py @@ -3,6 +3,8 @@ import logging import os from datetime import datetime +from seedsigner.models.settings import Settings + logger = logging.getLogger(__name__) @@ -17,9 +19,6 @@ class Version: Internal utility functions are separated out as class methods for easier mocking in tests. """ - - VERSION = "0.8.6" - VERSION_FILENAME = "version.json" @@ -98,6 +97,31 @@ class Version: hard-coded VERSION constant. """ name = None + + def _prefix_version_name(version: str) -> str: + """ + Ensure version strings are prefixed with 'v' if they look like semantic + versions. Only checks that the first part is numeric in order to be compatible + with minor versions like an "rc1". + """ + if not version.startswith("v") and version.count(".") >= 1 and version.split(".")[0].isnumeric(): + return f"v{version}" + return version + + print(f"{Settings.HOSTNAME=}") + + if Settings.HOSTNAME == Settings.SEEDSIGNER_OS: + # The SeedSigner OS build process generates the version.json file for the tag, + # branch, or commit hash the image is targeting. + try: + name = cls._get_version_file()["version"] + except Exception: + # Shouldn't be possible. Raise an exception to alert testers before this + # image goes out. + raise Exception("Could not read the version from the version.json file.") + return _prefix_version_name(name) + + # In local dev we dynamically read from the .git/HEAD file branch_name, commit_hash = cls._read_HEAD_file() if branch_name: name = branch_name @@ -108,18 +132,11 @@ class Version: name = matching_tag else: name = commit_hash[:7] # short commit hash - - if name is None: - # Try reading from version.json file - version_file_data = cls._get_version_file() - if version_file_data: - name = version_file_data.get("version") if name is None: - # Fallback to hard-coded version - name = f"v{cls.VERSION}" + raise Exception("Could not determine version from git info.") - return name + return _prefix_version_name(name) @classmethod @@ -127,6 +144,17 @@ class Version: """ Recursively scan the src/ directory for the most recent python file edit time. """ + if Settings.HOSTNAME == Settings.SEEDSIGNER_OS: + # The SeedSigner OS build process generates the version.json file which will + # already contain the last edit time. + try: + last_src_edit_str = cls._get_version_file()["last_src_edit"] + return datetime.fromisoformat(last_src_edit_str) + except Exception as e: + # Shouldn't be possible. Raise an exception to alert testers before this + # image goes out. + raise Exception("Could not read the last_src_edit from the version.json file.") + try: path = os.path.dirname(os.path.abspath(__file__)) @@ -148,11 +176,8 @@ class Version: last_modified = max(file_mtime, last_modified) if num_files == 0: - # Fallback to reading from the version file - version_file_data = cls._get_version_file() - if version_file_data: - last_src_edit_str = version_file_data.get("last_src_edit") - return datetime.fromisoformat(last_src_edit_str) + # Shouldn't be possible + raise Exception("No python source files found in src/ directory") return datetime.fromtimestamp(last_modified) @@ -167,6 +192,8 @@ class Version: if __name__ == "__main__": """ CLI to extract the current version and last edit time and write to `src/seedsigner/version.json`. + + Used by the SeedSigner OS build process to generate the version.json file. """ version_info = dict(version=Version.get_version()) last_edit_dt = Version.get_last_src_edit() diff --git a/tests/test_version.py b/tests/test_version.py index cc99ef3d..3819c6aa 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -11,33 +11,33 @@ from seedsigner.helpers.version import Version class TestVersion(BaseTest): - def test_version_with_no_git_head(self): - """ - If there is no .git/HEAD file, the hard-coded VERSION constant should be returned. - """ - # mock out the os.path.exists call to always return False - with mock.patch("os.path.exists", return_value=False): - version = Version.get_version() - assert version == f"v{Version.VERSION}" + # def test_version_with_no_git_head(self): + # """ + # If there is no .git/HEAD file, the hard-coded VERSION constant should be returned. + # """ + # # mock out the os.path.exists call to always return False + # with mock.patch("os.path.exists", return_value=False): + # version = Version.get_version() + # assert version == f"v{Version.VERSION}" - def test_version_with_actual_git_head(self): - """ - If the .git dir exists on our actual filesystem right now, we should get a version - based on its contents. + # def test_version_with_actual_git_head(self): + # """ + # If the .git dir exists on our actual filesystem right now, we should get a version + # based on its contents. - NOTE: This test is potentially fragile as it depends on the test runner system's actual - git state. The get_version() call should be able to handle all possible git states, but - this does create the possibility of external variability. - """ - fake_hardcoded_version = "fake.version.123" - with mock.patch.object(Version, 'VERSION', fake_hardcoded_version): - git_dot_dir = Version._get_dot_git_dir() - if os.path.exists(git_dot_dir): - assert Version.get_version() != f"v{fake_hardcoded_version}" - else: - # If there's no .git dir, mark this test as skipped - pytest.skip(f"No .git dir found at {git_dot_dir}, skipping test.") + # NOTE: This test is potentially fragile as it depends on the test runner system's actual + # git state. The get_version() call should be able to handle all possible git states, but + # this does create the possibility of external variability. + # """ + # fake_hardcoded_version = "fake.version.123" + # with mock.patch.object(Version, 'VERSION', fake_hardcoded_version): + # git_dot_dir = Version._get_dot_git_dir() + # if os.path.exists(git_dot_dir): + # assert Version.get_version() != f"v{fake_hardcoded_version}" + # else: + # # If there's no .git dir, mark this test as skipped + # pytest.skip(f"No .git dir found at {git_dot_dir}, skipping test.") def test_version_with_mocked_git_head(self):