Skip to content

Commit 541d672

Browse files
feat(packagesettings): add ${__version__} template variable to env settings
Inject the resolved package version into template_env as __version__, so package env settings can reference it via ${__version__}. Closes: #946 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Lalatendu Mohanty <lmohanty@redhat.com>
1 parent 4667c98 commit 541d672

2 files changed

Lines changed: 152 additions & 0 deletions

File tree

src/fromager/packagesettings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,10 @@ def get_extra_environ(
899899
else:
900900
template_env = template_env.copy()
901901

902+
# make package version available as ${__version__} for substitution
903+
if version is not None:
904+
template_env["__version__"] = str(version)
905+
902906
# configure max jobs settings, settings depend on package, available
903907
# CPU cores, and available virtual memory.
904908
jobs = self.parallel_jobs()

tests/test_packagesettings.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
EnvVars,
1616
GitOptions,
1717
Package,
18+
PackageBuildInfo,
1819
PackageSettings,
1920
ResolverDist,
21+
Settings,
2022
SettingsFile,
2123
Variant,
2224
substitute_template,
@@ -805,3 +807,149 @@ def test_use_pypi_org_metadata(testdata_context: context.WorkContext) -> None:
805807
"somepackage_without_customization"
806808
)
807809
assert pbi.use_pypi_org_metadata
810+
811+
812+
class TestGetExtraEnvironVersion:
813+
"""Tests for ${__version__} template variable in env settings.
814+
815+
PackageBuildInfo.get_extra_environ() should make the package version
816+
available as ${__version__} for template substitution in env entries.
817+
"""
818+
819+
PARALLEL: typing.ClassVar[dict[str, str]] = {
820+
"CMAKE_BUILD_PARALLEL_LEVEL": "1",
821+
"MAKEFLAGS": "-j1",
822+
"MAX_JOBS": "1",
823+
}
824+
825+
def _make_pbi(self, env_yaml: str, tmp_path: pathlib.Path) -> PackageBuildInfo:
826+
"""Create a PackageBuildInfo with custom env settings."""
827+
ps = PackageSettings.from_string("version-test-pkg", env_yaml)
828+
settings = Settings(
829+
settings=SettingsFile(),
830+
package_settings=[ps],
831+
variant="cpu",
832+
patches_dir=tmp_path,
833+
max_jobs=1,
834+
)
835+
return settings.package_build_info("version-test-pkg")
836+
837+
def test_version_available_as_template_variable(
838+
self, tmp_path: pathlib.Path
839+
) -> None:
840+
"""Verify ${__version__} resolves to the package version."""
841+
pbi = self._make_pbi(
842+
"""
843+
env:
844+
MY_VERSION: "${__version__}"
845+
""",
846+
tmp_path,
847+
)
848+
result = pbi.get_extra_environ(template_env={}, version=Version("2.1.0"))
849+
assert result == {"MY_VERSION": "2.1.0"} | self.PARALLEL
850+
assert "__version__" not in result
851+
852+
def test_version_in_compound_expression(self, tmp_path: pathlib.Path) -> None:
853+
"""Verify ${__version__} works inside a larger string."""
854+
pbi = self._make_pbi(
855+
"""
856+
env:
857+
DOWNLOAD_URL: "https://example.com/releases/v${__version__}/lib.tar.gz"
858+
""",
859+
tmp_path,
860+
)
861+
result = pbi.get_extra_environ(template_env={}, version=Version("3.0.1"))
862+
assert (
863+
result
864+
== {
865+
"DOWNLOAD_URL": "https://example.com/releases/v3.0.1/lib.tar.gz",
866+
}
867+
| self.PARALLEL
868+
)
869+
assert "__version__" not in result
870+
871+
def test_version_none_with_default(self, tmp_path: pathlib.Path) -> None:
872+
"""Verify ${__version__:-unknown} falls back when version is None."""
873+
pbi = self._make_pbi(
874+
"""
875+
env:
876+
MY_VERSION: "${__version__:-unknown}"
877+
""",
878+
tmp_path,
879+
)
880+
result = pbi.get_extra_environ(template_env={}, version=None)
881+
assert result == {"MY_VERSION": "unknown"} | self.PARALLEL
882+
assert "__version__" not in result
883+
884+
def test_version_none_without_default_raises(self, tmp_path: pathlib.Path) -> None:
885+
"""Verify ${__version__} without default raises when version is None."""
886+
pbi = self._make_pbi(
887+
"""
888+
env:
889+
MY_VERSION: "${__version__}"
890+
""",
891+
tmp_path,
892+
)
893+
with pytest.raises(ValueError, match="__version__"):
894+
pbi.get_extra_environ(template_env={}, version=None)
895+
896+
def test_version_does_not_interfere_with_existing_vars(
897+
self, tmp_path: pathlib.Path
898+
) -> None:
899+
"""Verify existing template variables still work when version is provided."""
900+
pbi = self._make_pbi(
901+
"""
902+
env:
903+
JOBS: "${MAX_JOBS}"
904+
CUSTOM: "${MYVAR}"
905+
""",
906+
tmp_path,
907+
)
908+
result = pbi.get_extra_environ(
909+
template_env={"MYVAR": "hello"}, version=Version("1.0.0")
910+
)
911+
assert (
912+
result
913+
== {
914+
"JOBS": "1",
915+
"CUSTOM": "hello",
916+
}
917+
| self.PARALLEL
918+
)
919+
assert "__version__" not in result
920+
921+
def test_version_referenced_by_subsequent_env_entry(
922+
self, tmp_path: pathlib.Path
923+
) -> None:
924+
"""Verify env entries can chain off __version__."""
925+
pbi = self._make_pbi(
926+
"""
927+
env:
928+
PKG_VER: "${__version__}"
929+
PKG_TAG: "v${PKG_VER}"
930+
""",
931+
tmp_path,
932+
)
933+
result = pbi.get_extra_environ(template_env={}, version=Version("4.2.0"))
934+
assert (
935+
result
936+
== {
937+
"PKG_VER": "4.2.0",
938+
"PKG_TAG": "v4.2.0",
939+
}
940+
| self.PARALLEL
941+
)
942+
assert "__version__" not in result
943+
944+
def test_version_provided_but_not_referenced(self, tmp_path: pathlib.Path) -> None:
945+
"""Verify passing version when no env entries reference it is a no-op."""
946+
pbi = self._make_pbi(
947+
"""
948+
env:
949+
SOME_FLAG: "enabled"
950+
""",
951+
tmp_path,
952+
)
953+
result = pbi.get_extra_environ(template_env={}, version=Version("5.0.0"))
954+
assert result == {"SOME_FLAG": "enabled"} | self.PARALLEL
955+
assert "__version__" not in result

0 commit comments

Comments
 (0)