Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/specify_cli/presets/_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,11 +757,16 @@ def preset_catalog_add(
# Load existing config
if config_path.exists():
try:
config = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
config = yaml.safe_load(config_path.read_text(encoding="utf-8"))
except Exception as e:
config_label = _display_project_path(project_root, config_path)
console.print(f"[red]Error:[/red] Failed to read {_escape_markup(str(config_label))}: {_escape_markup(str(e))}")
raise typer.Exit(1)
if config is None:
config = {}
elif not isinstance(config, dict):
console.print("[red]Error:[/red] Invalid catalog config: expected a mapping.")
raise typer.Exit(1)
else:
config = {}

Expand Down Expand Up @@ -817,10 +822,15 @@ def preset_catalog_remove(
raise typer.Exit(1)

try:
config = yaml.safe_load(config_path.read_text(encoding="utf-8")) or {}
config = yaml.safe_load(config_path.read_text(encoding="utf-8"))
except Exception as e:
console.print(f"[red]Error:[/red] Failed to read preset catalog config: {e}")
raise typer.Exit(1)
if config is None:
config = {}
elif not isinstance(config, dict):
console.print("[red]Error:[/red] Invalid catalog config: expected a mapping.")
raise typer.Exit(1)

catalogs = config.get("catalogs", [])
if not isinstance(catalogs, list):
Expand Down
32 changes: 32 additions & 0 deletions tests/test_presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3274,6 +3274,38 @@ def test_catalog_remove_escapes_markup_in_not_found_error(self, project_dir):
assert result.exit_code == 1
assert "[/red]absent" in result.output

@pytest.mark.parametrize(
"args",
[
[
"preset",
"catalog",
"add",
"https://example.com/catalog.json",
"--name",
"example",
],
["preset", "catalog", "remove", "example"],
],
)
def test_catalog_mutation_rejects_non_mapping_config_root(
self, project_dir, args
):
from typer.testing import CliRunner
from unittest.mock import patch
from specify_cli import app

config_path = project_dir / ".specify" / "preset-catalogs.yml"
original = "[]\n"
config_path.write_text(original, encoding="utf-8")

with patch.object(Path, "cwd", return_value=project_dir):
result = CliRunner().invoke(app, args)

assert result.exit_code == 1
assert "expected a mapping" in result.output
assert config_path.read_text(encoding="utf-8") == original

def test_env_var_overrides_catalogs(self, project_dir, monkeypatch):
"""Test that SPECKIT_PRESET_CATALOG_URL env var overrides defaults."""
monkeypatch.setenv(
Expand Down