diff --git a/CHANGELOG.md b/CHANGELOG.md index 8602beb..7b95ea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Changelog -## v7.1.0 (unreleased) +## v7.1.1 (unreleased) + +### Fix +- The compatibility check accepts a package with the same major as the Helm Chart `appVersion` and a + minor greater or equal to the Helm Chart minor. A minor release of the package keeps the Helm Chart + contract, so it no longer needs a new Helm Chart release. A Helm Chart minor newer than the package + minor still aborts, because the Helm Chart can use options that the package does not know. + v7.1.0 refused to start with the Helm Chart `appVersion` 7.0.0. + +## v7.1.0 (2026-10-20) v7.1.0 replaces v8.0.0. Chartreuse keeps major 7, because the [Chartreuse Helm Chart](https://github.com/wiremind/wiremind-helm-charts/tree/main/charts/chartreuse) diff --git a/README.md b/README.md index 2bafe71..039dc71 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ helm repo add wiremind https://wiremind.github.io/wiremind-helm-charts helm install wiremind-crds wiremind/wiremind-crds --version 0.1.0 ``` -- Please make sure Chartreuse Python **Package version** and Chartreuse **Helm Chart version**, you use, share `major.minor` otherwise Chartreuse won't start. +- Please make sure Chartreuse Python **Package version** and Chartreuse **Helm Chart version**, you use, share the same major, and that the Package minor is greater or equal to the Helm Chart minor, otherwise Chartreuse won't start. ## Configuration diff --git a/VERSION b/VERSION index a3fcc71..21c8c7b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0 +7.1.1 diff --git a/src/chartreuse/chartreuse_upgrade.py b/src/chartreuse/chartreuse_upgrade.py index 81f5716..90947fa 100755 --- a/src/chartreuse/chartreuse_upgrade.py +++ b/src/chartreuse/chartreuse_upgrade.py @@ -1,5 +1,6 @@ import logging import os +import re from chartreuse import get_version @@ -10,14 +11,20 @@ logger = logging.getLogger(__name__) +def _get_major_minor(version: str) -> tuple[str, int]: + """Return ("1", 2) for "1.2.3". A missing minor ("1", "1.") counts as 0.""" + major, _, rest = version.partition(".") + minor_digits = re.match(r"\d*", rest) + return major, int(minor_digits.group()) if minor_digits and minor_digits.group() else 0 + + def ensure_safe_run() -> None: """ - The compatibility between the Chartreuse package and Chartreuse Helm Chart is only ensured for - versions with the same "major.minor". + The Chartreuse package supports a Helm Chart appVersion with the same major and a minor lower or + equal to its own. A minor release of the package keeps the Helm Chart contract, but a newer Helm + Chart can use options that an older package does not know. """ package_v: str = get_version() - # Get "1.2" from "1.2.3" - package_v_major_minor: list[str] = package_v.split(".", 2)[:2] helm_chart_v: str = os.getenv("HELM_CHART_APP_VERSION", "") if not helm_chart_v: @@ -25,12 +32,14 @@ def ensure_safe_run() -> None: "Couldn't get the Chartreuse's Helm Chart appVersion from the env var HELM_CHART_APP_VERSION," " couldn't make sure that the package is of a compatible version, ABORTING!" ) - helm_chart_v_major_minor: list[str] = helm_chart_v.split(".", 2)[:2] - if helm_chart_v_major_minor != package_v_major_minor: + + package_major, package_minor = _get_major_minor(package_v) + helm_chart_major, helm_chart_minor = _get_major_minor(helm_chart_v) + if helm_chart_major != package_major or package_minor < helm_chart_minor: raise ValueError( f"Chartreuse's Helm Chart version '{helm_chart_v}' and the package's version '{package_v}' " - f"don't have the same 'major.minor' ({helm_chart_v_major_minor} != {package_v_major_minor})," - " they may be incompatible. Make sure they're semver, align them and retry, ABORTING!" + "are incompatible: the package needs the same major and a minor greater or equal to the" + " Helm Chart's. Align them and retry, ABORTING!" ) diff --git a/src/chartreuse/tests/unit_tests/test_chartreuse_restore.py b/src/chartreuse/tests/unit_tests/test_chartreuse_restore.py index 085b920..423d25d 100644 --- a/src/chartreuse/tests/unit_tests/test_chartreuse_restore.py +++ b/src/chartreuse/tests/unit_tests/test_chartreuse_restore.py @@ -32,10 +32,10 @@ def test_chartreuse_restore_restores_stopped_pods(mocker: MockerFixture) -> None def test_chartreuse_restore_incompatible_helm_chart_version(mocker: MockerFixture) -> None: """ Test that chartreuse_restore aborts before touching anything when the package and the - Helm Chart don't have the same major.minor. + Helm Chart don't have the same major. """ mocked_kdm = _configure_deployment_manager_mock(mocker) - mocker.patch("chartreuse.chartreuse_upgrade.get_version", return_value="5.1.0") + mocker.patch("chartreuse.chartreuse_upgrade.get_version", return_value="6.0.0") configure_os_environ_mock(mocker=mocker, additional_environment={"HELM_CHART_APP_VERSION": "5.0.0"}) with pytest.raises(ValueError): diff --git a/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade.py b/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade.py index d3df7d7..a1bfe77 100644 --- a/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade.py +++ b/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade.py @@ -70,13 +70,17 @@ def test_chartreuse_upgrade_no_migration_disabled_stop_pods( ("1.32.1", "1.32.21-dev.12", False), ("12.32.1", "12.32.33.dev.yes.we.can", False), ("2.0.0", "", True), - ("2.0.0", "2.2.3", True), + ("2.0.0", "2.2.3", False), ("2.2.0", "1.8.5", True), ("2.0", "2.0.3.344", False), ("2.0.", "2.0.3.344", False), ("2.0.", "2.0", False), - ("2", "2.0", True), - ("2", "2.0.0", True), + ("2", "2.0", False), + ("2", "2.0.0", False), + ("3.0.0", "2.9.9", True), + ("2.3.0", "2.2.9", True), + ("2.10.0", "2.9.0", True), + ("2.9.0", "2.10.0", False), ], ) def test_chartreuse_upgrade_compatibility_check( diff --git a/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade_extended.py b/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade_extended.py index 3ced5c5..76fcc31 100644 --- a/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade_extended.py +++ b/src/chartreuse/tests/unit_tests/test_chartreuse_upgrade_extended.py @@ -12,24 +12,32 @@ class TestEnsureSafeRun: """Test cases for ensure_safe_run function.""" def test_ensure_safe_run_matching_versions(self, mocker: MockerFixture) -> None: - """Test ensure_safe_run with matching major.minor versions.""" + """Test ensure_safe_run with matching major versions.""" mocker.patch("chartreuse.chartreuse_upgrade.get_version", return_value="5.2.1") mocker.patch.dict(os.environ, {"HELM_CHART_APP_VERSION": "5.2.0"}) # Should not raise any exception ensure_safe_run() - def test_ensure_safe_run_mismatched_versions(self, mocker: MockerFixture) -> None: - """Test ensure_safe_run with mismatched major.minor versions.""" - mocker.patch("chartreuse.chartreuse_upgrade.get_version", return_value="5.1.3") - mocker.patch.dict(os.environ, {"HELM_CHART_APP_VERSION": "5.2.0"}) + def test_ensure_safe_run_package_newer_minor(self, mocker: MockerFixture) -> None: + """Test ensure_safe_run accepts a package minor newer than the Helm Chart minor.""" + mocker.patch("chartreuse.chartreuse_upgrade.get_version", return_value="7.1.0") + mocker.patch.dict(os.environ, {"HELM_CHART_APP_VERSION": "7.0.0"}) + + # Should not raise any exception + ensure_safe_run() + + def test_ensure_safe_run_helm_chart_newer_minor(self, mocker: MockerFixture) -> None: + """Test ensure_safe_run refuses a Helm Chart minor newer than the package minor.""" + mocker.patch("chartreuse.chartreuse_upgrade.get_version", return_value="7.0.2") + mocker.patch.dict(os.environ, {"HELM_CHART_APP_VERSION": "7.1.0"}) with pytest.raises(ValueError) as exc_info: ensure_safe_run() - assert "don't have the same 'major.minor'" in str(exc_info.value) - assert "5.1.3" in str(exc_info.value) - assert "5.2.0" in str(exc_info.value) + assert "are incompatible" in str(exc_info.value) + assert "7.0.2" in str(exc_info.value) + assert "7.1.0" in str(exc_info.value) def test_ensure_safe_run_missing_helm_version(self, mocker: MockerFixture) -> None: """Test ensure_safe_run with missing HELM_CHART_APP_VERSION environment variable.""" @@ -59,7 +67,9 @@ def test_ensure_safe_run_different_major_versions(self, mocker: MockerFixture) - with pytest.raises(ValueError) as exc_info: ensure_safe_run() - assert "(['5', '2'] != ['4', '2'])" in str(exc_info.value) + assert "are incompatible" in str(exc_info.value) + assert "4.2.1" in str(exc_info.value) + assert "5.2.0" in str(exc_info.value) def test_ensure_safe_run_complex_version_formats(self, mocker: MockerFixture) -> None: """Test ensure_safe_run with complex version formats."""