diff --git a/vinca/distro.py b/vinca/distro.py index 1b23bb0..cdb3420 100644 --- a/vinca/distro.py +++ b/vinca/distro.py @@ -148,6 +148,7 @@ def __init__( self._last_archive = None self._depends_cache = {} self._direct_depends_cache = {} + self._warned_snapshot_without_dependencies = False os.environ["ROS_VERSION"] = "1" if self.check_ros1() else "2" @@ -205,14 +206,21 @@ def _get_direct_depends(self, pkg: str) -> set[str]: additional_packages_snapshot = self.additional_packages_snapshot or {} is_additional_package = pkg in additional_packages_snapshot if snapshot_info is not None and not is_additional_package: - if "dependencies" not in snapshot_info: - raise RuntimeError( - f"Snapshot metadata for '{pkg}' has no dependencies; " - "regenerate the rosdistro snapshot" + if "dependencies" in snapshot_info: + direct = set(snapshot_info["dependencies"] or []) + self._direct_depends_cache[pkg] = set(direct) + return direct + # Snapshots written before dependencies were recorded: resolve them + # from the rosdistro cache, as vinca-snapshot itself does, so such a + # snapshot keeps working until it is regenerated. + if not getattr(self, "_warned_snapshot_without_dependencies", False): + print( + "Warning: the rosdistro snapshot has no dependency metadata " + f"(e.g. for '{pkg}'); resolving dependencies from the rosdistro " + "cache instead. Regenerate the snapshot with vinca-snapshot to " + "pin them." ) - direct = set(snapshot_info["dependencies"] or []) - self._direct_depends_cache[pkg] = set(direct) - return direct + self._warned_snapshot_without_dependencies = True # if pkg comes from additional_packages_snapshot, extract from its package.xml if is_additional_package: diff --git a/vinca/test_snapshot_metadata.py b/vinca/test_snapshot_metadata.py index e616572..4df7d08 100644 --- a/vinca/test_snapshot_metadata.py +++ b/vinca/test_snapshot_metadata.py @@ -1,8 +1,6 @@ from typing import Any from unittest.mock import Mock, patch -import pytest - import vinca.main as main import vinca.recipes as recipes from vinca.distro import Distro @@ -154,12 +152,20 @@ def test_snapshot_package_xml_does_not_use_live_cache_after_snapshot_change( distro._distro.get_release_package_xml.assert_not_called() -def test_snapshot_without_dependencies_requires_regeneration(monkeypatch): +def test_snapshot_without_dependencies_falls_back_to_rosdistro_cache( + monkeypatch, capsys +): distro = make_snapshot_distro(monkeypatch) del distro.snapshot["snapshot_package"]["dependencies"] + distro._walker = Mock() + distro._walker.get_depends.side_effect = ( + lambda pkg, dependency_type, ros_packages_only: ( + {"cache_dependency"} if dependency_type == "build" else set() + ) + ) - with pytest.raises(RuntimeError, match="regenerate the rosdistro snapshot"): - distro.get_depends("snapshot_package") + assert distro.get_direct_depends("snapshot_package") == {"cache_dependency"} + assert "Regenerate the snapshot" in capsys.readouterr().out def test_snapshot_metadata_generates_dependency_required_by_pinned_source(