diff --git a/docsgpt/deploy/commands.py b/docsgpt/deploy/commands.py index 6353925a..1495bb5b 100644 --- a/docsgpt/deploy/commands.py +++ b/docsgpt/deploy/commands.py @@ -240,6 +240,22 @@ def _docsgpt_launcher() -> list[str]: ) +def _refuse_docker_only_options(args) -> None: + """Options that only mean something to the Docker stack, refused rather than quietly ignored.""" + if getattr(args, "domain", None) or getattr(args, "expose", None) in ("network", "domain"): + raise DeployError( + "a native install serves on 127.0.0.1 only, so --domain, --expose network and " + "--expose domain have nothing to act on here. Put a reverse proxy in front of it, or run " + "the Docker stack with `docsgpt up --expose ...`, which brings its own Caddy for a domain." + ) + if getattr(args, "docling", None): + raise DeployError( + "--docling selects a Docker image variant, which a native install does not use. Install the " + 'parser engine into this environment instead, with `uv tool install "docsgpt[docling]"` or ' + '`pip install "docsgpt[docling]"`, then run `docsgpt up --native` again.' + ) + + def _native_port(env: Mapping[str, str]) -> str: """The port a native install listens on.""" return str(env.get("DOCSGPT_PORT") or stack.DEFAULT_PORT) @@ -252,6 +268,7 @@ def _native_address(env: Mapping[str, str]) -> str: def _native_up(args, context: Context, directory: Path) -> int: """Run the API and the worker as services on this machine, against an existing Postgres and Redis.""" + _refuse_docker_only_options(args) services = context.service_manager() env_path = directory / ".env" existing = envfile.read(env_path) diff --git a/tests/deploy/test_native.py b/tests/deploy/test_native.py index 2b360803..db3b1dfb 100644 --- a/tests/deploy/test_native.py +++ b/tests/deploy/test_native.py @@ -164,6 +164,22 @@ class TestNativeUp: assert _run(["uninstall", "--yes", "--dir", str(tmp_path)], _native_context(services)) == 0 assert sorted(services.removed) == ["docsgpt-api", "docsgpt-worker"] + def test_docker_only_options_are_refused_rather_than_ignored(self, tmp_path): + """Asking for network exposure and silently getting loopback is the worst of both.""" + base = ["up", "--native", "--dir", str(tmp_path), "--yes", "--postgres-uri", "postgresql://localhost/d"] + with pytest.raises(DeployError, match="127.0.0.1 only"): + _run([*base, "--expose", "network"], _native_context()) + with pytest.raises(DeployError, match="127.0.0.1 only"): + _run([*base, "--domain", "docs.example.com"], _native_context()) + with pytest.raises(DeployError, match=r"docsgpt\[docling\]"): + _run([*base, "--docling"], _native_context()) + assert not (tmp_path / ".env").exists(), "it refuses before writing anything" + + def test_the_options_that_describe_what_native_already_does_are_kept(self, tmp_path): + argv = ["up", "--native", "--dir", str(tmp_path), "--yes", "--postgres-uri", "postgresql://localhost/d", + "--expose", "local", "--no-docling"] + assert _run(argv, _native_context()) == 0 + def test_a_database_url_is_required(self, tmp_path): with pytest.raises(DeployError, match="--postgres-uri"): _run(["up", "--native", "--dir", str(tmp_path), "--yes"], _native_context())