Activate one auth provider instead of failing the boot on SM020 - #256
Merged
Conversation
A fresh clone could not boot. `uv sync --all-packages` installs every workspace member, so both `users` and `keycloak` register auth-provider entry points, SM020 fires, and `create_app` exits before serving a request — `make dev`, `python -m host.main`, and `make doctor` all died on a clean checkout. Add `SM_AUTH_PROVIDER` (default `users`) and `select_auth_provider()`, which drops the non-selected providers at discovery when more than one is installed. Keycloak stays installed and testable but inert until asked for. Nothing is dropped when only one provider is present, so a keycloak-only host is unaffected, and an unrecognised name leaves the conflict for SM020 to report rather than silently picking a winner. Applied in `create_app`, `make doctor`, and `smpy host gen-pages` so the manifest, the diagnostics, and the running app agree on the module set. Also: - `users` and `background_tasks` call `register_module_settings`, which reads `app.state.settings`, but neither declared a dependency on `Settings` — they were ordered correctly only by way of a transitive edge. Selecting keycloak removed that edge and `background_tasks` crashed at boot. Declare the dependency they actually have. - Fix the one Biome warning (`useOptionalChain` in ModuleForm.tsx). Claude-Session: https://claude.ai/code/session_01RtK7xkMUveHaw6TdK1DF7L
Deploying simple-module-python with
|
| Latest commit: |
7ed4ddc
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://7e820b2a.simple-module-python.pages.dev |
| Branch Preview URL: | https://claude-project-build-compile.simple-module-python.pages.dev |
Branding declares `palette`, Audit Log declares `scroll-text`, and the Doctor page declares `stethoscope` — none were in NavIcon's ICON_MAP, so those three sidebar entries rendered an empty 5x5 span where the icon should be. NavIcon falls back to that spacer for an unknown name instead of throwing, so nothing surfaced the gap: no console warning, no failing test, just a missing icon. Add the three lucide imports and their map entries. Guard it with host/tests/test_nav_icons.py, which walks every module's register_menu_items(), collects the declared icon names, and asserts each one has an ICON_MAP entry. It found `stethoscope`, which a manual grep of the module sources had missed. Claude-Session: https://claude.ai/code/session_01RtK7xkMUveHaw6TdK1DF7L
…ests from it Four gaps in the auth-provider selection, all found by review: 1. An unrecognised name (typo) kept every provider mounted and said nothing. The docstring claimed SM020 would report it, but diagnostics run in development only (app_builder gates run_diagnostics on is_development), so a deployed app silently mounted both providers, rendered two "Logout" entries, and let topological order decide which one won app.state.auth.auth_provider. select_auth_provider now always warns, and takes strict= (passed as `not is_development`) to raise InvalidModuleError in production — matching how discover_modules already treats strictness. 2. The shared `settings` fixture pinned every other env-sensitive knob but not auth_provider, so `SM_AUTH_PROVIDER=keycloak` in a .env — exactly what the new README tells Keycloak users to set — reshaped the suite. Pinning the fixture alone wasn't enough: module conftests build their own Settings. A session-scoped autouse fixture in the plugin pins the env var itself, which outranks .env and so covers every construction site. With keycloak in .env the suite went from 152 failures to 0. 3. test_app_state_has_sm_services read SM_AUTH_PROVIDER from the ambient environment while hard-asserting Keycloak was absent. It now passes auth_provider explicitly, and asserts the Services type before the module-list detail. 4. Blank `SM_AUTH_PROVIDER=` gave auth_provider == '', which matches no provider, while resolve_auth_provider() treated blank as the default — so the host and `make doctor` / gen-pages disagreed about the active provider. A field validator normalises blank to the default, mirroring _normalize_trusted_proxy in the same file. Claude-Session: https://claude.ai/code/session_01RtK7xkMUveHaw6TdK1DF7L
antosubash
marked this pull request as ready for review
August 9, 2026 18:30
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.
Problem
A fresh clone can't boot.
uv sync --all-packagesinstalls every workspace member, so bothusersandkeycloakregister auth-provider entry points,SM020fires as an error, andcreate_appexits before serving a request:make dev, importinghost.main, andmake doctorall died on a clean checkout. The workaround was an explicitSM_MODULES_ENABLEDallowlist, which nothing in.env.exampleor the README mentioned — andframework/hosting/tests/test_app.pycarried the same workaround inline.Change
SM_AUTH_PROVIDER(defaultusers) plusselect_auth_provider(), which drops the non-selected providers at discovery when more than one is installed. Keycloak stays installed and testable but inert until asked for.Behaviour at the edges:
app.state.auth.auth_providerand topological order silently picks the winner. So this always logs a warning, andselect_auth_provider(..., strict=True)raisesInvalidModuleError.create_apppassesstrict=not is_development, becauserun_diagnosticsis gated onis_development— SM020 would never report this in a deployed app.SM_AUTH_PROVIDER=→ normalised to the default by a field validator, so the host agrees withresolve_auth_provider()(used bymake doctor/gen-pages) instead of diverging on''.Applied in
create_app,make doctor, andsmpy host gen-pagesso the pages manifest, the diagnostics, and the running app agree on one module set.Test isolation
The README tells Keycloak users to put
SM_AUTH_PROVIDER=keycloakin.env— andSettingsreads.env, so that config reshaped the whole suite (152 failures). Pinning the sharedsettingsfixture wasn't enough: module conftests build their ownSettings. A session-scoped autouse fixture insimple_module_testpins the env var itself, which outranks.envand so covers every construction site at once. The suite is now green under both configurations.Incidental fixes
usersandbackground_taskscallregister_module_settings, which readsapp.state.settings, but neither declared a dependency onSettings— they were ordered correctly only via a transitive edge through another module. Selecting keycloak removed that edge andbackground_taskscrashed at boot withAttributeError: 'State' object has no attribute 'settings'. Both now declare the dependency they actually have.palette), Audit Log (scroll-text), and the Doctor page (stethoscope) all declared icons with noICON_MAPentry inNavIcon.tsx.NavIconfalls back to an empty<span className="w-5 h-5">for an unknown name rather than throwing, so nothing surfaced it — no console warning, no failing test.host/tests/test_nav_icons.pynow walks every module'sregister_menu_items()and asserts each declared icon is mapped; it foundstethoscope, which a manual grep had missed.useOptionalChaininModuleForm.tsx).Docs
New README section under User management stating that
usersandkeycloakare mutually exclusive, how to switch, and two caveats:dashboard/permissions/audit_log/background_tasksdepend on theUsersmodule sosimple_module_usersmust stay installed, and the local-account flows disappear while Keycloak is active.SM_AUTH_PROVIDERadded to the config table and to.env.example.Verification
uv run pytestuv run pytestwithSM_AUTH_PROVIDER=keycloakin.envnpm testmake lintmake buildmake doctorSM_AUTH_PROVIDER=keycloakInvalidModuleErrornaming the value and the installed providersNew tests:
framework/core/tests/test_auth_provider_selection.py(selection, case-insensitive match, both no-op paths, the warn and strict-raise paths,.envresolution precedence),framework/hosting/tests/test_auth_provider_setting.py(blank/whitespace normalisation, and thatSettingsandresolve_auth_provider()agree on every input), andhost/tests/test_nav_icons.py.Also driven end-to-end in a browser: logged in as a bootstrapped admin and walked all eight sidebar pages with no 4xx/5xx and no console errors.
Not addressed
The dashboard home page renders raw i18n keys (
dashboard.home.title,DASHBOARD.HOME.STATS.TOTAL_USERS) even thoughmodules/dashboard/dashboard/locales/en.jsondefines them. Confirmed pre-existing by screenshotting the same page on the parent commit — out of scope here, worth its own issue.https://claude.ai/code/session_01RtK7xkMUveHaw6TdK1DF7L