diff --git a/src/specify_cli/workflows/_commands.py b/src/specify_cli/workflows/_commands.py index 095e3e415e..375cc937a9 100644 --- a/src/specify_cli/workflows/_commands.py +++ b/src/specify_cli/workflows/_commands.py @@ -1009,6 +1009,13 @@ def workflow_catalog_list(): def workflow_catalog_add( url: str = typer.Argument(..., help="Catalog URL to add"), name: str | None = typer.Option(None, "--name", help="Catalog name"), + priority: int | None = typer.Option(None, "--priority", help="Priority (lower = higher priority)"), + install_allowed: bool = typer.Option( + True, + "--install-allowed/--no-install-allowed", + help="Allow workflows from this catalog to be installed", + ), + description: str = typer.Option("", "--description", help="Description of the catalog"), ): """Add a workflow catalog source.""" from .catalog import WorkflowCatalog, WorkflowValidationError @@ -1016,7 +1023,13 @@ def workflow_catalog_add( project_root = _require_specify_project() catalog = WorkflowCatalog(project_root) try: - catalog.add_catalog(url, name) + catalog.add_catalog( + url, + name, + priority=priority, + install_allowed=install_allowed, + description=description, + ) except WorkflowValidationError as exc: console.print(f"[red]Error:[/red] {exc}") raise typer.Exit(1) @@ -1661,6 +1674,13 @@ def workflow_step_catalog_list(): def workflow_step_catalog_add( url: str = typer.Argument(..., help="Catalog URL to add"), name: str | None = typer.Option(None, "--name", help="Catalog name"), + priority: int | None = typer.Option(None, "--priority", help="Priority (lower = higher priority)"), + install_allowed: bool = typer.Option( + True, + "--install-allowed/--no-install-allowed", + help="Allow steps from this catalog to be installed", + ), + description: str = typer.Option("", "--description", help="Description of the catalog"), ): """Add a step catalog source.""" from .catalog import StepCatalog, StepValidationError @@ -1669,7 +1689,13 @@ def workflow_step_catalog_add( catalog = StepCatalog(project_root) try: - catalog.add_catalog(url, name) + catalog.add_catalog( + url, + name, + priority=priority, + install_allowed=install_allowed, + description=description, + ) except StepValidationError as exc: console.print(f"[red]Error:[/red] {exc}") raise typer.Exit(1) diff --git a/src/specify_cli/workflows/catalog.py b/src/specify_cli/workflows/catalog.py index 97bf58a04e..67f06f9dae 100644 --- a/src/specify_cli/workflows/catalog.py +++ b/src/specify_cli/workflows/catalog.py @@ -476,7 +476,14 @@ def get_catalog_configs(self) -> list[dict[str, Any]]: for e in entries ] - def add_catalog(self, url: str, name: str | None = None) -> None: + def add_catalog( + self, + url: str, + name: str | None = None, + priority: int | None = None, + install_allowed: bool = True, + description: str = "", + ) -> None: """Add a catalog source to the project-level config.""" self._validate_catalog_url(url) config_path = self.project_root / ".specify" / "workflow-catalogs.yml" @@ -530,9 +537,9 @@ def _coerce_priority(value: Any) -> int: { "name": name or f"catalog-{len(catalogs) + 1}", "url": url, - "priority": max_priority + 1, - "install_allowed": True, - "description": "", + "priority": max_priority + 1 if priority is None else priority, + "install_allowed": install_allowed, + "description": description, } ) data["catalogs"] = catalogs @@ -1086,7 +1093,14 @@ def get_catalog_configs(self) -> list[dict[str, Any]]: for e in entries ] - def add_catalog(self, url: str, name: str | None = None) -> None: + def add_catalog( + self, + url: str, + name: str | None = None, + priority: int | None = None, + install_allowed: bool = True, + description: str = "", + ) -> None: """Add a catalog source to the project-level config.""" self._validate_catalog_url(url) config_path = self.project_root / ".specify" / "step-catalogs.yml" @@ -1136,9 +1150,9 @@ def _coerce_priority(value: Any) -> int: { "name": name or f"catalog-{len(catalogs) + 1}", "url": url, - "priority": max_priority + 1, - "install_allowed": True, - "description": "", + "priority": max_priority + 1 if priority is None else priority, + "install_allowed": install_allowed, + "description": description, } ) data["catalogs"] = catalogs diff --git a/tests/test_workflows.py b/tests/test_workflows.py index 11338b3009..c8aa98b0c4 100644 --- a/tests/test_workflows.py +++ b/tests/test_workflows.py @@ -4513,6 +4513,60 @@ def test_add_catalog(self, project_dir): data = yaml.safe_load(config_path.read_text()) assert len(data["catalogs"]) == 1 assert data["catalogs"][0]["url"] == "https://example.com/new-catalog.json" + assert data["catalogs"][0]["priority"] == 1 + assert data["catalogs"][0]["install_allowed"] is True + assert data["catalogs"][0]["description"] == "" + + def test_add_catalog_accepts_metadata_overrides(self, project_dir): + from specify_cli.workflows.catalog import WorkflowCatalog + + catalog = WorkflowCatalog(project_dir) + catalog.add_catalog( + "https://example.com/new-catalog.json", + "my-catalog", + priority=7, + install_allowed=False, + description="Workflow source", + ) + + config_path = project_dir / ".specify" / "workflow-catalogs.yml" + data = yaml.safe_load(config_path.read_text()) + assert data["catalogs"][0] == { + "name": "my-catalog", + "url": "https://example.com/new-catalog.json", + "priority": 7, + "install_allowed": False, + "description": "Workflow source", + } + + def test_catalog_add_cli_accepts_metadata_options(self, project_dir, monkeypatch): + from typer.testing import CliRunner + from specify_cli import app + + monkeypatch.chdir(project_dir) + result = CliRunner().invoke( + app, + [ + "workflow", + "catalog", + "add", + "https://example.com/new-catalog.json", + "--name", + "my-catalog", + "--priority", + "7", + "--no-install-allowed", + "--description", + "Workflow source", + ], + ) + + assert result.exit_code == 0, result.output + config_path = project_dir / ".specify" / "workflow-catalogs.yml" + data = yaml.safe_load(config_path.read_text()) + assert data["catalogs"][0]["priority"] == 7 + assert data["catalogs"][0]["install_allowed"] is False + assert data["catalogs"][0]["description"] == "Workflow source" def test_add_catalog_duplicate_rejected(self, project_dir): from specify_cli.workflows.catalog import WorkflowCatalog, WorkflowValidationError @@ -4959,6 +5013,61 @@ def test_add_catalog(self, project_dir): data = yaml.safe_load(config_path.read_text()) assert len(data["catalogs"]) == 1 assert data["catalogs"][0]["url"] == "https://example.com/new-steps.json" + assert data["catalogs"][0]["priority"] == 1 + assert data["catalogs"][0]["install_allowed"] is True + assert data["catalogs"][0]["description"] == "" + + def test_add_catalog_accepts_metadata_overrides(self, project_dir): + from specify_cli.workflows.catalog import StepCatalog + + catalog = StepCatalog(project_dir) + catalog.add_catalog( + "https://example.com/new-steps.json", + "my-steps", + priority=7, + install_allowed=False, + description="Step source", + ) + + config_path = project_dir / ".specify" / "step-catalogs.yml" + data = yaml.safe_load(config_path.read_text()) + assert data["catalogs"][0] == { + "name": "my-steps", + "url": "https://example.com/new-steps.json", + "priority": 7, + "install_allowed": False, + "description": "Step source", + } + + def test_catalog_add_cli_accepts_metadata_options(self, project_dir, monkeypatch): + from typer.testing import CliRunner + from specify_cli import app + + monkeypatch.chdir(project_dir) + result = CliRunner().invoke( + app, + [ + "workflow", + "step", + "catalog", + "add", + "https://example.com/new-steps.json", + "--name", + "my-steps", + "--priority", + "7", + "--no-install-allowed", + "--description", + "Step source", + ], + ) + + assert result.exit_code == 0, result.output + config_path = project_dir / ".specify" / "step-catalogs.yml" + data = yaml.safe_load(config_path.read_text()) + assert data["catalogs"][0]["priority"] == 7 + assert data["catalogs"][0]["install_allowed"] is False + assert data["catalogs"][0]["description"] == "Step source" def test_add_catalog_empty_yaml_file(self, project_dir): """An empty YAML config file should be treated as empty, not corrupted."""