From c498fc174ceedf8deb33b1e217265ee8845c82ef Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Sun, 30 Aug 2026 16:11:48 -0700 Subject: [PATCH] Keep static manifest schema consistent --- archivebox/core/models.py | 8 +++++++- archivebox/tests/test_cli_list.py | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/archivebox/core/models.py b/archivebox/core/models.py index 068f47ec..b1637f09 100644 --- a/archivebox/core/models.py +++ b/archivebox/core/models.py @@ -459,6 +459,7 @@ class SnapshotQuerySet(models.QuerySet): template = "static_index.html" if with_headers else "minimal_index.html" snapshot_list = list(self.iterator(chunk_size=500)) + manifest_records = [] for snapshot in snapshot_list: outputs = snapshot.discover_outputs(include_filesystem_fallback=True) output_paths = [str(output.get("path") or "") for output in outputs] @@ -467,9 +468,14 @@ class SnapshotQuerySet(models.QuerySet): ] snapshot._public_favicon_paths = [path for path in output_paths if path in ("favicon/favicon.ico", "favicon.ico")] snapshot.write_html_details() + if with_headers: + # Use the same portable schema as the JSON export. Rendering + # above has already populated result-count caches, archive_size + # reuses the sealed output_size field, and tags are prefetched. + manifest_records.append(snapshot.to_dict(extended=True, static_export=True)) if with_headers: - manifest = "".join(f"{json.dumps(snapshot.to_json(), ensure_ascii=False, sort_keys=True)}\n" for snapshot in snapshot_list) + manifest = "".join(f"{to_json(record, indent=None, sort_keys=True)}\n" for record in manifest_records) atomic_write(str(CONSTANTS.DATA_DIR / CONSTANTS.JSONL_INDEX_FILENAME), manifest) return render_to_string( diff --git a/archivebox/tests/test_cli_list.py b/archivebox/tests/test_cli_list.py index e5c0e20e..8af08bf0 100644 --- a/archivebox/tests/test_cli_list.py +++ b/archivebox/tests/test_cli_list.py @@ -40,8 +40,15 @@ def test_static_export_creates_detail_page_for_unarchived_snapshot(snapshot): assert 'href="./index.json"' not in html root_manifest = CONSTANTS.DATA_DIR / "index.jsonl" assert root_manifest.exists() - manifest_records = parse_jsonl_output(root_manifest.read_text()) + manifest_records = [json.loads(line) for line in root_manifest.read_text().splitlines() if line.strip()] assert [record["id"] for record in manifest_records] == [str(snapshot.id)] + # JSON and JSONL are alternate containers for one static-export schema; + # consumers must not see TYPE/tags/archive paths change by file format. + assert manifest_records[0]["TYPE"] == "core.models.Snapshot" + assert "type" not in manifest_records[0] + assert isinstance(manifest_records[0]["tags"], list) + assert manifest_records[0]["archive_path"] == static_path + assert manifest_records[0]["archive_url"] == f"./{static_path}/index.html" assert detail_path.exists() detail_html = detail_path.read_text() assert f"/snapshot/{snapshot.id.hex}" not in detail_html