diff --git a/duckdb_packaging/setuptools_scm_version.py b/duckdb_packaging/setuptools_scm_version.py index 51c3f01a..4891b12a 100644 --- a/duckdb_packaging/setuptools_scm_version.py +++ b/duckdb_packaging/setuptools_scm_version.py @@ -6,6 +6,7 @@ import os import re +from datetime import datetime, timezone from typing import Protocol # Import from our own versioning module to avoid duplication @@ -72,15 +73,19 @@ def _bump_dev_version(base_version: str, distance: int) -> str: raise ValueError(msg) major, minor, patch, post, rc = parse_version(base_version) + # Use date-based versioning for dev builds (YYYYMMDD format) + # This allows daily nightlies even without code changes + date_suffix = datetime.now(timezone.utc).strftime("%Y%m%d") + if post != 0: # We're developing on top of a post-release - return f"{format_version(major, minor, patch, post=post + 1)}.dev{distance}" + return f"{format_version(major, minor, patch, post=post + 1)}.dev{date_suffix}" elif rc != 0: # We're developing on top of an rc - return f"{format_version(major, minor, patch, rc=rc + 1)}.dev{distance}" + return f"{format_version(major, minor, patch, rc=rc + 1)}.dev{date_suffix}" elif _main_branch_versioning(): - return f"{format_version(major, minor + 1, 0)}.dev{distance}" - return f"{format_version(major, minor, patch + 1)}.dev{distance}" + return f"{format_version(major, minor + 1, 0)}.dev{date_suffix}" + return f"{format_version(major, minor, patch + 1)}.dev{date_suffix}" def forced_version_from_env() -> str: diff --git a/tests/fast/test_versioning.py b/tests/fast/test_versioning.py index d21052c8..64003e11 100644 --- a/tests/fast/test_versioning.py +++ b/tests/fast/test_versioning.py @@ -3,6 +3,7 @@ import os import subprocess import unittest +from datetime import datetime, timezone from unittest.mock import MagicMock, patch import pytest @@ -108,6 +109,17 @@ def test_roundtrip_conversion(self): class TestSetupToolsScmIntegration(unittest.TestCase): """Test setuptools_scm integration functions.""" + def _verify_date_suffix(self, version_string: str) -> None: + """Helper to verify the date suffix in a dev version is today's date.""" + date_part = version_string.split(".dev")[1] + assert len(date_part) == 8, f"Date part should be 8 digits, got {len(date_part)}" + assert date_part.isdigit(), f"Date part should be all digits, got {date_part}" + + # Parse the date and verify it's today + parsed_date = datetime.strptime(date_part, "%Y%m%d").date() + today = datetime.now(timezone.utc).date() + assert parsed_date == today, f"Date {parsed_date} should be today {today}" + def test_bump_version_exact_tag(self): """Test bump_version with exact tag (distance=0, dirty=False).""" assert _tag_to_version("1.2.3") == "1.2.3" @@ -116,15 +128,21 @@ def test_bump_version_exact_tag(self): @patch.dict("os.environ", {"MAIN_BRANCH_VERSIONING": "1"}) def test_bump_version_with_distance(self): """Test bump_version with distance from tag.""" - assert _bump_dev_version("1.2.3", 5) == "1.3.0.dev5" + result = _bump_dev_version("1.2.3", 5) + assert result.startswith("1.3.0.dev") + self._verify_date_suffix(result) # Post-release development - assert _bump_dev_version("1.2.3.post1", 3) == "1.2.3.post2.dev3" + result = _bump_dev_version("1.2.3.post1", 3) + assert result.startswith("1.2.3.post2.dev") + self._verify_date_suffix(result) @patch.dict("os.environ", {"MAIN_BRANCH_VERSIONING": "0"}) def test_bump_version_release_branch(self): """Test bump_version on bugfix branch.""" - assert _bump_dev_version("1.2.3", 5) == "1.2.4.dev5" + result = _bump_dev_version("1.2.3", 5) + assert result.startswith("1.2.4.dev") + self._verify_date_suffix(result) @patch.dict("os.environ", {"MAIN_BRANCH_VERSIONING": "1"}) def test_bump_version_dirty(self): @@ -142,7 +160,8 @@ def test_version_scheme_function(self): mock_version.dirty = False result = version_scheme(mock_version) - assert result == "1.3.0.dev5" + assert result.startswith("1.3.0.dev") + self._verify_date_suffix(result) def test_bump_version_invalid_format(self): """Test bump_version with invalid version format."""