ci: Parse Cargo.toml and Cargo.lock with Python script to determine affected packages (#48389)

Piotr Osiewicz created

Closes #ISSUE

Release Notes:

- N/A

Change summary

.github/workflows/run_tests.yml                | 14 +-
crates/agent_ui/src/agent_ui.rs                |  1 
crates/collab_ui/src/collab_ui.rs              |  1 
crates/debugger_ui/src/debugger_ui.rs          |  1 
script/diff-cargo-deps                         | 93 ++++++++++++++++++++
tooling/xtask/src/tasks/workflows/run_tests.rs | 14 +-
6 files changed, 108 insertions(+), 16 deletions(-)

Detailed changes

.github/workflows/run_tests.yml 🔗

@@ -65,17 +65,15 @@ jobs:
             FILE_CHANGED_PKGS=$(printf '%s\n%s\n%s\n%s' "$FILE_CHANGED_PKGS" "settings" "storybook" "assets" | sort -u)
           fi
 
-          # Parse Cargo.lock diff for added/changed crates
-          LOCK_CHANGED_PKGS=""
-          if echo "$CHANGED_FILES" | grep -qP '^Cargo\.lock$'; then
-            echo "Cargo.lock changed, analyzing diff..."
-            LOCK_CHANGED_PKGS=$(git diff "$COMPARE_REV" "${{ github.sha }}" -- Cargo.lock | \
-              grep -oP '^[+-]name = "\K[^"]+' | \
-              sort -u || true)
+          # Parse Cargo.toml and Cargo.lock for changed dependencies
+          CARGO_CHANGED_DEPS=""
+          if echo "$CHANGED_FILES" | grep -qP '^Cargo\.(toml|lock)$'; then
+            echo "Cargo files changed, analyzing..."
+            CARGO_CHANGED_DEPS=$(./script/diff-cargo-deps "$COMPARE_REV" || true)
           fi
 
           # Combine all changed packages
-          ALL_CHANGED_PKGS=$(printf '%s\n%s' "$FILE_CHANGED_PKGS" "$LOCK_CHANGED_PKGS" | sort -u | grep -v '^$' || true)
+          ALL_CHANGED_PKGS=$(printf '%s\n%s' "$FILE_CHANGED_PKGS" "$CARGO_CHANGED_DEPS" | sort -u | grep -v '^$' || true)
 
           if [ -z "$ALL_CHANGED_PKGS" ]; then
             echo "No package changes detected, will run all tests"

crates/agent_ui/src/agent_ui.rs 🔗

@@ -25,6 +25,7 @@ mod ui;
 use std::rc::Rc;
 use std::sync::Arc;
 
+// Another comment
 use agent_settings::{AgentProfileId, AgentSettings};
 use assistant_slash_command::SlashCommandRegistry;
 use client::Client;

crates/collab_ui/src/collab_ui.rs 🔗

@@ -16,6 +16,7 @@ use release_channel::ReleaseChannel;
 use ui::px;
 use workspace::AppState;
 
+// Another comment, nice.
 pub fn init(app_state: &Arc<AppState>, cx: &mut App) {
     channel_view::init(cx);
     collab_panel::init(cx);

script/diff-cargo-deps 🔗

@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+"""
+Diff workspace dependencies between two git revisions.
+
+Compares both Cargo.toml [workspace.dependencies] and Cargo.lock [[package]]
+entries to find which dependencies have changed.
+
+Usage: diff-cargo-deps <old-git-ref>
+
+Outputs one dependency name per line for any deps that were added, removed,
+or modified (version, features, checksum, etc).
+"""
+
+import subprocess
+import sys
+import tomllib
+
+
+def get_file_at_ref(ref: str, path: str) -> str:
+    """Get file contents at a specific git ref."""
+    result = subprocess.run(
+        ["git", "show", f"{ref}:{path}"],
+        capture_output=True,
+        text=True,
+    )
+    return result.stdout if result.returncode == 0 else ""
+
+
+def get_workspace_deps(toml_content: str) -> dict:
+    """Extract [workspace.dependencies] from Cargo.toml content."""
+    if not toml_content:
+        return {}
+    try:
+        data = tomllib.loads(toml_content)
+        return data.get("workspace", {}).get("dependencies", {})
+    except Exception:
+        return {}
+
+
+def get_lock_packages(lock_content: str) -> dict[str, dict]:
+    """Extract [[package]] entries from Cargo.lock as {name: full_entry}."""
+    if not lock_content:
+        return {}
+    try:
+        data = tomllib.loads(lock_content)
+        return {pkg["name"]: pkg for pkg in data.get("package", [])}
+    except Exception:
+        return {}
+
+
+def diff_dicts(old: dict, new: dict) -> set[str]:
+    """Find keys that were added, removed, or whose values changed."""
+    changed = set()
+
+    for name, value in new.items():
+        if name not in old or old[name] != value:
+            changed.add(name)
+
+    for name in old:
+        if name not in new:
+            changed.add(name)
+
+    return changed
+
+
+def main():
+    if len(sys.argv) < 2:
+        print("Usage: diff-cargo-deps <old-git-ref>", file=sys.stderr)
+        sys.exit(1)
+
+    old_ref = sys.argv[1]
+    changed = set()
+
+    # Diff Cargo.toml workspace dependencies
+    old_toml = get_file_at_ref(old_ref, "Cargo.toml")
+    new_toml = open("Cargo.toml").read()
+    old_deps = get_workspace_deps(old_toml)
+    new_deps = get_workspace_deps(new_toml)
+    changed |= diff_dicts(old_deps, new_deps)
+
+    # Diff Cargo.lock packages
+    old_lock = get_file_at_ref(old_ref, "Cargo.lock")
+    new_lock = open("Cargo.lock").read()
+    old_pkgs = get_lock_packages(old_lock)
+    new_pkgs = get_lock_packages(new_lock)
+    changed |= diff_dicts(old_pkgs, new_pkgs)
+
+    for name in sorted(changed):
+        print(name)
+
+
+if __name__ == "__main__":
+    main()

tooling/xtask/src/tasks/workflows/run_tests.rs 🔗

@@ -173,17 +173,15 @@ fn orchestrate_impl(rules: &[&PathCondition], include_package_filter: bool) -> N
             FILE_CHANGED_PKGS=$(printf '%s\n%s\n%s\n%s' "$FILE_CHANGED_PKGS" "settings" "storybook" "assets" | sort -u)
           fi
 
-          # Parse Cargo.lock diff for added/changed crates
-          LOCK_CHANGED_PKGS=""
-          if echo "$CHANGED_FILES" | grep -qP '^Cargo\.lock$'; then
-            echo "Cargo.lock changed, analyzing diff..."
-            LOCK_CHANGED_PKGS=$(git diff "$COMPARE_REV" "${{ github.sha }}" -- Cargo.lock | \
-              grep -oP '^[+-]name = "\K[^"]+' | \
-              sort -u || true)
+          # Parse Cargo.toml and Cargo.lock for changed dependencies
+          CARGO_CHANGED_DEPS=""
+          if echo "$CHANGED_FILES" | grep -qP '^Cargo\.(toml|lock)$'; then
+            echo "Cargo files changed, analyzing..."
+            CARGO_CHANGED_DEPS=$(./script/diff-cargo-deps "$COMPARE_REV" || true)
           fi
 
           # Combine all changed packages
-          ALL_CHANGED_PKGS=$(printf '%s\n%s' "$FILE_CHANGED_PKGS" "$LOCK_CHANGED_PKGS" | sort -u | grep -v '^$' || true)
+          ALL_CHANGED_PKGS=$(printf '%s\n%s' "$FILE_CHANGED_PKGS" "$CARGO_CHANGED_DEPS" | sort -u | grep -v '^$' || true)
 
           if [ -z "$ALL_CHANGED_PKGS" ]; then
             echo "No package changes detected, will run all tests"