docs: add USER_GUIDE.md, tighten comments, fix CLI needing a DSN
ci / lint (push) Successful in 33s
ci / types (push) Successful in 43s
ci / unit (push) Successful in 32s
ci / security (push) Successful in 57s
ci / dockerfile (push) Successful in 7s
ci / chart (push) Successful in 8s
ci / integration (push) Successful in 55s
ci / image (api) (push) Successful in 3m39s
ci / image (reconciler) (push) Successful in 2m53s
ci / image (worker) (push) Successful in 2m14s
ci / bump (push) Successful in 16s
ci / lint (push) Successful in 33s
ci / types (push) Successful in 43s
ci / unit (push) Successful in 32s
ci / security (push) Successful in 57s
ci / dockerfile (push) Successful in 7s
ci / chart (push) Successful in 8s
ci / integration (push) Successful in 55s
ci / image (api) (push) Successful in 3m39s
ci / image (reconciler) (push) Successful in 2m53s
ci / image (worker) (push) Successful in 2m14s
ci / bump (push) Successful in 16s
The comment pass is prose-only: every distinct "why" is kept, the narration around it is not. Verified by AST-comparing each changed file against HEAD with docstrings stripped — only the two files below differ in executable code. Two real fixes fell out of the read-through: * The CLI documented itself as never touching the database, then called load_settings(), which requires SVCFORGE_PG_DSN. It refused to start without a Postgres URL it never opens. It now has its own two-field ClientSettings; the orphaned api_url/api_token are dropped from Settings, where nothing else read them. * repo/db.py had the DictRow alias comment and the ERROR_MAX_CHARS comment run together above the wrong symbol. USER_GUIDE.md is the caller-facing guide the README only gestured at: auth, catalog, every endpoint with curl, the lifecycle, the error table, rate limiting, the CLI, client generation, an end-to-end poll loop. It records two facts about the live deployment rather than documenting a flow nobody can run. SVCFORGE_JWKS_URL points at a realm with no IdP behind it, so the API logs "JWKS warm-up failed" at startup and every /v1 request is a 401. And `helm repo list` in the worker returns no repositories, so the three bitnamilegacy/ catalog entries cannot resolve at provision time; only the oci:// entries can. make lint clean, 76 unit + 111 integration tests pass.
This commit is contained in:
+22
-6
@@ -1,9 +1,9 @@
|
||||
"""svcforge — the control plane client.
|
||||
|
||||
This talks to the API over HTTP and never touches the database. That restraint is the
|
||||
whole design: if the CLI could write to Postgres, every invariant the API enforces
|
||||
(the state machine, the one-transaction create, AuthZ in the WHERE clause) would have a
|
||||
back door, and the first 3am incident would go through it.
|
||||
Talks to the API over HTTP and never touches the database. If the CLI could write to
|
||||
Postgres, every invariant the API enforces — the state machine, the one-transaction create,
|
||||
AuthZ in the WHERE clause — would have a back door, and the first 3am incident would go
|
||||
through it. `ClientSettings` below is what keeps that true in practice.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -16,9 +16,9 @@ from typing import Annotated, Any
|
||||
|
||||
import httpx
|
||||
import typer
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
from svcforge_core.domain.states import InstanceState
|
||||
from svcforge_core.settings import load_settings
|
||||
|
||||
app = typer.Typer(help="svcforge control plane client", no_args_is_help=True)
|
||||
|
||||
@@ -41,8 +41,24 @@ class Size(StrEnum):
|
||||
MEDIUM = "medium"
|
||||
|
||||
|
||||
class ClientSettings(BaseSettings):
|
||||
"""The two values the CLI needs, and nothing else.
|
||||
|
||||
Its own model rather than `svcforge_core.settings.Settings`, which requires
|
||||
`SVCFORGE_PG_DSN`: loading that here would refuse to run the CLI without a database URL
|
||||
it then never opens, on a laptop that has no reason to hold one.
|
||||
"""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_prefix="SVCFORGE_", env_file=".env", env_file_encoding="utf-8", extra="ignore", frozen=True
|
||||
)
|
||||
|
||||
api_url: str = "http://localhost:8000"
|
||||
api_token: str | None = None
|
||||
|
||||
|
||||
def _client() -> httpx.Client:
|
||||
settings = load_settings()
|
||||
settings = ClientSettings()
|
||||
headers = {"authorization": f"Bearer {settings.api_token}"} if settings.api_token else {}
|
||||
return httpx.Client(base_url=settings.api_url, headers=headers, timeout=10.0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user