Skip to content

Commit 52aa23c

Browse files
authored
Merges newly downloaded packages into existing index.json. (#132)
Fixes #131
1 parent 6d8c627 commit 52aa23c

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

src/manage/install_command.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,26 @@ def _install_one(cmd, source, install, *, target=None):
534534
LOGGER.verbose("Install complete")
535535

536536

537+
def _merge_existing_index(versions, index_json):
538+
try:
539+
with open(index_json, "r", encoding="utf-8") as f:
540+
existing_index = json.load(f)
541+
list(existing_index["versions"])
542+
except FileNotFoundError:
543+
pass
544+
except (json.JSONDecodeError, KeyError, ValueError):
545+
LOGGER.warn("Existing index file appeared invalid and was overwritten.")
546+
LOGGER.debug("TRACEBACK", exc_info=True)
547+
else:
548+
LOGGER.debug("Merging into existing %s", index_json)
549+
current = {i["url"].casefold() for i in versions}
550+
added = []
551+
for install in existing_index["versions"]:
552+
if install.get("url", "").casefold() not in current:
553+
LOGGER.debug("Merging %s", install.get("url", "<unspecified>"))
554+
versions.append(install)
555+
556+
537557
def _fatal_install_error(cmd, ex):
538558
logfile = cmd.get_log_file()
539559
if logfile:
@@ -753,6 +773,7 @@ def execute(cmd):
753773
return _fatal_install_error(cmd, ex)
754774

755775
if cmd.download:
776+
_merge_existing_index(download_index["versions"], cmd.download / "index.json")
756777
with open(cmd.download / "index.json", "w", encoding="utf-8") as f:
757778
json.dump(download_index, f, indent=2, default=str)
758779
LOGGER.info("Offline index has been generated at !Y!%s!W!.", cmd.download)

tests/test_install_command.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import os
23
import pytest
34
import secrets
@@ -131,3 +132,60 @@ def get_installs(self):
131132
assert_log(
132133
assert_log.skip_until(".*Global shortcuts directory is not on PATH")
133134
)
135+
136+
137+
def test_merge_existing_index(tmp_path):
138+
# This function is for multiple downloaded index.jsons, so it merges based
139+
# on the url property, which should usually be a local file.
140+
existing = tmp_path / "index.json"
141+
with open(existing, "w", encoding="utf-8") as f:
142+
json.dump({"versions": [
143+
{"id": "test-1", "url": "test-file-1.zip"},
144+
{"id": "test-2", "url": "test-file-2.zip"},
145+
{"id": "test-3", "url": "test-file-3.zip"},
146+
]}, f)
147+
148+
new = [
149+
# Ensure new versions appear first
150+
{"id": "test-4", "url": "test-file-4.zip"},
151+
# Ensure matching ID doesn't result in overwrite
152+
{"id": "test-1", "url": "test-file-1b.zip"},
153+
# Ensure matching URL excludes original entry
154+
{"id": "test-2b", "url": "test-file-2.zip"},
155+
]
156+
157+
IC._merge_existing_index(new, existing)
158+
159+
assert new == [
160+
{"id": "test-4", "url": "test-file-4.zip"},
161+
{"id": "test-1", "url": "test-file-1b.zip"},
162+
{"id": "test-2b", "url": "test-file-2.zip"},
163+
{"id": "test-1", "url": "test-file-1.zip"},
164+
{"id": "test-3", "url": "test-file-3.zip"},
165+
]
166+
167+
168+
def test_merge_existing_index_not_found(tmp_path):
169+
existing = tmp_path / "index.json"
170+
try:
171+
existing.unlink()
172+
except FileNotFoundError:
173+
pass
174+
175+
# Expect no failure and no change
176+
new = [1, 2, 3]
177+
IC._merge_existing_index(new, existing)
178+
assert new == [1, 2, 3]
179+
180+
181+
def test_merge_existing_index_not_valid(tmp_path):
182+
existing = tmp_path / "index.json"
183+
with open(existing, "w", encoding="utf-8") as f:
184+
print("It's not a list of installs", file=f)
185+
print("But more importantly,", file=f)
186+
print("it's not valid JSON!", file=f)
187+
188+
# Expect no failure and no change
189+
new = [1, 2, 3]
190+
IC._merge_existing_index(new, existing)
191+
assert new == [1, 2, 3]

0 commit comments

Comments
 (0)