88from typing import Any
99
1010from scripts .native_layering .constants import (
11+ CARGO_LOCKFILE ,
1112 CARGO_MANIFEST ,
1213 CLI_DISTRIBUTION_PIN_PREFIX ,
14+ LOCK_SOURCE_KEY ,
15+ PYPROJECT_LOCK_SOURCE ,
16+ PYPROJECT_LOCKFILE ,
1317 PYPROJECT_MANIFEST ,
1418 RELEASE_MANIFEST ,
1519 RELEASE_MANIFEST_PACKAGE ,
1620)
1721
1822
1923def get_native_version_errors (root : Path ) -> tuple [str , ...]:
20- """Return an error when the Cargo workspace , Python package and release versions differ ."""
24+ """Return errors when the Cargo, Python, lockfile and release versions disagree ."""
2125 with (root / CARGO_MANIFEST ).open ("rb" ) as handle :
2226 cargo : dict [str , Any ] = tomllib .load (handle )
2327 with (root / PYPROJECT_MANIFEST ).open ("rb" ) as handle :
2428 pyproject : dict [str , Any ] = tomllib .load (handle )
2529 release : dict [str , Any ] = json .loads ((root / RELEASE_MANIFEST ).read_text (encoding = "utf-8" ))
30+ workspace_version : Any = cargo .get ("workspace" , {}).get ("package" , {}).get ("version" )
2631 versions : dict [str , Any ] = {
27- f"{ CARGO_MANIFEST } [workspace.package] version" : cargo .get ("workspace" , {})
28- .get ("package" , {})
29- .get ("version" ),
32+ f"{ CARGO_MANIFEST } [workspace.package] version" : workspace_version ,
3033 f"{ PYPROJECT_MANIFEST } [project] version" : pyproject .get ("project" , {}).get ("version" ),
3134 f"{ RELEASE_MANIFEST } version" : release .get (RELEASE_MANIFEST_PACKAGE ),
3235 f"{ PYPROJECT_MANIFEST } sqlbuild-cli dependency" : next (
@@ -38,7 +41,62 @@ def get_native_version_errors(root: Path) -> tuple[str, ...]:
3841 None ,
3942 ),
4043 }
41- if len (set (versions .values ())) == 1 :
44+ errors : list [str ] = []
45+ if len (set (versions .values ())) != 1 :
46+ listed : str = ", " .join (f"{ source } is { version } " for source , version in versions .items ())
47+ errors .append (f"Release versions differ: { listed } ." )
48+ errors .extend (
49+ _cargo_lockfile_errors (root = root , cargo = cargo , workspace_version = workspace_version )
50+ )
51+ errors .extend (_python_lockfile_errors (root = root , pyproject = pyproject ))
52+ return tuple (errors )
53+
54+
55+ def _python_lockfile_errors (* , root : Path , pyproject : dict [str , Any ]) -> tuple [str , ...]:
56+ """Report a uv.lock entry for the project that carries a version other than pyproject's."""
57+ project : dict [str , Any ] = pyproject .get ("project" , {})
58+ with (root / PYPROJECT_LOCKFILE ).open ("rb" ) as handle :
59+ lock : dict [str , Any ] = tomllib .load (handle )
60+ locked : list [Any ] = [
61+ entry .get ("version" )
62+ for entry in lock .get ("package" , ())
63+ if entry .get ("name" ) == project .get ("name" )
64+ and entry .get (LOCK_SOURCE_KEY ) == PYPROJECT_LOCK_SOURCE
65+ ]
66+ if locked == [project .get ("version" )]:
67+ return ()
68+ listed : str = ", " .join (map (str , locked )) or "no version"
69+ return (
70+ f"{ PYPROJECT_LOCKFILE } locks { project .get ('name' )} at { listed } , "
71+ f"not { PYPROJECT_MANIFEST } { project .get ('version' )} . Run `uv lock`." ,
72+ )
73+
74+
75+ def _cargo_lockfile_errors (
76+ * , root : Path , cargo : dict [str , Any ], workspace_version : Any
77+ ) -> tuple [str , ...]:
78+ """Report workspace crates whose Cargo.lock entry differs from their manifest version."""
79+ expected : dict [str , Any ] = {}
80+ for pattern in cargo .get ("workspace" , {}).get ("members" , ()):
81+ for directory in sorted (root .glob (pattern )):
82+ with (directory / CARGO_MANIFEST ).open ("rb" ) as handle :
83+ package : dict [str , Any ] = tomllib .load (handle ).get ("package" , {})
84+ version : Any = package .get ("version" )
85+ if isinstance (version , dict ) and version .get ("workspace" ) is True :
86+ version = workspace_version
87+ expected [package .get ("name" )] = version
88+ with (root / CARGO_LOCKFILE ).open ("rb" ) as handle :
89+ lock : dict [str , Any ] = tomllib .load (handle )
90+ stale : list [str ] = sorted (
91+ f"{ entry ['name' ]} is { entry .get ('version' )} , not { expected [entry ['name' ]]} "
92+ for entry in lock .get ("package" , ())
93+ if LOCK_SOURCE_KEY not in entry
94+ and entry .get ("name" ) in expected
95+ and entry .get ("version" ) != expected [entry ["name" ]]
96+ )
97+ if not stale :
4298 return ()
43- listed : str = ", " .join (f"{ source } is { version } " for source , version in versions .items ())
44- return (f"Release versions differ: { listed } ." ,)
99+ return (
100+ f"{ CARGO_LOCKFILE } workspace crate versions are stale: { '; ' .join (stale )} . "
101+ "Run `cargo update --workspace`." ,
102+ )
0 commit comments