Skip to content
Closed
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
82 changes: 82 additions & 0 deletions .github/scripts/check-duplicated-deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python3

"""
A script to check for duplicated dependencies across [dependencies] and [dev-dependencies]
in all Cargo.toml files in the workspace.

This is useful for CI to enforce clean separation between runtime and dev-only dependencies.

A duplicated dependency is one that appears in both sections with the exact same configuration,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you mean with "exact same configuration"? Features? Or Version / Path stuff?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of that, same features, same path, same version, basically if we consider the dependency as

name = value

with "exact same configuration" I mean "same value"

which is usually unnecessary and should be avoided.

# Example

```sh
python3 -m pip install toml
.github/scripts/check-duplicated-deps.py
"""

#!/usr/bin/env python3
import os
import sys

import toml


def find_cargo_toml_files(root='.'):
cargo_files = []
for dirpath, dirnames, filenames in os.walk(root):
if 'target' in dirnames:
dirnames.remove('target')
if 'Cargo.toml' in filenames:
cargo_files.append(os.path.join(dirpath, 'Cargo.toml'))
return cargo_files

def parse_dependencies(file_path):
try:
data = toml.load(file_path)
except Exception as e:
print(f"Error parsing {file_path}: {e}", file=sys.stderr)
return {}, {}

deps = data.get("dependencies", {})
dev_deps = data.get("dev-dependencies", {})
return deps, dev_deps

def format_dep_config(config):
if isinstance(config, str):
return {"version": config}
elif isinstance(config, dict):
return dict(sorted(config.items()))
else:
return {}

def main():
files = find_cargo_toml_files()
any_duplicates = False

for file_path in files:
deps, dev_deps = parse_dependencies(file_path)

duplicates = []
for dep_name, dep_config in deps.items():
if dep_name in dev_deps:
config1 = format_dep_config(dep_config)
config2 = format_dep_config(dev_deps[dep_name])
if config1 == config2:
duplicates.append(dep_name)

if duplicates:
any_duplicates = True
print(f"❌ Duplicated dependencies in {file_path}:")
for dep in sorted(duplicates):
print(f" - {dep}")

if any_duplicates:
print("\n🚫 CI check failed due to duplicated dependencies.")
sys.exit(1)
else:
print("✅ No duplicated dependencies found.")

if __name__ == "__main__":
main()
20 changes: 20 additions & 0 deletions .github/workflows/tests-misc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ jobs:
- cargo-check-each-crate
- test-deterministic-wasm
- cargo-check-all-crate-macos
- check-duplicated-deps
# - cargo-hfuzz remove from required for now, as it's flaky
if: always() && !cancelled()
steps:
Expand All @@ -406,3 +407,22 @@ jobs:
else
echo '### Good job! All the required jobs passed 🚀' >> $GITHUB_STEP_SUMMARY
fi

check-duplicated-deps:
needs: [preflight]
runs-on: ubuntu-latest
if: ${{ needs.preflight.outputs.changes_rust }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dependencies
run: python3 -m pip install toml

- name: Run duplicated dependency check
run: python3 .github/scripts/check-duplicated-deps.py
Loading