From b77abb55b027197880819396652937fba2d9dc67 Mon Sep 17 00:00:00 2001 From: DeepZone Date: Wed, 20 May 2026 20:51:10 +0100 Subject: [PATCH] v0.6.4-beta Alembic logging config hotfix --- README.md | 4 +-- RELEASE_NOTES.md | 15 +++++++++++- backend/alembic.ini | 34 ++++++++++++++++++++++++++ backend/alembic/env.py | 18 ++++++++++++-- backend/app/api/routes_system.py | 2 +- backend/app/core/system_status.py | 2 +- backend/app/main.py | 2 +- backend/pyproject.toml | 2 +- backend/tests/test_api_smoke.py | 2 +- docs/operations/database-migrations.md | 10 ++++---- docs/operations/troubleshooting.md | 15 ++++++++++++ frontend/package-lock.json | 4 +-- frontend/package.json | 2 +- frontend/src/App.tsx | 6 ++--- frontend/src/components/Layout.tsx | 2 +- 15 files changed, 98 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 58e036e..66e95e3 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ RouteForge Logo

- Version + Version License Status Selfhosted @@ -43,7 +43,7 @@ Routing changes often require fast but traceable checks across multiple external ## Current Alpha Status -RouteForge is a **functional beta** release with production-like workflows for read-only validation and demo usage. Current release target: **v0.6.3-beta**. +RouteForge is a **functional beta** release with production-like workflows for read-only validation and demo usage. Current release target: **v0.6.4-beta**. ## Quickstart with Docker Compose diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 49e1113..6957f78 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,18 @@ # Release Notes +## v0.6.4-beta + +**Alembic Logging Config Hotfix** + +### Highlights + +- Fixed Alembic CLI crash with `KeyError: "formatters"`. +- Alembic `env.py` now handles minimal `alembic.ini` files safely. +- Database migration commands now work in Docker selfhosting setups. +- Migration troubleshooting documentation updated. + +--- + ## v0.6.2-beta **Frontend Auth Render Hotfix** @@ -192,7 +205,7 @@ Key capabilities in v0.2.x: - ASN-RPKI batch availability explanations - Demo mode and CI-backed test workflows -## v0.6.3-beta +## v0.6.4-beta **Migration UX & Auth Visibility Hotfix** diff --git a/backend/alembic.ini b/backend/alembic.ini index a163fe9..e430112 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -1,3 +1,37 @@ [alembic] script_location = alembic sqlalchemy.url = sqlite:///./routeforge.db + +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/backend/alembic/env.py b/backend/alembic/env.py index 731138b..8c1bff4 100644 --- a/backend/alembic/env.py +++ b/backend/alembic/env.py @@ -1,6 +1,8 @@ from __future__ import annotations from logging.config import fileConfig +from pathlib import Path +import configparser from alembic import context from sqlalchemy import engine_from_config, pool @@ -12,8 +14,20 @@ config = context.config config.set_main_option("sqlalchemy.url", settings.database_url) -if config.config_file_name is not None: - fileConfig(config.config_file_name) +def _configure_logging() -> None: + if not config.config_file_name: + return + config_path = Path(config.config_file_name) + if not config_path.exists(): + return + parser = configparser.ConfigParser() + parser.read(config_path) + required = {"loggers", "handlers", "formatters"} + if required.issubset(set(parser.sections())): + fileConfig(config_path) + + +_configure_logging() target_metadata = Base.metadata diff --git a/backend/app/api/routes_system.py b/backend/app/api/routes_system.py index cc4e7f3..a6f8eb5 100644 --- a/backend/app/api/routes_system.py +++ b/backend/app/api/routes_system.py @@ -12,7 +12,7 @@ def system_info(): return { 'name': 'RouteForge', - 'version': 'v0.6.3-beta', + 'version': 'v0.6.4-beta', 'demo_mode': settings.demo_mode, 'read_only': True, 'data_sources': ['RIPEstat', 'RIPEstat Whois/Registry'], diff --git a/backend/app/core/system_status.py b/backend/app/core/system_status.py index 9d03244..e885b7c 100644 --- a/backend/app/core/system_status.py +++ b/backend/app/core/system_status.py @@ -139,7 +139,7 @@ def build_system_status(engine: Engine | None) -> dict: return { "status": "ok", "name": settings.app_name, - "version": "v0.6.3-beta", + "version": "v0.6.4-beta", "read_only": True, "mode": "demo" if settings.demo_mode else "live", "demo_mode": settings.demo_mode, diff --git a/backend/app/main.py b/backend/app/main.py index 86167e0..ac2a577 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -17,7 +17,7 @@ logging.basicConfig(level=getattr(logging, settings.log_level.upper(), logging.INFO)) logger = logging.getLogger("routeforge") -app = FastAPI(title="RouteForge", version="0.6.3") +app = FastAPI(title="RouteForge", version="0.6.4") app.add_middleware( CORSMiddleware, diff --git a/backend/pyproject.toml b/backend/pyproject.toml index ac11b34..d54f240 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "routeforge-backend" -version = "0.6.3" +version = "0.6.4" description = "RouteForge backend" license = "AGPL-3.0-or-later" requires-python = ">=3.12" diff --git a/backend/tests/test_api_smoke.py b/backend/tests/test_api_smoke.py index ac45309..e1876a3 100644 --- a/backend/tests/test_api_smoke.py +++ b/backend/tests/test_api_smoke.py @@ -177,7 +177,7 @@ def test_system_status_endpoint() -> None: response = client.get('/api/system/status') assert response.status_code == 200 payload = response.json() - assert payload.get('version') == 'v0.6.3-beta' + assert payload.get('version') == 'v0.6.4-beta' assert payload.get('read_only') is True assert payload.get('database', {}).get('status') assert payload.get('ripestat', {}).get('cache_ttl_seconds') is not None diff --git a/docs/operations/database-migrations.md b/docs/operations/database-migrations.md index cf4173e..b6e7a53 100644 --- a/docs/operations/database-migrations.md +++ b/docs/operations/database-migrations.md @@ -13,16 +13,16 @@ RouteForge uses Alembic for schema lifecycle tracking from `v0.5.3-beta`. ## Commands ```bash -cd backend -alembic history -alembic current -alembic upgrade head +docker compose exec backend alembic current +docker compose exec backend alembic heads +docker compose exec backend alembic upgrade head ``` ## Existing pre-baseline databases If the schema already exists from historical `create_all`, mark baseline first: ```bash -alembic stamp 0001_initial_schema +docker compose exec backend alembic stamp 0001_initial_schema +docker compose exec backend alembic upgrade head ``` ## Auto migration diff --git a/docs/operations/troubleshooting.md b/docs/operations/troubleshooting.md index 4530d7c..6cd3136 100644 --- a/docs/operations/troubleshooting.md +++ b/docs/operations/troubleshooting.md @@ -18,3 +18,18 @@ docker compose down docker compose run --rm --user root backend sh -c "mkdir -p /app/data && chown -R routeforge:routeforge /app/data && chmod -R u+rwX /app/data" docker compose up -d --build ``` + + +## KeyError: 'formatters' (Alembic) + +### Ursache + +Ältere Versionen hatten eine minimale `backend/alembic.ini` ohne Logging-Sektionen, während `backend/alembic/env.py` `fileConfig(...)` aufgerufen hat. + +### Fix + +Update auf `v0.6.4-beta` oder neuer, dann: + +```bash +docker compose exec backend alembic upgrade head +``` diff --git a/frontend/package-lock.json b/frontend/package-lock.json index ae2f0ab..c44e3eb 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "routeforge-frontend", - "version": "0.6.3", + "version": "0.6.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "routeforge-frontend", - "version": "0.6.3", + "version": "0.6.4", "license": "AGPL-3.0-or-later", "dependencies": { "react": "^18.3.1", diff --git a/frontend/package.json b/frontend/package.json index 5bbbad7..dbfd135 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "routeforge-frontend", - "version": "0.6.3", + "version": "0.6.4", "private": true, "license": "AGPL-3.0-or-later", "type": "module", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7d5a4f9..8cdcc2d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -65,19 +65,19 @@ export default function App() { if (authMode === 'login') return if (authMode === 'error') return

{authError}
- const systemLine = system ? `${system.name} ${system.version} · mode=${system.demo_mode ? 'DEMO' : 'LIVE'} · read_only=${String(system.read_only)}` : 'RouteForge v0.6.3-beta · read-only preflight checks' + const systemLine = system ? `${system.name} ${system.version} · mode=${system.demo_mode ? 'DEMO' : 'LIVE'} · read_only=${String(system.read_only)}` : 'RouteForge v0.6.4-beta · read-only preflight checks' const title = { dashboard: 'Dashboard', asn: 'ASN Check', prefix: 'Prefix Check', preflight: 'Preflight Check', reports: 'Reports', system: 'System Status', about: 'About RouteForge' }[active] const proxyStatus = systemStatusError ? 'ERROR' : 'OK' const migrationStatus = systemStatus?.database?.migration_status || 'unknown' const migrationsBlocked = migrationStatus === 'behind' || migrationStatus === 'error' return - {active === 'dashboard' &&

RouteForge v0.6.3-beta

Modernes read-only Operator-Tool für Preflight Checks von ASN, Prefix, RPKI und Registry/IRR.

{migrationsBlocked &&
Database migrations are required before using RouteForge.
}
} + {active === 'dashboard' &&

RouteForge v0.6.4-beta

Modernes read-only Operator-Tool für Preflight Checks von ASN, Prefix, RPKI und Registry/IRR.

{migrationsBlocked &&
Database migrations are required before using RouteForge.
}
} {active === 'asn' && } {active === 'prefix' && } {active === 'preflight' && } {active === 'reports' &&

Reports

{reports.length===0 ?
Noch keine Reports vorhanden.
:
{reports.map(r=>)}
{r.summary}
}
} {active === 'system' &&
{systemStatusError &&
{systemStatusError}
}{migrationsBlocked &&
Database migrations are required before using RouteForge.
}{systemStatus &&
Version: {systemStatus.version}
Mode: {systemStatus.mode}
API Proxy: {proxyStatus}
Migration Status: {migrationStatus}
}
} - {active === 'about' &&

Version: v0.6.3-beta

} + {active === 'about' &&

Version: v0.6.4-beta

}
} diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index c9eed16..37cd443 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -35,7 +35,7 @@ export function Layout({ children, active, onNav, systemLine, title, demoMode, c {currentUser && Angemeldet als {currentUser.username} · {currentUser.role}} {demoMode ? 'DEMO' : 'LIVE'} READ-ONLY - v0.6.3-beta + v0.6.4-beta