diff --git a/autotest/test_cli.py b/autotest/test_cli.py new file mode 100644 index 00000000..82361089 --- /dev/null +++ b/autotest/test_cli.py @@ -0,0 +1,55 @@ +from types import SimpleNamespace + +import pytest + +from modflow_devtools import cli + + +class _Registry: + def __init__(self, release_id, cache_path, error=None): + self.release_id = release_id + self.cache_path = cache_path + self._error = error + + def sync(self): + if self._error: + raise self._error + + +class _ModelConfig: + def __init__(self, failed): + self._failed = failed + + def sync(self): + return {"src": SimpleNamespace(failed=self._failed)} + + +@pytest.fixture +def fake_registries(monkeypatch, tmp_path): + def _fake(dfn_error=None, model_failed=()): + from modflow_devtools.dfns.registry import RemoteDfnRegistry + from modflow_devtools.models import ModelSourceConfig + + registry = _Registry("owner/repo@1.0", tmp_path, dfn_error) + monkeypatch.setattr(RemoteDfnRegistry, "load_default", lambda: {"r": registry}) + monkeypatch.setattr(ModelSourceConfig, "load", lambda: _ModelConfig(list(model_failed))) + + return _fake + + +def test_sync_all_succeeds(fake_registries, capsys): + fake_registries() + assert cli._sync_all() == 0 + assert "All registries synced!" in capsys.readouterr().out + + +def test_sync_all_fails_on_dfn_error(fake_registries, capsys): + fake_registries(dfn_error=OSError("no network")) + assert cli._sync_all() == 1 + assert "Failed to sync owner/repo@1.0: no network" in capsys.readouterr().out + + +def test_sync_all_fails_on_model_error(fake_registries, capsys): + fake_registries(model_failed=[("current", "models.toml not found")]) + assert cli._sync_all() == 1 + assert "Models synced successfully" not in capsys.readouterr().out diff --git a/modflow_devtools/cli.py b/modflow_devtools/cli.py index c80e1e19..b5c4d949 100644 --- a/modflow_devtools/cli.py +++ b/modflow_devtools/cli.py @@ -21,24 +21,32 @@ import sys -def _sync_all(): +def _sync_all() -> int: """Sync all registries (dfns, models). The Programs API has no registry to sync - - it installs directly from GitHub releases and tracks installs in a local ledger.""" + it installs directly from GitHub releases and tracks installs in a local ledger. + Returns 0 if everything synced, 1 if anything failed.""" print("Syncing all registries...") print() + failed = False # Sync DFNs print("=== DFNs ===") try: - from modflow_devtools.dfns.registry import sync_dfns - - registries = sync_dfns() - for registry in registries: - meta = registry.registry_meta - print(f" {registry.ref}: {len(meta.files)} files") + from modflow_devtools.dfns.registry import RemoteDfnRegistry + + registries = RemoteDfnRegistry.load_default() + for registry in registries.values(): + try: + registry.sync() + n_files = len(list(registry.cache_path.glob("*.*"))) + print(f" {registry.release_id}: {n_files} files") + except Exception as e: + print(f" [-] Failed to sync {registry.release_id}: {e}") + failed = True print(f"Synced {len(registries)} DFN registry(ies)") except Exception as e: print(f"Error syncing DFNs: {e}") + failed = True print() # Sync Models @@ -47,13 +55,21 @@ def _sync_all(): from modflow_devtools.models import ModelSourceConfig config = ModelSourceConfig.load() - config.sync() - print("Models synced successfully") + results = config.sync() + if any(result.failed for result in results.values()): + failed = True + else: + print("Models synced successfully") except Exception as e: print(f"Error syncing models: {e}") + failed = True print() + if failed: + print("Some registries failed to sync.") + return 1 print("All registries synced!") + return 0 def main(): @@ -85,7 +101,7 @@ def main(): # Dispatch to the appropriate module CLI with remaining args if args.subcommand == "sync": - _sync_all() + sys.exit(_sync_all()) elif args.subcommand == "dfns": from modflow_devtools.dfns.__main__ import main as dfns_main