feat(release): add named release anchors with codenames and roadmap integration
- Add release_names.json mapping version ranges to codenames - Add release_name property to Settings in app/config.py - Update build metadata script to include codename in RUNTIME_INFO - Display release codename in status dashboard and page footer - Inject release_name globally via template response wrapper - Update ROADMAP.md with codenames for all milestone releases - Add docs/ReleaseNaming.md with naming guide and best practices - Add comprehensive tests for release name resolution Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any, List, Optional, Union
|
||||
|
||||
@@ -599,5 +600,51 @@ class Settings(BaseSettings):
|
||||
# Return basic info if file not found
|
||||
return f"Version: {self.version}\nBuild Date: {self.build_date}\nGit SHA: {self.git_sha}"
|
||||
|
||||
@property
|
||||
def release_name(self) -> str | None:
|
||||
"""Get the release codename for the current version from release_names.json.
|
||||
|
||||
Looks up the current version's minor version prefix (e.g., '0.5' for '0.5.3')
|
||||
in release_names.json to find the associated codename. Returns None if no
|
||||
codename is defined for the current version.
|
||||
|
||||
Returns:
|
||||
The release codename string, or None if not found.
|
||||
"""
|
||||
version = self.version
|
||||
if not version or version == "unknown":
|
||||
return None
|
||||
|
||||
release_names_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), "release_names.json")
|
||||
if not os.path.exists(release_names_file):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(release_names_file, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
releases = data.get("releases", {})
|
||||
|
||||
# Try exact version match first (e.g., "0.5.0")
|
||||
if version in releases:
|
||||
return releases[version].get("codename")
|
||||
|
||||
# Try minor version prefix (e.g., "0.5" for "0.5.3")
|
||||
parts = version.split(".")
|
||||
if len(parts) >= 2:
|
||||
minor_prefix = f"{parts[0]}.{parts[1]}"
|
||||
if minor_prefix in releases:
|
||||
return releases[minor_prefix].get("codename")
|
||||
|
||||
# Try major version prefix (e.g., "1" for "1.0.0")
|
||||
if len(parts) >= 1:
|
||||
major_prefix = parts[0]
|
||||
if major_prefix in releases:
|
||||
return releases[major_prefix].get("codename")
|
||||
|
||||
return None
|
||||
except (json.JSONDecodeError, KeyError, IndexError):
|
||||
return None
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
@@ -30,6 +30,7 @@ def template_response_with_version(*args, **kwargs):
|
||||
# If context dict is provided, add version to it
|
||||
if len(args) >= 2 and isinstance(args[1], dict):
|
||||
args[1].setdefault("version", settings.version)
|
||||
args[1].setdefault("release_name", getattr(settings, "release_name", None))
|
||||
# Inject CSRF token from request state when available
|
||||
req = args[1].get("request")
|
||||
if req is not None and hasattr(req.state, "csrf_token"):
|
||||
@@ -38,6 +39,7 @@ def template_response_with_version(*args, **kwargs):
|
||||
args[1].setdefault("ui_default_color_scheme", getattr(settings, "ui_default_color_scheme", "system"))
|
||||
elif "context" in kwargs and isinstance(kwargs["context"], dict):
|
||||
kwargs["context"].setdefault("version", settings.version)
|
||||
kwargs["context"].setdefault("release_name", getattr(settings, "release_name", None))
|
||||
req = kwargs["context"].get("request")
|
||||
if req is not None and hasattr(req.state, "csrf_token"):
|
||||
kwargs["context"].setdefault("csrf_token", req.state.csrf_token)
|
||||
|
||||
@@ -73,12 +73,16 @@ async def status_dashboard(request: Request):
|
||||
# Get notification URLs for the notification box
|
||||
notification_urls = getattr(settings, "notification_urls", [])
|
||||
|
||||
# Get release codename if available
|
||||
release_name = getattr(settings, "release_name", None)
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"status_dashboard.html",
|
||||
{
|
||||
"request": request,
|
||||
"providers": providers,
|
||||
"app_version": settings.version,
|
||||
"release_name": release_name,
|
||||
"build_date": build_date,
|
||||
"debug_enabled": getattr(settings, "debug", False),
|
||||
"last_check": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
|
||||
Reference in New Issue
Block a user