Skip to content

SP-12: config server backends — Git backend + tiered overlay + ConfigClient e2e (v26.06.92)#120

Merged
ancongui merged 6 commits into
mainfrom
worktree-parity-sp12-config-server
Jun 10, 2026
Merged

SP-12: config server backends — Git backend + tiered overlay + ConfigClient e2e (v26.06.92)#120
ancongui merged 6 commits into
mainfrom
worktree-parity-sp12-config-server

Conversation

@ancongui

Copy link
Copy Markdown
Contributor

Summary

Twelfth sub-project of the PyFly↔Spring-Boot parity initiative — the config server.

  • GitConfigBackend (new pyfly[config-server-git] extra): clones a config repo (local paths too), checks out a label, serves from the working tree; save() commits locally; blocking git work off the event loop; backend.type=git selection with filesystem fallback when GitPython is absent.
  • Tiered overlay: FilesystemConfigBackend(search_locations=[...]) merges config across common/core/domain dirs (highest precedence first); single-root unchanged.
  • ConfigClient real e2e: injectable http_client enables a true httpx.ASGITransport → Starlette routes → backend round-trip asserting reverse-merge precedence.
  • Fixes: wired the backend-selection logic into the auto-config bean (was dead code — backend.type/search-locations silently ignored); cleaned up the clone tempdir + guarded concurrent clone; corrected an over-promising docstring/doc. Docs updated.

Test Plan

  • ruff / format / mypy --strict clean (668 src files).
  • Fast suite: 4543 passed, 7 skipped, 45 deselected.
  • 38 config_server tests incl. 6 git (local-repo), 3 ASGITransport e2e, 7 tiered, 4 backend-selection.
  • Review: CHANGES REQUESTED → CRITICAL dead-code wiring + tempdir leak + init race all fixed.
  • CI lint/typecheck/test/build pass on this PR.

Andrés Contreras Guillén added 6 commits June 10, 2026 02:06
…nfig-server-git extra

New `src/pyfly/config_server/adapters/git.py`: `GitConfigBackend` that lazily
clones a Git repo (local path or remote URI) and composes a
`FilesystemConfigBackend` over the working tree for all file-read logic.
`save()` commits changes locally (no push — documented out of scope).
`refresh()` pulls from origin when a remote exists.

- Lazy import via `_require_git()` helper; friendly ImportError when absent
- `pyproject.toml`: adds `config-server-git = ["GitPython>=3.1"]` extra and
  includes it in `full`; `uv lock` resolves gitdb/smmap/gitpython
- `tests/config_server/test_git_backend.py`: six `importorskip("git")` tests
  covering fetch, list, save-commits, save-updates, refresh-no-remote, and
  missing-returns-None — all using a local on-disk repo (no network)
…nsport e2e test

`client.py`: adds optional `http_client: Any | None = None` ctor param.
When injected, `fetch()` uses it without closing (caller owns lifecycle);
when omitted, builds+closes its own httpx.AsyncClient as before.

`tests/config_server/test_config_client_e2e.py`: three async tests that
drive `ConfigClient` through `httpx.ASGITransport(app=starlette_app)` with
no mocking — a real round-trip through routing, JSON serialisation, and
reverse-merge precedence (app+profile beats application+default; common-only
key survives).
…ackend

`backend.py`: `FilesystemConfigBackend` accepts optional
`search_locations: list[str | Path] | None = None` (highest-precedence first,
e.g. [domain, core, common]).  When set, `fetch()` iterates locations from
lowest to highest and merges properties so higher-precedence keys win and
lower-precedence fills gaps.  `save()` and `list()` always target `_root`
(the primary location).  Single-root behaviour unchanged when None.

`tests/config_server/test_tiered_overlay.py`: seven tests covering
domain-overrides-common, three-tier chain, missing-in-all, partial presence,
save-to-primary, list-uses-primary, and single-root-unchanged.
…fix docs

auto_configuration.py: extracts `_build_backend()` that selects GitConfigBackend
when backend.type=git (gracefully falls back to FS when GitPython absent),
or tiered FilesystemConfigBackend when search-locations is set, or the
original single-root FS backend otherwise.

__init__.py: replaces the over-promising "subclass ConfigBackend to plug in
Git, Consul, etcd, Vault" with an accurate statement: Filesystem and Git
backends ship; implement ConfigBackend for Consul/Vault/etc.  Adds
GitConfigBackend to __all__ via lazy __getattr__ to avoid hard import.

docs/modules/config-server.md: adds GitConfigBackend section, tiered
search-locations section, corrected custom-backend wording, and http_client
injection example for ConfigClient.
…ed selection now applies); clean up git tempdir + guard concurrent clone

- Fix 1: config_backend @bean now calls _build_backend(config) and returns ConfigBackend (Protocol); config_server bean widened to accept ConfigBackend — pyfly.config-server.backend.type=git and backend.search-locations are no longer silently ignored at boot.
- Fix 2: GitConfigBackend registers atexit(shutil.rmtree) when it creates its own tempdir; docstring corrected to recommend explicit clone_dir in production.
- Fix 3: _ensure_repo_async acquires asyncio.Lock before cloning with a double-checked guard so concurrent first-fetch calls cannot both dispatch git-clone to the same directory.
- Fix 4: asyncio.get_event_loop() → asyncio.get_running_loop() in FilesystemConfigBackend._fetch_from_root/fetch/save and GitConfigBackend._run_sync (all run inside coroutines).
- Tests: new tests/config_server/test_backend_selection.py covers _build_backend with backend.type=git (GitConfigBackend + live fetch), search-locations (tiered merge), default single-root, and no-config tempdir fallback; all 38 config_server tests pass.
@ancongui ancongui merged commit 9fcc11e into main Jun 10, 2026
5 checks passed
@ancongui ancongui deleted the worktree-parity-sp12-config-server branch June 10, 2026 00:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant