From 4c6857a00f39f81611bbbf5e7333e4c039844e5e Mon Sep 17 00:00:00 2001 From: MarekWo Date: Sun, 26 Jul 2026 19:20:57 +0200 Subject: [PATCH] feat: numbered releases (2.1.0) with git tags and GitHub releases The app already knew exactly which build was running - a calendar version of commit date plus short hash - but nothing gave a release a name users could quote, and the repo had no tags at all. Adds a VERSION file as the single source of truth for a SemVer release number, read by app/version.py alongside the existing build string rather than replacing it: the number is for people, the build is for pinning down a deploy, and both ship in /api/version and the template context. The menu shows the release first with the build underneath. #versionText still holds the build string, because the remote-update poller compares it to detect that the server came back on a new build. Resolution order is unchanged (frozen file > git > fallback), and a frozen file written before this change still yields a correct release number, so an already-deployed server does not need re-freezing to stay sane. The container has no git, hence COPY VERSION into the image - otherwise a plain 'docker compose build' reports 0.0.0. scripts/release.sh cuts a release from main: it refuses a dirty tree, a wrong branch, a malformed number or an existing tag, extracts the notes from the matching whatsnew section, then tags, pushes and publishes via gh. Numbering starts at 2.1.0 rather than 1.x: the v2 line has been in production since March and 'v1' is the archived pre-migration branch, so 1.x would have been ambiguous. Sections in whatsnew before 2.1.0 keep their date-only headings - they were never tagged. Co-Authored-By: Claude Opus 5 --- Dockerfile | 5 ++- README.md | 2 +- VERSION | 1 + app/main.py | 3 +- app/routes/api.py | 7 +++- app/templates/base.html | 5 ++- app/version.py | 42 ++++++++++++++++++--- docs/architecture.md | 25 +++++++++++++ docs/whatsnew.md | 11 +++++- scripts/release.sh | 82 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 VERSION create mode 100755 scripts/release.sh diff --git a/Dockerfile b/Dockerfile index 9032f71..863b74b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,10 @@ RUN pip install --no-cache-dir -r requirements.txt \ && rm -rf /var/lib/apt/lists/* # Copy application code -# Note: Run 'python -m app.version freeze' before build to include version info +# Note: Run 'python -m app.version freeze' before build to include version info. +# VERSION holds the release number and is read at /app/VERSION when no frozen +# version file is present (e.g. a plain 'docker compose build' during dev). +COPY VERSION ./ COPY app/ ./app/ # Expose Flask port diff --git a/README.md b/README.md index a933bb9..a2cf27d 100644 --- a/README.md +++ b/README.md @@ -335,7 +335,7 @@ python3 -m app.version freeze docker compose up -d --build ``` -The `python3 -m app.version freeze` command captures the current Git version (date + commit hash) for display in the app menu. +The `python3 -m app.version freeze` command captures the current Git build (date + commit hash) for display in the app menu, underneath the release number from the `VERSION` file. Released versions are tagged `v` and published at [Releases](https://github.com/MarekWo/mc-webui/releases); see [Versioning & Releases](docs/architecture.md#versioning--releases) for how a release is cut. ### Testing experimental features diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..7ec1d6d --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.1.0 diff --git a/app/main.py b/app/main.py index dff6819..1a1a719 100644 --- a/app/main.py +++ b/app/main.py @@ -22,7 +22,7 @@ from app.log_handler import MemoryLogHandler from app.observer import ObserverManager from app.routes.views import views_bp from app.routes.api import api_bp -from app.version import VERSION_STRING, GIT_BRANCH +from app.version import RELEASE_VERSION, VERSION_STRING, GIT_BRANCH # Configure logging logging.basicConfig( @@ -230,6 +230,7 @@ def create_app(): @app.context_processor def inject_globals(): return { + 'release': RELEASE_VERSION, 'version': VERSION_STRING, 'git_branch': GIT_BRANCH, 'transport_type': config.transport_type, diff --git a/app/routes/api.py b/app/routes/api.py index d61f92a..fad6b6e 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -4962,14 +4962,19 @@ def get_version(): JSON with version info: { "success": true, + "release": "2.1.0", "version": "2025.01.18+576c8ca9", "docker_tag": "2025.01.18-576c8ca9", "branch": "dev" } + + 'release' is the human-facing release number; 'version' stays the exact + build and remains what the update poller compares. """ - from app.version import VERSION_STRING, DOCKER_TAG, GIT_BRANCH + from app.version import RELEASE_VERSION, VERSION_STRING, DOCKER_TAG, GIT_BRANCH return jsonify({ 'success': True, + 'release': RELEASE_VERSION, 'version': VERSION_STRING, 'docker_tag': DOCKER_TAG, 'branch': GIT_BRANCH diff --git a/app/templates/base.html b/app/templates/base.html index b2ab716..742d3a2 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -96,8 +96,11 @@
- {{ version }} + {{ release }} {{ git_branch }} + + {{ version }}