From a1a0dbbbad7b1097b1b49d6a68da0765948bb2ec Mon Sep 17 00:00:00 2001 From: RomainFloreani Date: Wed, 27 May 2026 09:15:58 -0400 Subject: [PATCH 1/4] Logic for auto-versioning in local conda recipes --- .github/workflows/python_deploy_dev.yml | 1 + pyproject.toml | 14 ++++++++------ recipe.yaml | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python_deploy_dev.yml b/.github/workflows/python_deploy_dev.yml index ad24eb9..7f4f264 100644 --- a/.github/workflows/python_deploy_dev.yml +++ b/.github/workflows/python_deploy_dev.yml @@ -19,6 +19,7 @@ jobs: source-repo-names: '["public-noremote-conda-dev"]' conda-channels: '["conda-forge"]' publish-repo-names: '["public-noremote-conda-dev"]' + build-experimental: true secrets: JFROG_ARTIFACTORY_URL: ${{ secrets.JFROG_ARTIFACTORY_URL }} JFROG_ARTIFACTORY_TOKEN: ${{ secrets.JFROG_ARTIFACTORY_TOKEN }} diff --git a/pyproject.toml b/pyproject.toml index 07c3f87..66e65e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,21 +118,23 @@ style = "pep440" vcs = "git" [tool.poetry-dynamic-versioning.substitution] -files = ["surface_apps/_version.py", "recipe.yaml"] +files = ["surface_apps/_version.py", "_version.json"] patterns = [ - { value = '''(^__version__\s*(?::.*?)?=\s*['"])[^'"]*(['"])''', mode = "str" }, - { value = '''(^\s*version\s*(?::.*?)?:\s*['"])[^'"]*(['"])''', mode = "str" }, + '''(^__version__\s*(?::.*?)?=\s*['"])[^'"]*(['"])''', + '''(^{\s*"version"\s*:\s*")[^"]*("\s*})''', ] [tool.poetry-dynamic-versioning.files."surface_apps/_version.py"] persistent-substitution = true initial-content = """ - # Version placeholder that will be replaced during substitution - __version__ = "0.0.0" + __version__ = "0.0.0.dev0" """ -[tool.poetry-dynamic-versioning.files."recipe.yaml"] +[tool.poetry-dynamic-versioning.files."_version.json"] persistent-substitution = true +initial-content = """ +{ "version": "0.0.0.dev0" } +""" [tool.conda-lock] platforms = ['win-64', 'linux-64'] diff --git a/recipe.yaml b/recipe.yaml index 2210a57..e2fd9cb 100644 --- a/recipe.yaml +++ b/recipe.yaml @@ -2,8 +2,9 @@ schema_version: 1 context: name: "surface-apps" - version: "0.0.0.dev0" # This will be replaced by the actual version in the build process - python_min: "3.10" + # Extract version from auto-generated _version.json + version: ${{ load_from_file("_version.json").version | trim }} + python_min: '3.10' package: name: ${{ name|lower }} From badf531a8b17a7af53e8ce7de61d8d007070c03a Mon Sep 17 00:00:00 2001 From: RomainFloreani Date: Wed, 27 May 2026 09:54:41 -0400 Subject: [PATCH 2/4] Logic for auto-versioning in local conda recipes --- .gitignore | 3 +- tests/version_test.py | 68 +++++++++++++++++-------------------------- 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index 63e0314..d2f5ff6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -# Byte-compiled / optimized / DLL files +# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class @@ -148,3 +148,4 @@ pyproject-sha.toml #version ignore surface_apps/_version.py +/_version.json diff --git a/tests/version_test.py b/tests/version_test.py index b4ef96c..25cbf47 100644 --- a/tests/version_test.py +++ b/tests/version_test.py @@ -1,4 +1,4 @@ -# '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +# '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' # Copyright (c) 2024-2026 Mira Geoscience Ltd. ' # ' # This file is part of surface-apps package. ' @@ -6,40 +6,35 @@ # surface-apps is distributed under the terms and conditions of the MIT License # (see LICENSE file at the root of this source code package). ' # '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - from __future__ import annotations import importlib +import json +import re from pathlib import Path import pytest import yaml -from jinja2 import Template -from packaging.version import InvalidVersion, Version +from packaging.version import Version import surface_apps -def get_conda_recipe_version(): - path = Path(__file__).resolve().parents[1] / "recipe.yaml" +def _get_json_version() -> str: + version_json_path = Path(__file__).resolve().parents[1] / "_version.json" + with version_json_path.open(encoding="utf-8") as file: + version_json = json.load(file) + return version_json["version"] - with open(str(path), encoding="utf-8") as file: - content = file.read() - template = Template(content) - rendered_yaml = template.render() - - recipe = yaml.safe_load(rendered_yaml) +def _get_conda_recipe_version_def() -> str: + recipe_path = Path(__file__).resolve().parents[1] / "recipe.yaml" + with recipe_path.open(encoding="utf-8") as file: + recipe = yaml.safe_load(file) return recipe["context"]["version"] -def test_version_is_consistent(): - project_version = Version(surface_apps.__version__) - conda_version = Version(get_conda_recipe_version()) - assert conda_version.base_version == project_version.base_version - - def _version_module_exists(): try: importlib.import_module("surface_apps._version") @@ -48,6 +43,16 @@ def _version_module_exists(): return False +def test_conda_recipe_version_loads_json(): + conda_version_def = _get_conda_recipe_version_def() + regex = ( + r"\$\{\{\s*load_from_file\(\s*['\"](_version\.json)['\"]\s*\)" + r"\s*\.version\b.*\}\}" + ) + regex_match = re.match(regex, conda_version_def) + assert regex_match is not None + + @pytest.mark.skipif( _version_module_exists(), reason="surface_apps._version can be found: package is built", @@ -65,28 +70,7 @@ def test_fallback_version_is_zero(): not _version_module_exists(), reason="surface_apps._version cannot be found: uses a fallback version", ) -def test_conda_version_is_consistent(): +def test_version_json_is_consistent(): project_version = Version(surface_apps.__version__) - conda_version = Version(get_conda_recipe_version()) - - assert conda_version.is_devrelease == project_version.is_devrelease - assert conda_version.is_prerelease == project_version.is_prerelease - assert conda_version.is_postrelease == project_version.is_postrelease - assert conda_version == project_version - - -def test_conda_version_is_pep440(): - version = Version(get_conda_recipe_version()) - assert version is not None - - -def validate_version(version_str): - try: - version = Version(version_str) - return (version.major, version.minor, version.micro, version.pre, version.post) - except InvalidVersion: - return None - - -def test_version_is_valid(): - assert validate_version(surface_apps.__version__) is not None + json_version = Version(_get_json_version()) + assert project_version == json_version \ No newline at end of file From 06f6f23428dcc95f8f90a08fe63d71c7da89ee11 Mon Sep 17 00:00:00 2001 From: RomainFloreani Date: Wed, 27 May 2026 10:34:04 -0400 Subject: [PATCH 3/4] Fixing whitespace --- tests/version_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/version_test.py b/tests/version_test.py index 25cbf47..e0f1a02 100644 --- a/tests/version_test.py +++ b/tests/version_test.py @@ -1,4 +1,4 @@ -# '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +# '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' # Copyright (c) 2024-2026 Mira Geoscience Ltd. ' # ' # This file is part of surface-apps package. ' @@ -73,4 +73,4 @@ def test_fallback_version_is_zero(): def test_version_json_is_consistent(): project_version = Version(surface_apps.__version__) json_version = Version(_get_json_version()) - assert project_version == json_version \ No newline at end of file + assert project_version == json_version From 8d2377d07a77616d0bf0e05ed003da1852d2bd4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Hensgen?= <24550538+sebhmg@users.noreply.github.com> Date: Wed, 27 May 2026 22:24:03 -0400 Subject: [PATCH 4/4] Apply suggestion from @sebhmg --- tests/version_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/version_test.py b/tests/version_test.py index e0f1a02..3903b31 100644 --- a/tests/version_test.py +++ b/tests/version_test.py @@ -6,6 +6,7 @@ # surface-apps is distributed under the terms and conditions of the MIT License # (see LICENSE file at the root of this source code package). ' # '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + from __future__ import annotations import importlib