@@ -50,6 +50,40 @@ def git_output(args: list[str], cwd: Path = ROOT_DIR) -> tuple[int, str, str]:
5050 return result .returncode , result .stdout .strip (), result .stderr .strip ()
5151
5252
53+ def resolve_source_ref (source_dir : Path , ref : str ) -> tuple [str , str ]:
54+ if not (source_dir / ".git" ).is_dir ():
55+ return "" , "local source cache missing"
56+ rc , resolved , error = git_output (["git" , "-C" , str (source_dir ), "rev-parse" , f"{ ref } ^{{commit}}" ])
57+ if rc != 0 :
58+ return "" , error or f"git rev-parse failed for { ref } "
59+ return resolved , ""
60+
61+
62+ def sync_source_cache (source_name : str , ref : str , * , reason : str ) -> dict [str , Any ]:
63+ env = {
64+ ** os .environ ,
65+ "SOURCE" : source_name ,
66+ "SOURCE_NAME" : source_name ,
67+ "UPSTREAM_REF" : ref ,
68+ }
69+ result = subprocess .run (
70+ [str (ROOT_DIR / "scripts" / "sync-upstream.sh" )],
71+ cwd = ROOT_DIR ,
72+ env = env ,
73+ text = True ,
74+ capture_output = True ,
75+ check = False ,
76+ )
77+ return {
78+ "status" : "passed" if result .returncode == 0 else "failed" ,
79+ "classification" : "ok" if result .returncode == 0 else "external source sync failed" ,
80+ "reason" : reason ,
81+ "returncode" : result .returncode ,
82+ "stdout" : result .stdout .strip (),
83+ "stderr" : result .stderr .strip (),
84+ }
85+
86+
5387def run_patch_apply (source_name : str , source_dir : Path , ref : str ) -> dict [str , Any ]:
5488 if not (source_dir / ".git" ).is_dir ():
5589 return {
@@ -117,9 +151,25 @@ def drift_check(include_upstream_head: bool = False) -> dict[str, Any]:
117151 entry = sources ["sources" ][source_name ]
118152 ref = entry ["ref" ]
119153 source_dir = ROOT_DIR / ".sources" / source_name
120- _rc , resolved , ref_error = git_output (["git" , "-C" , str (source_dir ), "rev-parse" , f"{ ref } ^{{commit}}" ]) if source_dir .exists () else (1 , "" , "local source cache missing" )
154+ resolved , ref_error = resolve_source_ref (source_dir , ref )
155+ source_sync = {
156+ "status" : "skipped" ,
157+ "classification" : "ok" ,
158+ "reason" : "configured source ref already resolved locally" ,
159+ }
160+ if not resolved :
161+ source_sync = sync_source_cache (source_name , ref , reason = ref_error )
162+ if source_sync ["status" ] == "passed" :
163+ resolved , ref_error = resolve_source_ref (source_dir , ref )
121164 series_digest = patch_series_hash (source_name )
122- configured_pin = run_patch_apply (source_name , source_dir , ref )
165+ if source_sync ["status" ] == "failed" :
166+ configured_pin = {
167+ "status" : "skipped" ,
168+ "classification" : "external check skipped" ,
169+ "reason" : "source sync failed before patch apply" ,
170+ }
171+ else :
172+ configured_pin = run_patch_apply (source_name , source_dir , ref )
123173 upstream_head = {"status" : "skipped" , "classification" : "external check skipped" , "reason" : "run with --upstream-head" }
124174 if include_upstream_head :
125175 upstream_ref = "origin/master"
@@ -148,13 +198,16 @@ def drift_check(include_upstream_head: bool = False) -> dict[str, Any]:
148198 release_blockers = []
149199 if configured_pin ["classification" ] == "release-blocking drift" :
150200 release_blockers .append ("configured pin patch stack does not apply" )
201+ if source_sync ["status" ] == "failed" :
202+ release_blockers .append ("configured source sync failed" )
151203 if not resolved :
152204 release_blockers .append (f"configured source ref is not resolved locally: { ref_error } " )
153205 return {
154206 "status" : "passed" if not release_blockers else "failed" ,
155207 "source" : source_name ,
156208 "configuredRef" : ref ,
157209 "resolvedConfiguredRef" : resolved ,
210+ "sourceSync" : source_sync ,
158211 "patchSeriesSha256" : series_digest ,
159212 "patchCount" : len (series (source_name )),
160213 "configuredPinPatchCheck" : configured_pin ,
0 commit comments