@@ -281,6 +281,51 @@ def test_output_deterministic(self):
281281 ],
282282 )
283283
284+ def _extract_zip (self , zip_path , extract_dir ):
285+ # Manually extract to preserve symlinks
286+ with zipfile .ZipFile (zip_path , "r" ) as zf :
287+ for info in zf .infolist ():
288+ extract_path = extract_dir / info .filename
289+ extract_path .parent .mkdir (parents = True , exist_ok = True )
290+ if self .is_symlink (info ):
291+ target = zf .read (info ).decode ()
292+ os .symlink (target , extract_path )
293+ else :
294+ with zf .open (info ) as src , open (extract_path , "wb" ) as dst :
295+ shutil .copyfileobj (src , dst )
296+
297+ def test_symlink_extraction (self ):
298+ # Test that 'symlink' entries extract correctly as relative symlinks
299+ # Create a file that the symlink will point to
300+ target_file = self .test_dir / "target_file.txt"
301+ target_file .write_text ("target content" )
302+
303+ manifest_content = [
304+ f"rf-file|0|target/path|{ target_file } " ,
305+ "symlink|my_ws/path/to/link|my_ws/target/path" ,
306+ f"rf-file|0|same_dir_target|{ target_file } " ,
307+ "symlink|my_ws/same_dir_link|my_ws/same_dir_target" ,
308+ ]
309+ self .manifest_path .write_text ("\n " .join (manifest_content ))
310+
311+ self ._create_zip (workspace_name = "my_ws" )
312+
313+ extract_dir = self .test_dir / "extract"
314+ extract_dir .mkdir ()
315+
316+ self ._extract_zip (self .output_zip , extract_dir )
317+
318+ link_path = extract_dir / "runfiles/my_ws/path/to/link"
319+ self .assertTrue (link_path .is_symlink (), f"{ link_path } should be a symlink" )
320+ self .assertEqual (os .readlink (link_path ), "../../target/path" )
321+ self .assertEqual (link_path .read_text (), "target content" )
322+
323+ link2_path = extract_dir / "runfiles/my_ws/same_dir_link"
324+ self .assertTrue (link2_path .is_symlink (), f"{ link2_path } should be a symlink" )
325+ # Relative path from runfiles/my_ws/ to runfiles/my_ws/same_dir_target is just same_dir_target
326+ self .assertEqual (os .readlink (link2_path ), "same_dir_target" )
327+ self .assertEqual (link2_path .read_text (), "target content" )
328+
284329 def is_symlink (self , zip_info ):
285330 # Check upper 4 bits of external_attr for S_IFLNK
286331 # S_IFLNK is 0o120000 = 0xA000
0 commit comments