@@ -75,32 +75,63 @@ def inspect_components(
7575) -> ComponentReport :
7676 """Inspect managed native components without mutating user data."""
7777 environment = dict (env if env is not None else os .environ )
78- manifest_source = manifest_path or component_manifest .manifest_path ()
7978 manifest_unknown : tuple [str , ...] = ()
8079 manifest_schema_version : int | None = None
8180 manifest_revision : str | None = None
8281 manifest_brigade_version : str | None = None
82+ roots : component_install .SetupRoots | None = None
83+ environment_error : str | None = None
84+ try :
85+ roots = component_install .resolve_roots (env = environment , system = system )
86+ except ValueError as exc :
87+ environment_error = str (exc )
88+
89+ state_path = Path (
90+ component_paths .installed_state_path (roots .data_root if roots is not None else _UNAVAILABLE_DATA_ROOT )
91+ )
92+ installed_state , state_file_status = _read_installed_state (state_path ) if roots is not None else (None , "missing" )
93+
94+ manifest_source = manifest_path or component_manifest .manifest_path ()
8395 manifest : component_manifest .ComponentManifest | None = None
8496 try :
85- manifest = component_manifest .load (manifest_path ) if manifest_path is not None else component_manifest .load ()
97+ if manifest_path is not None :
98+ manifest = component_manifest .load (manifest_path )
99+ elif roots is not None and component_install .uses_bundled_compatibility_manifest ():
100+ exact_release_manifest = component_install .load_verified_exact_release_manifest (roots )
101+ if exact_release_manifest is None :
102+ manifest = component_manifest .load ()
103+ else :
104+ manifest , manifest_source = exact_release_manifest
105+ if installed_state is not None and not _installed_state_matches_manifest (installed_state , manifest ):
106+ manifest = component_manifest .load ()
107+ manifest_source = component_manifest .manifest_path ()
108+ else :
109+ manifest = component_manifest .load ()
110+ except component_install .ExactReleaseManifestError as exc :
111+ return _environment_blocked_report (
112+ manifest_source = exc .manifest_path ,
113+ platform_error = str (exc ),
114+ manifest = None ,
115+ roots = roots ,
116+ state_path = state_path ,
117+ installed_state = installed_state ,
118+ state_file_status = state_file_status ,
119+ )
86120 except ValueError as exc :
87121 return _environment_blocked_report (
88122 manifest_source = manifest_source ,
89123 platform_error = str (exc ),
90124 manifest = None ,
125+ roots = roots ,
126+ state_path = state_path ,
127+ installed_state = installed_state ,
128+ state_file_status = state_file_status ,
91129 )
92130 manifest_schema_version = manifest .schema_version
93131 manifest_revision = manifest .manifest_revision
94132 manifest_brigade_version = manifest .brigade_version
95133 manifest_unknown = manifest .unknown_component_diagnostics
96134
97- roots : component_install .SetupRoots | None = None
98- environment_error : str | None = None
99- try :
100- roots = component_install .resolve_roots (env = environment , system = system )
101- except ValueError as exc :
102- environment_error = str (exc )
103-
104135 platform : str | None = None
105136 platform_error : str | None = environment_error
106137 if environment_error is None :
@@ -115,11 +146,12 @@ def inspect_components(
115146 platform_error = platform_error or "component environment is unavailable" ,
116147 manifest = manifest ,
117148 manifest_unknown_diagnostics = manifest_unknown ,
149+ roots = roots ,
150+ state_path = state_path ,
151+ installed_state = installed_state ,
152+ state_file_status = state_file_status ,
118153 )
119154
120- state_path = Path (component_paths .installed_state_path (roots .data_root ))
121- installed_state , state_file_status = _read_installed_state (state_path )
122-
123155 inspections : list [ComponentInspection ] = []
124156 for component_id in component_manifest .KNOWN_COMPONENT_IDS :
125157 inspections .append (
@@ -160,9 +192,14 @@ def _environment_blocked_report(
160192 platform_error : str ,
161193 manifest : component_manifest .ComponentManifest | None ,
162194 manifest_unknown_diagnostics : tuple [str , ...] = (),
195+ roots : component_install .SetupRoots | None = None ,
196+ state_path : Path | None = None ,
197+ installed_state : component_state .InstalledState | None = None ,
198+ state_file_status : STATE_FILE_STATUS = "missing" ,
163199) -> ComponentReport :
164200 """Return a read-only unsupported report when roots or manifest cannot be resolved."""
165- state_path = Path (component_paths .installed_state_path (_UNAVAILABLE_DATA_ROOT ))
201+ report_state_path = state_path or Path (component_paths .installed_state_path (_UNAVAILABLE_DATA_ROOT ))
202+ data_root = roots .data_root if roots is not None else _UNAVAILABLE_DATA_ROOT
166203 components = tuple (
167204 ComponentInspection (
168205 component_id = component_id ,
@@ -171,16 +208,36 @@ def _environment_blocked_report(
171208 expected_component_revision = (
172209 manifest .components [component_id ].component_revision if manifest is not None else None
173210 ),
174- installed_component_revision = None ,
211+ installed_component_revision = (
212+ installed_state .components [component_id ].component_revision
213+ if installed_state is not None and component_id in installed_state .components
214+ else None
215+ ),
175216 expected_asset_name = None ,
176217 expected_byte_size = None ,
177218 expected_sha256 = None ,
178- installed_asset_name = None ,
179- installed_byte_size = None ,
180- installed_sha256 = None ,
181- recorded_executable = None ,
219+ installed_asset_name = (
220+ installed_state .components [component_id ].asset_name
221+ if installed_state is not None and component_id in installed_state .components
222+ else None
223+ ),
224+ installed_byte_size = (
225+ installed_state .components [component_id ].byte_size
226+ if installed_state is not None and component_id in installed_state .components
227+ else None
228+ ),
229+ installed_sha256 = (
230+ installed_state .components [component_id ].sha256
231+ if installed_state is not None and component_id in installed_state .components
232+ else None
233+ ),
234+ recorded_executable = (
235+ installed_state .components [component_id ].executable
236+ if installed_state is not None and component_id in installed_state .components
237+ else None
238+ ),
182239 managed_executable_path = component_paths .managed_executable_path (
183- _UNAVAILABLE_DATA_ROOT ,
240+ data_root ,
184241 component_id ,
185242 ),
186243 actual_byte_size = None ,
@@ -198,16 +255,46 @@ def _environment_blocked_report(
198255 platform = None ,
199256 platform_error = platform_error ,
200257 state_schema_version = component_state .SCHEMA_VERSION ,
201- installed_state_path = str (state_path ),
202- state_file_status = "missing" ,
203- installed_manifest_revision = None ,
204- installed_brigade_version = None ,
205- installed_platform = None ,
258+ installed_state_path = str (report_state_path ),
259+ state_file_status = state_file_status ,
260+ installed_manifest_revision = installed_state . manifest_revision if installed_state else None ,
261+ installed_brigade_version = installed_state . brigade_version if installed_state else None ,
262+ installed_platform = installed_state . platform if installed_state else None ,
206263 manifest_unknown_diagnostics = manifest_unknown_diagnostics ,
207264 components = components ,
208265 )
209266
210267
268+ def _installed_state_matches_manifest (
269+ installed_state : component_state .InstalledState ,
270+ manifest : component_manifest .ComponentManifest ,
271+ ) -> bool :
272+ """Return whether installed components use the manifest's exact recorded coordinates."""
273+ if (
274+ installed_state .manifest_revision != manifest .manifest_revision
275+ or installed_state .brigade_version != manifest .brigade_version
276+ ):
277+ return False
278+ for component_id in component_manifest .KNOWN_COMPONENT_IDS :
279+ installed = installed_state .components .get (component_id )
280+ if installed is None :
281+ return False
282+ component = manifest .components [component_id ]
283+ try :
284+ asset = component_manifest .resolve_asset (manifest , component_id , installed_state .platform )
285+ except ValueError :
286+ return False
287+ if (
288+ installed .component_revision != component .component_revision
289+ or installed .asset_name != asset .asset_name
290+ or installed .byte_size != asset .byte_size
291+ or installed .sha256 != asset .sha256
292+ or installed .download_url != asset .download_url
293+ ):
294+ return False
295+ return True
296+
297+
211298def doctor_checks (
212299 * ,
213300 env : Mapping [str , str ] | None = None ,
0 commit comments