(font_url: str = DEFAULT_FONT_URL)
| 566 | fileobj = tf.extractfile(member) |
| 567 | if fileobj is None: |
| 568 | raise RuntimeError("Backup manifest is unreadable.") |
| 569 | data = json.loads(fileobj.read().decode("utf-8")) |
| 570 | if not isinstance(data, dict) or not isinstance(data.get("targets"), list): |
| 571 | raise RuntimeError("Backup manifest is invalid.") |
| 572 | return data |
| 573 | |
| 574 | |
| 575 | def restore_backup(backup_path: Path) -> None: |
| 576 | if not backup_path.exists(): |
| 577 | raise FileNotFoundError(str(backup_path)) |
| 578 | |
| 579 | header("Restoring Termux settings") |
| 580 | with tarfile.open(backup_path, "r:gz") as tf: |
| 581 | manifest = _load_backup_manifest(tf) |
| 582 | for item in manifest["targets"]: |
| 583 | target_raw = item.get("path") |
| 584 | if not isinstance(target_raw, str): |
| 585 | continue |
| 586 | target = Path(target_raw) |
| 587 | |
| 588 | if not _safe_restore_target(target): |
| 589 | SUMMARY.warning(f"Skipped unsafe backup path: {target}") |
| 590 | continue |
| 591 | |
| 592 | if not item.get("existed", False): |
| 593 | if target.exists() and target.is_file(): |
| 594 | target.unlink() |
| 595 | SUMMARY.success(f"Removed file that did not exist at backup time: {target}") |
| 596 | continue |
| 597 | |
| 598 | archive_name = item.get("archive_name") |
| 599 | if not isinstance(archive_name, str) or not archive_name.startswith(("home/", "prefix/", "files/")): |
| 600 | SUMMARY.warning(f"Missing archive entry for {target}") |
| 601 | continue |
| 602 | |
| 603 | try: |
| 604 | member = tf.getmember(archive_name) |
| 605 | source = tf.extractfile(member) |
| 606 | except KeyError: |
| 607 | source = None |
| 608 | if source is None: |
| 609 | SUMMARY.warning(f"Backup data missing for {target}") |
| 610 | continue |
| 611 | |
| 612 | data = source.read() |
| 613 | expected = item.get("sha256") |
| 614 | if expected: |
no test coverage detected