Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ A base configuration can itself extend another one. Paths (`patch_dir`,
`rosdistro_snapshot`, `rosdistro_additional_recipes`, `conda_index` files) are relative
to the file that sets them.

## Package tests

`tests/<package>.yaml` (next to `vinca.yaml`, or next to a configuration it extends)
becomes the `tests` section of the package's recipe. Selectors that test `ros_distro`
are resolved by vinca, so one test file can serve several distributions; all other
selectors are left to rattler-build:

```yaml
tests:
- script:
- if: ros_distro in ["humble", "jazzy"]
then:
- if: osx
then: export ROS_LOCALHOST_ONLY=1 && launch_test smoke_test.py
else:
- launch_test smoke_test.py
```

## Package naming

The optional `package_name_mode` setting controls the transition from legacy distro-qualified names such as `ros-humble-rclcpp` to ROS-major-version names such as `ros2-rclcpp`:
Expand Down
7 changes: 5 additions & 2 deletions vinca/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
source_reference,
)
from .template import write_recipe, write_recipe_package
from .v1_selectors import evaluate_distro_selectors

unsatisfied_deps = set()
distro = None
Expand Down Expand Up @@ -499,8 +500,10 @@ def parse_package(pkg, distro, vinca_conf, path):

if test := vinca_conf.get("_tests", {}).get(final_name):
# parse as yaml
text = test.read_text()
test_content = ruamel.yaml.safe_load(text)
text = test.read_text(encoding="utf-8")
test_content = evaluate_distro_selectors(
ruamel.yaml.safe_load(text), ros_distro=config.ros_distro
)
recipe["test"] = test_content

for p in pkg["authors"]:
Expand Down
7 changes: 5 additions & 2 deletions vinca/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
get_pkg_additional_info,
get_pkg_build_number,
)
from vinca.v1_selectors import evaluate_distro_selectors
from vinca.variants import VariantsMode, get_recipe_variants, get_variants_mode

TEMPLATE = """\
Expand Down Expand Up @@ -146,8 +147,10 @@ def write_recipe(source, outputs, vinca_conf, distro, single_file=True):
test := vinca_conf["_tests"].get(package_name)
):
print("Using test: ", test)
text = test.read_text()
test_content = yaml.safe_load(text)
text = test.read_text(encoding="utf-8")
test_content = evaluate_distro_selectors(
yaml.safe_load(text), ros_distro=config.ros_distro
)
meta["tests"] = test_content["tests"]

recipe_dir = (Path("recipes") / package_name).absolute()
Expand Down
32 changes: 32 additions & 0 deletions vinca/test_distro_selectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from vinca.v1_selectors import evaluate_distro_selectors

TEST = {
"tests": [
{
"script": [
{
"if": 'ros_distro in ["humble", "jazzy"]',
"then": [{"if": "osx", "then": "old-osx"}],
"else": [{"if": "osx", "then": "new-osx"}],
},
{"if": 'ros_distro == "humble"', "then": ["humble-only"]},
{"if": "linux", "then": "linux-cmd"},
]
}
]
}


def test_distro_selectors_are_resolved_platform_ones_kept():
humble = evaluate_distro_selectors(TEST, ros_distro="humble")
assert humble["tests"][0]["script"] == [
{"if": "osx", "then": "old-osx"},
"humble-only",
{"if": "linux", "then": "linux-cmd"},
]

rolling = evaluate_distro_selectors(TEST, ros_distro="rolling")
assert rolling["tests"][0]["script"] == [
{"if": "osx", "then": "new-osx"},
{"if": "linux", "then": "linux-cmd"},
]
41 changes: 41 additions & 0 deletions vinca/v1_selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,44 @@ def evaluate_selectors(
ctx = _platform_flags(target_platform)

return _process_node(copy.deepcopy(data), ctx)


def _process_distro_node(node: Any, ctx: Mapping[str, Any]) -> Any | None:
"""Like _process_node, but only for selectors that mention ``ros_distro``;
other selectors (e.g. platform ones) are left for rattler-build."""
if (
isinstance(node, Mapping)
and "if" in node
and "then" in node
and "ros_distro" in str(node["if"])
):
branch = "then" if _eval_condition(str(node["if"]), ctx) else "else"
if branch in node:
return _process_distro_node(node[branch], ctx)
return None
if isinstance(node, MutableMapping):
mapping_out: dict[str, Any] = {}
for k, v in node.items():
new = _process_distro_node(v, ctx)
if new is not None:
mapping_out[k] = new
return mapping_out
if isinstance(node, Sequence) and not isinstance(node, (str, bytes)):
sequence_out: list[Any] = []
for item in node:
new = _process_distro_node(item, ctx)
if new is None:
continue
if isinstance(new, list):
sequence_out.extend(new)
else:
sequence_out.append(new)
return sequence_out
return node


def evaluate_distro_selectors(data: Any, *, ros_distro: str | None) -> Any:
"""Return *data* with the selectors that test ``ros_distro`` evaluated, e.g.
``- if: ros_distro in ["humble", "jazzy"]``, so one test file can serve several
distributions. Selectors without ``ros_distro`` are kept unchanged."""
return _process_distro_node(copy.deepcopy(data), {"ros_distro": ros_distro})
Loading