Skip to content

Commit 3ff1ecf

Browse files
authored
Polish extension add URL-install tests and docs
1 parent 50bcf63 commit 3ff1ecf

3 files changed

Lines changed: 26 additions & 10 deletions

File tree

src/specify_cli/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3649,10 +3649,10 @@ def extension_add(
36493649
from specify_cli.authentication.http import open_url as _open_url
36503650

36513651
with _open_url(from_url, timeout=60) as response:
3652-
zip_data = response.read()
3652+
zip_bytes = response.read()
36533653

36543654
# Install from downloaded ZIP
3655-
manifest = manager.install_from_zip_bytes(zip_data, speckit_version, priority=priority)
3655+
manifest = manager.install_from_zip_bytes(zip_bytes, speckit_version, priority=priority)
36563656
except urllib.error.URLError as e:
36573657
console.print(f"[red]Error:[/red] Failed to download from {from_url}: {e}")
36583658
raise typer.Exit(1)

src/specify_cli/extensions.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,16 +1225,32 @@ def install_from_zip(
12251225
ValidationError: If manifest is invalid or priority is invalid
12261226
CompatibilityError: If extension is incompatible
12271227
"""
1228-
with zip_path.open("rb") as zip_file:
1229-
return self.install_from_zip_bytes(zip_file.read(), speckit_version, priority=priority)
1228+
try:
1229+
with zip_path.open("rb") as zip_file:
1230+
return self.install_from_zip_bytes(zip_file.read(), speckit_version, priority=priority)
1231+
except OSError as e:
1232+
raise ValidationError(f"Failed to read ZIP file {zip_path}: {e}") from e
12301233

12311234
def install_from_zip_bytes(
12321235
self,
12331236
zip_bytes: bytes,
12341237
speckit_version: str,
12351238
priority: int = 10,
12361239
) -> ExtensionManifest:
1237-
"""Install extension from ZIP bytes."""
1240+
"""Install extension from ZIP bytes.
1241+
1242+
Args:
1243+
zip_bytes: ZIP archive content.
1244+
speckit_version: Current spec-kit version.
1245+
priority: Resolution priority (lower = higher precedence, default 10).
1246+
1247+
Returns:
1248+
Installed extension manifest.
1249+
1250+
Raises:
1251+
ValidationError: If manifest is invalid or priority is invalid.
1252+
CompatibilityError: If extension is incompatible.
1253+
"""
12381254
# Validate priority early
12391255
if priority < 1:
12401256
raise ValidationError("Priority must be a positive integer (1 or higher)")

tests/test_extensions.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3510,28 +3510,28 @@ def test_add_from_url_installs_from_downloaded_bytes(self, tmp_path):
35103510
zip_payload = b"fake-zip-bytes"
35113511
install_args = {}
35123512

3513-
class _Resp:
3513+
class _MockHTTPResponse:
35143514
def __enter__(self):
35153515
return self
35163516

3517-
def __exit__(self, *_args):
3517+
def __exit__(self, exc_type, exc_val, exc_tb):
35183518
return False
35193519

35203520
def read(self):
35213521
return zip_payload
35223522

3523-
def _install_from_zip_bytes(self_obj, payload, _speckit_version, priority=10):
3523+
def _install_from_zip_bytes(_self, payload, _speckit_version, priority=10):
35243524
install_args["payload"] = payload
35253525
install_args["priority"] = priority
35263526
return fake_manifest
35273527

35283528
with patch.object(Path, "cwd", return_value=project_dir), \
3529-
patch("specify_cli.authentication.http.open_url", return_value=_Resp()), \
3529+
patch("specify_cli.authentication.http.open_url", return_value=_MockHTTPResponse()), \
35303530
patch.object(ExtensionManager, "install_from_zip_bytes", _install_from_zip_bytes), \
35313531
patch.object(ExtensionManager, "install_from_zip", side_effect=AssertionError("legacy path install should not be used")):
35323532
result = runner.invoke(
35333533
app,
3534-
["extension", "add", "../../evil", "--from", "https://example.com/ext.zip"],
3534+
["extension", "add", "ignored-extension-name", "--from", "https://example.com/ext.zip"],
35353535
catch_exceptions=True,
35363536
)
35373537

0 commit comments

Comments
 (0)