SP-12: config server backends — Git backend + tiered overlay + ConfigClient e2e (v26.06.92)#120
Merged
Merged
Conversation
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.
…d, tiered overlay, ConfigClient e2e)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Twelfth sub-project of the PyFly↔Spring-Boot parity initiative — the config server.
GitConfigBackend(newpyfly[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=gitselection with filesystem fallback when GitPython is absent.FilesystemConfigBackend(search_locations=[...])merges config across common/core/domain dirs (highest precedence first); single-root unchanged.http_clientenables a truehttpx.ASGITransport→ Starlette routes → backend round-trip asserting reverse-merge precedence.backend.type/search-locationssilently ignored); cleaned up the clone tempdir + guarded concurrent clone; corrected an over-promising docstring/doc. Docs updated.Test Plan
4543 passed, 7 skipped, 45 deselected.