blob: bbd85b6536c45061824f8da1ef9456a26a6d65b6 [file]
import os
import shutil
import zipfile
from tools.private.zipapp import zipper
def symlink_target_path(p):
return p.replace("/", os.sep)
def is_symlink(zip_info):
# Check upper 4 bits of external_attr for S_IFLNK
# S_IFLNK is 0o120000 = 0xA000
attr = zip_info.external_attr >> 16
return (attr & 0xF000) == 0xA000
def assert_zip_file_content(zf, path, content=None, is_symlink_file=False, target=None):
info = zf.getinfo(path)
if is_symlink_file:
assert is_symlink(info), f"{path} should be a symlink but is not"
assert zf.read(path).decode() == target
else:
assert not is_symlink(info), f"{path} should NOT be a symlink but is"
assert zf.read(path).decode() == content
def create_zip(manifest_path, output_zip, **kwargs):
defaults = {
"manifest_path": manifest_path,
"output_zip": output_zip,
"compress_level": 0,
"workspace_name": "my_ws",
"legacy_external_runfiles": False,
"runfiles_dir": "runfiles",
"platform_pathsep": os.sep,
}
defaults.update(kwargs)
zipper.create_zip(**defaults)
def extract_zip(zip_path, extract_dir):
# Manually extract to preserve symlinks
with zipfile.ZipFile(zip_path, "r") as zf:
for info in zf.infolist():
extract_path = extract_dir / info.filename
extract_path.parent.mkdir(parents=True, exist_ok=True)
if is_symlink(info):
target = zf.read(info).decode()
os.symlink(target, extract_path)
else:
with zf.open(info) as src, open(extract_path, "wb") as dst:
shutil.copyfileobj(src, dst)
def test_create_zip_with_files_and_symlinks(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
file1_path = tmp_path / "file1.txt"
file1_path.write_text("content1")
link_target_path = "target.txt"
symlink_path = tmp_path / "symlink_source"
symlink_path.symlink_to(link_target_path)
manifest_content = [
f"regular|0|file1.txt|{file1_path}",
f"rf-file|0|foo/bar.txt|{file1_path}",
f"rf-symlink|1|link1|{symlink_path}",
f"rf-root-symlink|0|root_file|{file1_path}",
"rf-empty|empty_file",
]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip)
assert output_zip.exists()
with zipfile.ZipFile(output_zip, "r") as zf:
assert set(zf.namelist()) == {
"file1.txt",
"runfiles/my_ws/foo/bar.txt",
"runfiles/my_ws/link1",
"runfiles/root_file",
"runfiles/my_ws/empty_file",
}
assert_zip_file_content(zf, "file1.txt", content="content1")
assert_zip_file_content(zf, "runfiles/my_ws/foo/bar.txt", content="content1")
assert_zip_file_content(
zf, "runfiles/my_ws/link1", is_symlink_file=True, target="target.txt"
)
assert_zip_file_content(zf, "runfiles/root_file", content="content1")
assert_zip_file_content(zf, "runfiles/my_ws/empty_file", content="")
def test_create_zip_with_direct_symlink(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
manifest_content = ["symlink|path/to/link|target/path"]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip)
with zipfile.ZipFile(output_zip, "r") as zf:
assert zf.namelist() == ["runfiles/path/to/link"]
assert_zip_file_content(
zf,
"runfiles/path/to/link",
is_symlink_file=True,
target=symlink_target_path("../../target/path"),
)
def test_pathsep_normalization(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
file1_path = tmp_path / "file1.txt"
file1_path.write_text("content1")
manifest_content = [
f"regular|0|dir/file.txt|{file1_path}",
"symlink|link/path|target/path",
]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip, platform_pathsep="\\")
with zipfile.ZipFile(output_zip, "r") as zf:
assert set(zf.namelist()) == {"dir/file.txt", "runfiles/link/path"}
assert_zip_file_content(
zf,
"runfiles/link/path",
is_symlink_file=True,
target="..\\target\\path",
)
def test_symlink_precedence(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
file1_path = tmp_path / "file1.txt"
file1_path.write_text("content1")
manifest_content = [
f"rf-file|0|path/to/file|{file1_path}",
"symlink|my_ws/path/to/file|symlink/target",
]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip)
with zipfile.ZipFile(output_zip, "r") as zf:
assert zf.namelist() == ["runfiles/my_ws/path/to/file"]
assert_zip_file_content(
zf,
"runfiles/my_ws/path/to/file",
is_symlink_file=True,
target=symlink_target_path("../../../symlink/target"),
)
def test_timestamps_are_deterministic(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
file1_path = tmp_path / "file1.txt"
file1_path.write_text("content1")
os.utime(file1_path, None)
manifest_content = [f"regular|0|file1.txt|{file1_path}"]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip)
with zipfile.ZipFile(output_zip, "r") as zf:
info = zf.getinfo("file1.txt")
expected_date_time = (1980, 1, 1, 0, 0, 0)
assert info.date_time == expected_date_time
def test_runfiles_mapping_with_cross_repo_paths(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
file1_path = tmp_path / "file1.txt"
file1_path.write_text("content1")
manifest_content = [
f"rf-file|0|../other_repo/foo.txt|{file1_path}",
"rf-empty|../other_repo/empty_file",
]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip, workspace_name="my_ws")
with zipfile.ZipFile(output_zip, "r") as zf:
assert set(zf.namelist()) == {
"runfiles/other_repo/foo.txt",
"runfiles/other_repo/empty_file",
}
assert_zip_file_content(zf, "runfiles/other_repo/foo.txt", content="content1")
assert_zip_file_content(zf, "runfiles/other_repo/empty_file", content="")
def test_runfiles_mapping_with_legacy_external_paths(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
file1_path = tmp_path / "file1.txt"
file1_path.write_text("content1")
manifest_content = [
f"rf-file|0|external/other_repo/foo.txt|{file1_path}",
"rf-empty|external/other_repo/empty_file",
]
manifest_path.write_text("\n".join(manifest_content))
create_zip(
manifest_path, output_zip, workspace_name="my_ws", legacy_external_runfiles=True
)
with zipfile.ZipFile(output_zip, "r") as zf:
assert set(zf.namelist()) == {
"runfiles/other_repo/foo.txt",
"runfiles/other_repo/empty_file",
}
assert_zip_file_content(zf, "runfiles/other_repo/foo.txt", content="content1")
assert_zip_file_content(zf, "runfiles/other_repo/empty_file", content="")
def test_output_deterministic(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
file1 = tmp_path / "file1"
file1.write_text("1")
file2 = tmp_path / "file2"
file2.write_text("2")
file3 = tmp_path / "file3"
file3.write_text("3")
manifest_content = [
f"regular|0|z/regular|{file1}",
f"rf-file|0|b_rf_file|{file2}",
f"rf-root-symlink|0|a_root_link|{file3}",
f"regular|0|a/regular|{file3}",
"rf-empty|d_rf_empty",
f"rf-symlink|0|c_rf_link|{file3}",
]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip, workspace_name="my_ws")
with zipfile.ZipFile(output_zip, "r") as zf:
assert zf.namelist() == [
"a/regular",
"runfiles/a_root_link",
"runfiles/my_ws/b_rf_file",
"runfiles/my_ws/c_rf_link",
"runfiles/my_ws/d_rf_empty",
"z/regular",
]
def test_symlink_extraction(tmp_path):
manifest_path = tmp_path / "manifest.txt"
output_zip = tmp_path / "output.zip"
target_file = tmp_path / "target_file.txt"
target_file.write_text("target content")
manifest_content = [
f"rf-file|0|target/path|{target_file}",
"symlink|my_ws/path/to/link|my_ws/target/path",
f"rf-file|0|same_dir_target|{target_file}",
"symlink|my_ws/same_dir_link|my_ws/same_dir_target",
]
manifest_path.write_text("\n".join(manifest_content))
create_zip(manifest_path, output_zip, workspace_name="my_ws")
extract_dir = tmp_path / "extract"
extract_dir.mkdir()
extract_zip(output_zip, extract_dir)
link_path = extract_dir / "runfiles/my_ws/path/to/link"
assert link_path.is_symlink(), f"{link_path} should be a symlink"
assert os.readlink(link_path) == "../../target/path".replace("/", os.path.sep)
assert link_path.read_text() == "target content"
link2_path = extract_dir / "runfiles/my_ws/same_dir_link"
assert link2_path.is_symlink(), f"{link2_path} should be a symlink"
assert os.readlink(link2_path) == "same_dir_target"
assert link2_path.read_text() == "target content"