mirror of
https://github.com/Quad4-Software/MeshChatX.git
synced 2026-08-18 09:49:09 -04:00
feat(repack-android-wheels): implement RECORD regeneration for pycodec2 wheels to ensure installability
This commit is contained in:
parent
4d7cae49a6
commit
e741859c50
3 changed files with 66 additions and 4 deletions
|
|
@ -6,6 +6,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import hashlib
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
|
@ -35,6 +37,46 @@ def _find_libcodec2(vendor_dir: Path, abi_tag: str, dest_dir: Path) -> Path | No
|
|||
return _extract_libcodec2(matches[-1], dest_dir)
|
||||
|
||||
|
||||
def _urlsafe_sha256_digest(data: bytes) -> str:
|
||||
return base64.urlsafe_b64encode(hashlib.sha256(data).digest()).decode("ascii").rstrip("=")
|
||||
|
||||
|
||||
def _rewrite_wheel_record(root: Path) -> None:
|
||||
"""Regenerate dist-info/RECORD after wheel contents change.
|
||||
|
||||
Chaquopy's pip post-processor parses RECORD sizes with ``int()`` and rejects
|
||||
directory placeholder lines (``path,,``). Rebuilding RECORD from file bytes
|
||||
keeps repacked pycodec2 wheels installable.
|
||||
"""
|
||||
dist_infos = sorted(root.glob("*.dist-info"))
|
||||
if not dist_infos:
|
||||
return
|
||||
|
||||
record_path = dist_infos[0] / "RECORD"
|
||||
if not record_path.is_file():
|
||||
return
|
||||
|
||||
record_rel = record_path.relative_to(root).as_posix()
|
||||
file_entries: list[tuple[str, bytes]] = []
|
||||
for path in sorted(root.rglob("*")):
|
||||
if not path.is_file():
|
||||
continue
|
||||
rel = path.relative_to(root).as_posix()
|
||||
if rel == record_rel:
|
||||
continue
|
||||
file_entries.append((rel, path.read_bytes()))
|
||||
|
||||
rows = []
|
||||
for rel, data in file_entries:
|
||||
digest = _urlsafe_sha256_digest(data)
|
||||
rows.append(f"{rel},sha256={digest},{len(data)}")
|
||||
|
||||
record_body = "\n".join(rows) + "\n"
|
||||
record_digest = _urlsafe_sha256_digest(record_body.encode())
|
||||
record_body += f"{record_rel},sha256={record_digest},{len(record_body.encode())}\n"
|
||||
record_path.write_bytes(record_body.encode())
|
||||
|
||||
|
||||
def repack_pycodec2_wheel(pycodec2_wheel: Path, libcodec2_so: Path) -> bool:
|
||||
if not libcodec2_so.is_file():
|
||||
print(f"Missing libcodec2.so for {pycodec2_wheel.name}", file=sys.stderr)
|
||||
|
|
@ -48,6 +90,7 @@ def repack_pycodec2_wheel(pycodec2_wheel: Path, libcodec2_so: Path) -> bool:
|
|||
target = root / "pycodec2" / "libcodec2.so"
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(libcodec2_so, target)
|
||||
_rewrite_wheel_record(root)
|
||||
|
||||
tmp_wheel = pycodec2_wheel.with_suffix(".repack.whl")
|
||||
with zipfile.ZipFile(
|
||||
|
|
|
|||
|
|
@ -51,8 +51,15 @@ def test_repack_script_bundles_libcodec2(tmp_path):
|
|||
with zipfile.ZipFile(lib_wheel, "w") as zout:
|
||||
zout.writestr("chaquopy/lib/libcodec2.so", b"\x7fELF-lib")
|
||||
|
||||
record = (
|
||||
"pycodec2/pycodec2.so,sha256=deadbeef,8\n"
|
||||
"pycodec2.dist-info/METADATA,sha256=deadbeef,4\n"
|
||||
"pycodec2.dist-info/RECORD,,\n"
|
||||
)
|
||||
with zipfile.ZipFile(py_wheel, "w") as zout:
|
||||
zout.writestr("pycodec2/pycodec2.so", b"\x7fELF-mod")
|
||||
zout.writestr("pycodec2.dist-info/METADATA", b"meta")
|
||||
zout.writestr("pycodec2.dist-info/RECORD", record)
|
||||
|
||||
lib_src = tmp_path / "libcodec2.so"
|
||||
lib_src.write_bytes(b"\x7fELF-lib")
|
||||
|
|
@ -60,4 +67,12 @@ def test_repack_script_bundles_libcodec2(tmp_path):
|
|||
|
||||
with zipfile.ZipFile(py_wheel) as zin:
|
||||
names = zin.namelist()
|
||||
record_text = zin.read("pycodec2.dist-info/RECORD").decode()
|
||||
assert "pycodec2/libcodec2.so" in names
|
||||
assert ",,\n" not in record_text
|
||||
for line in record_text.splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
size_field = line.rsplit(",", 1)[-1]
|
||||
assert size_field
|
||||
int(size_field)
|
||||
|
|
|
|||
|
|
@ -313,7 +313,11 @@ async def test_discovered_interfaces_filter_works_with_ifac_network_name(temp_di
|
|||
|
||||
response = await handler(MagicMock())
|
||||
data = json.loads(response.body)
|
||||
names = [i["name"] for i in data["interfaces"]]
|
||||
assert names == ["matching"]
|
||||
assert data["interfaces"][0]["network_name"] == "kin.earth"
|
||||
assert data["interfaces"][0]["passphrase"] == "secret"
|
||||
assert len(data["interfaces"]) == 2
|
||||
|
||||
matching = next(i for i in data["interfaces"] if i["name"] == "matching")
|
||||
non_matching = next(i for i in data["interfaces"] if i["name"] == "non-matching")
|
||||
assert matching["is_allowed"] is True
|
||||
assert non_matching["is_allowed"] is False
|
||||
assert matching["network_name"] == "kin.earth"
|
||||
assert matching["passphrase"] == "secret"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue