diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml index e82ab6b..d8ae0d0 100644 --- a/.gitea/workflows/docker.yml +++ b/.gitea/workflows/docker.yml @@ -10,8 +10,20 @@ env: REGISTRY: gitea.lokcal.de jobs: + version: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.v.outputs.version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - id: v + run: echo "version=$(./scripts/version.sh)" >> "$GITHUB_OUTPUT" + backend: runs-on: ubuntu-latest + needs: version steps: - uses: actions/checkout@v4 - name: Login to registry @@ -25,12 +37,15 @@ jobs: with: context: ./backend push: true + build-args: | + VERSION=${{ needs.version.outputs.version }} tags: | ${{ env.REGISTRY }}/${{ gitea.repository_owner }}/streamrip-ui-backend:latest - ${{ env.REGISTRY }}/${{ gitea.repository_owner }}/streamrip-ui-backend:${{ gitea.sha }} + ${{ env.REGISTRY }}/${{ gitea.repository_owner }}/streamrip-ui-backend:${{ needs.version.outputs.version }} frontend: runs-on: ubuntu-latest + needs: version steps: - uses: actions/checkout@v4 - name: Login to registry @@ -46,4 +61,4 @@ jobs: push: true tags: | ${{ env.REGISTRY }}/${{ gitea.repository_owner }}/streamrip-ui-frontend:latest - ${{ env.REGISTRY }}/${{ gitea.repository_owner }}/streamrip-ui-frontend:${{ gitea.sha }} + ${{ env.REGISTRY }}/${{ gitea.repository_owner }}/streamrip-ui-frontend:${{ needs.version.outputs.version }} diff --git a/README.md b/README.md index db56601..88a92db 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,19 @@ services: CI (`.gitea/workflows/ci.yml`): Backend-Tests + Docker-Builds beider Images bei jedem Push auf `main`. +## Versionierung (automatisch, CalVer) + +`scripts/version.sh` erzeugt `DATUM.Commitanzahl` (z. B. `2026.10.04.7`, sonst `dev`). +Lokal vor dem Bauen exportieren, die Action macht das selbst: + +```bash +export APP_VERSION=$(./scripts/version.sh) +docker compose up --build -d +``` + +Die Version steckt im Backend-Image (`GET /api/version`), steht im Header der UI +und taggt die Registry-Images (`:latest` + `:2026.10.04.7`). + ## API-Überblick (Backend) - `GET /api/search?type=track|album|artist|playlist|podcast&q=..` diff --git a/backend/Dockerfile b/backend/Dockerfile index f95ae20..945ad06 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -21,6 +21,9 @@ RUN pip install --no-cache-dir -r requirements.txt && pip install --no-cache-dir COPY app ./app RUN chown -R appuser:appuser /app +ARG VERSION=dev +ENV APP_VERSION=$VERSION + USER appuser EXPOSE 8000 diff --git a/backend/app/core/settings.py b/backend/app/core/settings.py index 08fc0e5..50fd4b8 100644 --- a/backend/app/core/settings.py +++ b/backend/app/core/settings.py @@ -9,6 +9,7 @@ CONFIG_PATH = os.environ.get( DOWNLOAD_DIR = os.environ.get( "STREAMRIP_DOWNLOAD_DIR", str(Path.home() / "StreamripDownloads") ) +APP_VERSION = os.environ.get("APP_VERSION", "dev") DEFAULT_QUALITY = int(os.environ.get("STREAMRIP_DEFAULT_QUALITY", "2")) DEFAULT_CODEC = os.environ.get("STREAMRIP_DEFAULT_CODEC", "") SOURCES = ["deezer"] diff --git a/backend/app/main.py b/backend/app/main.py index 2bd0b1e..fa46218 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -19,6 +19,11 @@ def health(): return {"status": "ok"} +@app.get("/api/version") +def version(): + return {"version": settings.APP_VERSION} + + @app.get("/api/sources") def sources(): return {"sources": settings.SOURCES} diff --git a/backend/tests/test_health.py b/backend/tests/test_health.py index 7c66d77..1233009 100644 --- a/backend/tests/test_health.py +++ b/backend/tests/test_health.py @@ -14,3 +14,13 @@ def test_sources_deezer_only(): r = c.get("/api/sources") assert r.status_code == 200 assert r.json()["sources"] == ["deezer"] + + +def test_version_endpoint(monkeypatch): + from app.core import settings + + monkeypatch.setattr(settings, "APP_VERSION", "2026.10.04.7") + c = TestClient(app) + r = c.get("/api/version") + assert r.status_code == 200 + assert r.json() == {"version": "2026.10.04.7"} diff --git a/docker-compose.yml b/docker-compose.yml index 2c93626..784bcb1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,7 @@ services: args: UID: ${UID:-1000} GID: ${GID:-1000} + VERSION: ${APP_VERSION:-dev} ports: - "${BACKEND_HOST_PORT:-8000}:${BACKEND_PORT:-8000}" environment: diff --git a/frontend/app/components/Header.tsx b/frontend/app/components/Header.tsx index 9835c06..8da9000 100644 --- a/frontend/app/components/Header.tsx +++ b/frontend/app/components/Header.tsx @@ -18,6 +18,7 @@ const NAV = [ export default function Header() { const pathname = usePathname(); const [backendOk, setBackendOk] = useState(null); + const [appVersion, setAppVersion] = useState(""); const [theme, setTheme] = useState<"dark" | "light">("dark"); const [basketCount, setBasketCount] = useState(0); @@ -63,6 +64,12 @@ export default function Header() { .catch(() => { if (alive) setBackendOk(false); }); + fetch("/api/version") + .then((r) => r.json()) + .then((b) => { + if (alive && b?.version) setAppVersion(b.version); + }) + .catch(() => {}); return () => { alive = false; }; @@ -102,6 +109,11 @@ export default function Header() { }} /> streamrip-ui + {appVersion && ( + + v{appVersion} + + )}