Repository navigation
ci: select isolated feature builds for edited crates #7433
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0f64afc
7d953d5
d1d5ac3
b84028c
131679f
db3de99
d80ff15
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,6 +140,13 @@ shapes_want() { | |
| [[ "$shape_packages" == ALL ]] || grep -qwE "$1" <<<"$shape_names" | ||
| } | ||
|
|
||
| # True when the diff edits the crate's own compiled code or build definition, | ||
| # so its isolated permutations run too. A dependency edit keeps only its | ||
| # consumers' boundary checks; shared build inputs select every owner's. | ||
| changed() { | ||
| [[ "$packages" == ALL ]] || grep -qE "^rs/$1/(src/|tests/|examples/|Cargo\\.toml$|build\\.rs$)" "$list" | ||
| } | ||
|
|
||
| case "$packages" in | ||
| "") | ||
| echo "rs: no crates affected; skipping." | ||
|
|
@@ -183,8 +190,18 @@ case "$action" in | |
| if wants '(kio|moq-net)'; then just rs loom-check; fi | ||
| if shapes_want moq-net; then just rs net-features; fi | ||
| if shapes_want moq-quic; then just rs quic-features; fi | ||
| if shapes_want moq-tokio; then just rs tokio-features; fi | ||
| if shapes_want '(moq-video|moq-audio|moq-transcode)'; then just rs media-features; fi | ||
| if shapes_want moq-tokio; then | ||
| isolated=() | ||
|
Comment on lines
+193
to
+194
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if changed moq-tokio; then isolated=(--all); fi | ||
| just rs tokio-features ${isolated[@]+"${isolated[@]}"} | ||
| fi | ||
| if shapes_want '(moq-video|moq-audio|moq-transcode)'; then | ||
| isolated=() | ||
| for crate in moq-video moq-audio moq-transcode; do | ||
| if changed "$crate"; then isolated+=(--package "$crate"); fi | ||
| done | ||
| just rs media-features ${isolated[@]+"${isolated[@]}"} | ||
| fi | ||
| # moq-wasm's crate root is `#![cfg(target_arch = "wasm32")]`, so only a | ||
| # wasm32 pass checks it. moq-mux, moq-ffi, and moq-clock ride along in that pass, and | ||
| # nothing depends on moq-ffi, so each has to reach it on its own. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,16 +75,37 @@ def test_queue_checks_selected_feature_shapes(self): | |
| self.assertIn("rs feature-shapes --workspace --exclude moq-net-fuzz --exclude moq-quic-fuzz", calls) | ||
|
|
||
| def test_boundary_checks_follow_reverse_dependencies(self): | ||
| self.assertIn("rs tokio-features", self.selected("rs/moq-tokio/src/lib.rs")) | ||
| self.assertIn("rs tokio-features", self.selected("rs/moq-sock/src/lib.rs")) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Declining: this is already an exact check. (Written by Claude Opus 5.5) |
||
| self.assertIn("rs net-features", self.selected("rs/moq-net/src/lib.rs")) | ||
| self.assertIn("rs quic-features", self.selected("rs/moq-quic/src/lib.rs")) | ||
| self.assertIn("rs media-features", self.selected("rs/moq-video/src/lib.rs")) | ||
| self.assertIn("rs media-features", self.selected("rs/moq-mux/src/lib.rs")) | ||
| calls = self.selected("rs/kio/src/lib.rs") | ||
| for owner in ("tokio", "net", "quic", "media"): | ||
| self.assertIn(f"rs {owner}-features", calls) | ||
| calls = self.selected("rs/moq-stats/benches/producer_tick.rs") | ||
| self.assertFalse(any(call.endswith("-features") for call in calls), calls) | ||
|
|
||
| def test_isolated_shapes_follow_their_own_source_and_manifest(self): | ||
| for scope in (None, "changed"): | ||
| for action in ("check", "check-test", "lint"): | ||
| for owner in ("moq-tokio", "moq-video", "moq-audio", "moq-transcode"): | ||
| for source in ("src/added.rs", "tests/added.rs", "examples/added.rs", "Cargo.toml", "build.rs"): | ||
| with self.subTest(scope=scope, action=action, owner=owner, source=source): | ||
| calls = self.selected(f"rs/{owner}/{source}", action=action, feature_scope=scope) | ||
| expected = ("rs tokio-features --all" if owner == "moq-tokio" | ||
| else f"rs media-features --package {owner}") | ||
| self.assertIn(expected, calls) | ||
| calls = self.selected("rs/moq-video/src/added.rs", "rs/moq-audio/src/added.rs") | ||
| self.assertIn("rs media-features --package moq-video --package moq-audio", calls) | ||
| # Shared build inputs isolate every owner, still without Nightly's | ||
| # bench and example targets. | ||
| calls = self.selected("Cargo.lock") | ||
| self.assertIn("rs tokio-features --all", calls) | ||
| self.assertIn("rs media-features --package moq-video --package moq-audio --package moq-transcode", calls) | ||
| for path in ("rs/moq-video/benches/added.rs", "rs/moq-tokio/README.md"): | ||
| calls = self.selected(path) | ||
| self.assertFalse(any("-features --" in call for call in calls), calls) | ||
|
|
||
| def test_local_feature_contracts_skip_dependents(self): | ||
| # `just check` lints feature shapes for the changed crates alone; their | ||
| # dependents keep the default-feature lint and the landing worker's shapes. | ||
|
|
@@ -97,12 +118,14 @@ def test_local_feature_contracts_skip_dependents(self): | |
| calls = self.selected("rs/kio/src/lib.rs", feature_scope="changed") | ||
| self.assertTrue(any("/rs/moq-net#" in call for call in calls if call.startswith("rs lint ")), calls) | ||
| self.assertIn("rs loom-check", calls) | ||
| self.assertFalse(any(call.endswith("-features") for call in calls), calls) | ||
| for path, owner in (("rs/moq-net/src/lib.rs", "net"), ("rs/moq-tokio/src/lib.rs", "tokio"), | ||
| ("rs/moq-quic/src/lib.rs", "quic"), ("rs/moq-video/src/lib.rs", "media")): | ||
| self.assertFalse(any("-features" in call for call in calls), calls) | ||
| for path, expected in (("rs/moq-net/src/lib.rs", "rs net-features"), | ||
| ("rs/moq-tokio/src/lib.rs", "rs tokio-features --all"), | ||
| ("rs/moq-quic/src/lib.rs", "rs quic-features"), | ||
| ("rs/moq-video/src/lib.rs", "rs media-features --package moq-video")): | ||
| with self.subTest(path=path): | ||
| calls = self.selected(path, feature_scope="changed") | ||
| self.assertEqual([call for call in calls if call.endswith("-features")], [f"rs {owner}-features"]) | ||
| self.assertEqual([call for call in calls if "-features" in call], [expected]) | ||
| # Inputs every crate shares still select the workspace. | ||
| self.assertIn("rs feature-shapes --workspace --exclude moq-net-fuzz --exclude moq-quic-fuzz", | ||
| self.selected("Cargo.lock", action="feature-shapes", feature_scope="changed")) | ||
|
|
@@ -291,7 +314,7 @@ def trace(self, owner, *args, net_features="", expected_returncode=0): | |
| result = subprocess.run(["bash", f"sh/rs/{owner}-features.sh", *args], | ||
| cwd=ROOT, env=env, capture_output=True, text=True) | ||
| self.assertEqual(result.returncode, expected_returncode, result.stdout + result.stderr) | ||
| return calls.read_text().splitlines() | ||
| return calls.read_text().splitlines() if calls.exists() else [] | ||
|
|
||
| def test_net_keeps_model_and_browser_contracts(self): | ||
| boundary = self.trace("net") | ||
|
|
@@ -331,6 +354,26 @@ def test_media_keeps_dependency_contracts_and_vpx_tests(self): | |
| self.assertEqual([call for call in boundary if not call.startswith("check ")], | ||
| [call for call in exhaustive if not call.startswith("check ")]) | ||
|
|
||
| def test_media_isolates_only_the_edited_owners(self): | ||
| boundary = self.trace("media") | ||
| exhaustive = self.trace("media", "--all") | ||
| for owners in (("moq-video",), ("moq-transcode",), ("moq-audio",), | ||
| ("moq-video", "moq-audio")): | ||
| with self.subTest(owners=owners): | ||
| calls = self.trace("media", *(arg for owner in owners for arg in ("--package", owner))) | ||
| # Keep all dependency contracts and tests, adding only the | ||
| # edited owners' checks without Nightly's bench/example targets. | ||
| expected = [call.replace(" --all-targets ", " --tests ") for call in exhaustive | ||
| if not call.startswith("check ") or | ||
| call.replace(" --all-targets ", " --tests ") in boundary or | ||
| any(f" -p {owner} " in call for owner in owners)] | ||
| self.assertEqual(calls, expected) | ||
|
|
||
| def test_media_rejects_unsupported_selections(self): | ||
| for args in (("--package",), ("--package", "moq-net"), ("--all", "--package", "moq-video")): | ||
| with self.subTest(args=args): | ||
| self.assertEqual(self.trace("media", *args, expected_returncode=2), []) | ||
|
|
||
|
|
||
| CARGO_STUB = r"""#!/usr/bin/env bash | ||
| printf '%s\n' "$*" >> "$FEATURE_CALLS" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: moq-dev/moq
Length of output: 11629
Script executed:
Repository: moq-dev/moq
Length of output: 15729
Script executed:
Repository: moq-dev/moq
Length of output: 8211
Guard the empty
isolatedarray in selector-backed checks.just rs checkdoes not invokesh/rs/select.shormedia-features.sh; it runs the direct clippy and lint recipes. The failure is reachable throughjust rs check-changed <list>when a media crate is selected only as a dependent. That selector can callmedia-features.shwithout package arguments. On Bash before 4.4,${isolated[*]}then can abort underset -u.Proposed fix
Committable suggestion