Skip to content

Commit aaf20c3

Browse files
author
FirstUnicorn
committed
feat: add comprehensive dependency validation system
Add validation infrastructure to prevent lock file sync issues and package name mismatches. Components: - GitHub Actions workflow for CI validation - Pre-commit hooks for local validation - Validation script for package consistency checks - Added pre-commit and toml to dev dependencies Validates: - Package name/path consistency - Orphaned lock file references - Lock file sync with pyproject.toml - Clean installation from scratch Prevents: - Lock file not updated after package renames - Package name mismatches - Orphaned references in lock file - Installation failures in CI/fresh clones
1 parent 8d2af41 commit aaf20c3

6 files changed

Lines changed: 617 additions & 2 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: Validate Dependencies
2+
3+
on:
4+
push:
5+
branches: [master, main]
6+
pull_request:
7+
8+
jobs:
9+
validate:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Set up Python
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: '3.10'
18+
19+
- name: Install Poetry and dependencies
20+
run: |
21+
pip install poetry toml
22+
23+
# Option 1: Poetry lock file validation
24+
- name: Check pyproject.toml validity
25+
run: poetry check
26+
27+
- name: Verify lock file is up to date
28+
run: poetry check --lock
29+
30+
# Option 4: Multi-package validation
31+
- name: Validate all package metadata
32+
run: |
33+
for pkg_dir in packages/*/; do
34+
if [ -f "$pkg_dir/pyproject.toml" ]; then
35+
echo "Validating $(basename $pkg_dir)..."
36+
cd "$pkg_dir"
37+
poetry check || exit 1
38+
cd ../..
39+
fi
40+
done
41+
42+
# Approach B: Name/path validation + Approach C: Orphaned references
43+
- name: Validate dependency consistency (name/path matching + orphaned refs)
44+
run: |
45+
python << 'EOF'
46+
import toml
47+
import os
48+
import re
49+
50+
errors = []
51+
52+
print("=" * 60)
53+
print("STEP 1: Checking name/path consistency")
54+
print("=" * 60)
55+
56+
# 1. Check name/path consistency
57+
root = toml.load('pyproject.toml')
58+
deps = root.get('tool', {}).get('poetry', {}).get('dependencies', {})
59+
60+
for pkg_name, config in deps.items():
61+
if isinstance(config, dict) and 'path' in config:
62+
path = config['path']
63+
print(f"Checking {pkg_name} -> {path}")
64+
65+
if not os.path.exists(path):
66+
errors.append(f"Missing path: {pkg_name} -> {path}")
67+
continue
68+
69+
pkg_toml = os.path.join(path, 'pyproject.toml')
70+
if os.path.exists(pkg_toml):
71+
pkg_config = toml.load(pkg_toml)
72+
actual_name = pkg_config.get('tool', {}).get('poetry', {}).get('name')
73+
if actual_name and actual_name != pkg_name:
74+
errors.append(
75+
f"Name mismatch: dependency key '{pkg_name}' but "
76+
f"package defines name as '{actual_name}' at {path}"
77+
)
78+
else:
79+
print(f" ✓ {pkg_name} matches package name")
80+
else:
81+
errors.append(f"Missing pyproject.toml in {path}")
82+
83+
print("\n" + "=" * 60)
84+
print("STEP 2: Checking for orphaned references in lock file")
85+
print("=" * 60)
86+
87+
# 2. Check for orphaned references in lock file
88+
actual_pkgs = set(os.listdir('packages'))
89+
print(f"Actual packages in packages/: {sorted(actual_pkgs)}")
90+
91+
with open('poetry.lock') as f:
92+
lock_content = f.read()
93+
refs = set(re.findall(r'packages/([^/\s"]+)', lock_content))
94+
95+
print(f"Packages referenced in lock file: {sorted(refs)}")
96+
97+
orphaned = refs - actual_pkgs
98+
if orphaned:
99+
errors.append(f"Orphaned lock file references: {', '.join(sorted(orphaned))}")
100+
else:
101+
print(" ✓ No orphaned references found")
102+
103+
print("\n" + "=" * 60)
104+
print("VALIDATION RESULTS")
105+
print("=" * 60)
106+
107+
if errors:
108+
print("ERRORS FOUND:")
109+
for e in errors:
110+
print(f" ✗ {e}")
111+
exit(1)
112+
else:
113+
print("✓ All dependency references are valid")
114+
EOF
115+
116+
# Approach D: Lock file sync validation
117+
- name: Verify lock file is properly synced
118+
run: |
119+
echo "Backing up current lock file..."
120+
cp poetry.lock poetry.lock.original
121+
122+
echo "Regenerating lock file..."
123+
poetry lock --no-update
124+
125+
echo "Comparing files..."
126+
if ! diff -q poetry.lock.original poetry.lock; then
127+
echo "ERROR: poetry.lock is out of sync with pyproject.toml"
128+
echo ""
129+
echo "Differences found:"
130+
diff poetry.lock.original poetry.lock || true
131+
echo ""
132+
echo "Please run 'poetry lock' locally and commit the updated lock file."
133+
exit 1
134+
fi
135+
136+
echo "✓ Lock file is properly synced"
137+
138+
# Option 3: Full dependency installation test
139+
- name: Test clean installation
140+
run: |
141+
echo "Removing existing virtual environment..."
142+
rm -rf .venv
143+
144+
echo "Installing dependencies with --sync..."
145+
poetry install --sync
146+
147+
echo ""
148+
echo "Installed packages:"
149+
poetry show
150+
151+
echo ""
152+
echo "✓ All packages installed successfully"

.pre-commit-config.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
repos:
2+
- repo: https://github.com/python-poetry/poetry
3+
rev: '1.8.0'
4+
hooks:
5+
- id: poetry-check
6+
name: Validate pyproject.toml
7+
description: Check pyproject.toml validity
8+
9+
- id: poetry-lock
10+
name: Check poetry.lock is up to date
11+
description: Verify lock file matches pyproject.toml
12+
args: ['--check']
13+
14+
- repo: local
15+
hooks:
16+
- id: validate-package-consistency
17+
name: Validate package name/path consistency
18+
description: Check that package names match their directory structure
19+
entry: python scripts/validation/validate_packages.py
20+
language: python
21+
additional_dependencies: ['toml']
22+
pass_filenames: false
23+
files: '^(pyproject\.toml|poetry\.lock|packages/.*/pyproject\.toml)$'

poetry.lock

Lines changed: 135 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ black = "^26.0"
6262
ruff = ">=0.15,<1.0"
6363
mypy = "^1.19"
6464
import-linter = "^2.11"
65+
pre-commit = "^4.5"
66+
toml = "^0.10"
6567

6668
[tool.poetry.group.docs.dependencies]
6769
sphinx = "^8.1"

0 commit comments

Comments
 (0)