versioning

This commit is contained in:
2025-12-14 23:20:51 -05:00
parent 596cb97a28
commit 68a4f1800c
5 changed files with 81 additions and 4 deletions

5
Jenkinsfile vendored
View File

@@ -10,6 +10,7 @@ pipeline {
VUE_CONTAINER_NAME = "chore-app-frontend" VUE_CONTAINER_NAME = "chore-app-frontend"
FLASK_CONTAINER_NAME = "chore-app-backend" FLASK_CONTAINER_NAME = "chore-app-backend"
NETWORK_NAME = "chore-app-net" NETWORK_NAME = "chore-app-net"
BASE_VERSION = '1.0.0'
} }
stages { stages {
@@ -32,8 +33,7 @@ pipeline {
stage('Build Backend (Flask) App') { stage('Build Backend (Flask) App') {
steps { steps {
dir('.') { dir('.') {
sh 'docker build -t ${BACKEND_IMAGE} .' sh """docker build --build-arg APP_BUILD=${BUILD_NUMBER} -t chore-app-backend:${BASE_VERSION} -t chore-app-backend:${BASE_VERSION}-${BUILD_NUMBER} -t chore-app-backend:latest ."""
sh 'docker tag ${BACKEND_IMAGE} ${BACKEND_IMAGE_LATEST}'
} }
} }
} }
@@ -65,6 +65,7 @@ pipeline {
docker run -d \\ docker run -d \\
--name ${FLASK_CONTAINER_NAME} \\ --name ${FLASK_CONTAINER_NAME} \\
--network ${NETWORK_NAME} \\ --network ${NETWORK_NAME} \\
-e BUILD_NUMBER=${BUILD_NUMBER} \\
-v ${FLASK_CONTAINER_NAME}_data:/app/data \\ -v ${FLASK_CONTAINER_NAME}_data:/app/data \\
${BACKEND_IMAGE_LATEST} ${BACKEND_IMAGE_LATEST}
""" """

12
config/version.py Normal file
View File

@@ -0,0 +1,12 @@
# python
# file: config/version.py
import os
BASE_VERSION = "1.0.0" # update manually when releasing features
def get_full_version() -> str:
"""
Return semantic version with optional Jenkins build metadata, e.g. 1.2.3+build.456.
"""
build = os.environ.get("BUILD_NUMBER") or os.environ.get("APP_BUILD")
return f"{BASE_VERSION}+build.{build}" if build else BASE_VERSION

30
docker-compose.yml Normal file
View File

@@ -0,0 +1,30 @@
# yaml
version: '3.8'
services:
chore-app-backend:
image: devserver.lan:5900/chore-app-backend:production
container_name: chore-app-backend
restart: unless-stopped
expose:
- "5000"
networks:
- chore-app-net
volumes:
- chore-app-backend-data:/app/data # persists backend data
chore-app-frontend:
image: devserver.lan:5900/chore-app-frontend:production
container_name: chore-app-frontend
restart: unless-stopped
ports:
- "4600:443"
networks:
- chore-app-net
networks:
chore-app-net:
driver: bridge
volumes:
chore-app-backend-data: {}

View File

@@ -1,12 +1,13 @@
import sys, logging, os import sys, logging, os
from config.paths import get_user_image_dir from config.paths import get_user_image_dir
from flask import Flask, request from flask import Flask, request, jsonify
from flask_cors import CORS from flask_cors import CORS
from api.child_api import child_api from api.child_api import child_api
from api.image_api import image_api from api.image_api import image_api
from api.reward_api import reward_api from api.reward_api import reward_api
from api.task_api import task_api from api.task_api import task_api
from config.version import get_full_version
from events.broadcaster import Broadcaster from events.broadcaster import Broadcaster
from events.sse import sse_response_for_user, send_to_user from events.sse import sse_response_for_user, send_to_user
from db.default import initializeImages from db.default import initializeImages
@@ -29,6 +30,10 @@ app.register_blueprint(task_api)
app.register_blueprint(image_api) app.register_blueprint(image_api)
CORS(app) CORS(app)
@app.route("/version")
def api_version():
return jsonify({"version": get_full_version()})
@app.route("/events") @app.route("/events")
def events(): def events():
# Authenticate user or read a token # Authenticate user or read a token

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { computed } from 'vue' import { computed, ref, onMounted } from 'vue'
import LoginButton from '../components/LoginButton.vue' import LoginButton from '../components/LoginButton.vue'
const router = useRouter() const router = useRouter()
@@ -23,6 +23,20 @@ const showBack = computed(
route.name === 'NotificationView' route.name === 'NotificationView'
), ),
) )
// Version fetching
const appVersion = ref('')
onMounted(async () => {
try {
const resp = await fetch('/api/version')
if (resp.ok) {
const data = await resp.json()
appVersion.value = data.version || ''
}
} catch (e) {
appVersion.value = ''
}
})
</script> </script>
<template> <template>
@@ -142,6 +156,8 @@ const showBack = computed(
<main class="main-content"> <main class="main-content">
<router-view /> <router-view />
</main> </main>
<div v-if="appVersion" class="app-version">v{{ appVersion }}</div>
</div> </div>
</template> </template>
@@ -251,6 +267,19 @@ const showBack = computed(
align-self: start; align-self: start;
} }
.app-version {
position: fixed;
right: 18px;
bottom: 12px;
font-size: 0.92rem;
color: #cbd5e1; /* Brighter slate-200 */
opacity: 0.85;
z-index: 100;
pointer-events: none;
user-select: none;
font-family: monospace;
}
@media (max-width: 480px) { @media (max-width: 480px) {
.back-btn { .back-btn {
padding: 0.45rem 0.75rem; padding: 0.45rem 0.75rem;