From b40a6a5a4ce4b665912e24bf3e706a109eae735f Mon Sep 17 00:00:00 2001 From: Ramon Date: Thu, 6 Aug 2026 23:21:21 +0200 Subject: [PATCH] feat(apps): toon uit welk sjabloon een app gerenderd wordt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sjablonen komen uit de git-cache van een app-repo. Loopt die achter, dan trekt 'Instellingen wijzigen' een oude versie over een werkende app heen zonder dat iets dat zegt — precies wat er bij UniFi gebeurde. - git.cache_status geeft commit, datum, synchronisatiemoment en de VERSION uit de cache terug. - /api/repos en /api/stacks//config sturen die stand mee, met een outdated-vlag als de repo-VERSION ouder is dan de draaiende versie. - Het wijzigscherm toont repo, branch en hoe lang geleden er opgehaald is, en waarschuwt bij een achterlopende cache. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01H9Vcwu7owJZ3nj5dCf4VFT --- CHANGELOG.md | 15 +++++++ VERSION | 2 +- server-up/app.py | 40 ++++++++++++++++- server-up/core/git.py | 38 ++++++++++++++++ server-up/templates/index.html | 43 +++++++++++++++++- server-up/translations/en.json | 5 +++ server-up/translations/nl.json | 5 +++ tests/test_repo_urls.py | 82 ++++++++++++++++++++++++++++++++++ 8 files changed, 226 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 041d960..957d35a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# v0.8.25-beta — Zichtbaar uit welk sjabloon een app komt + +Sjablonen komen uit de git-cache van een app-repo, niet uit het image van +Server Up. Die twee kunnen ver uit elkaar lopen: staat de repo op een branch +die achterloopt, dan rendert 'Instellingen wijzigen' een oud sjabloon over een +werkende app heen — en daar was aan niets te zien. + +- **Het wijzigscherm noemt zijn bron.** Repo, branch en hoe lang geleden er + gesynchroniseerd is, boven de velden waar je op opslaan drukt. +- **Een achterlopende repo waarschuwt.** Levert de repo zelf een `VERSION` mee + (zoals deze) en is die ouder dan de draaiende versie, dan zegt het scherm dat + met de versie erbij. +- **De repolijst toont de stand.** Commit en synchronisatiemoment per repo, + zodat je niet hoeft te gokken of een sync nog nodig is. + # v0.8.24-beta — Koppelen laat je database met rust Koppelen aan het gedeelde netwerk hing élke service van een stack eraan, ook de diff --git a/VERSION b/VERSION index 45612bf..0a2307a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.24-beta +0.8.25-beta diff --git a/server-up/app.py b/server-up/app.py index cd1e55e..94affa3 100644 --- a/server-up/app.py +++ b/server-up/app.py @@ -1115,7 +1115,25 @@ def api_stack_config(name): app_networks=meta.get("app_networks") or [], app_services=meta.get("app_services") or [], source=meta.get("source", ""), repo_id=meta.get("repo_id", ""), - name=meta.get("name", name)) + name=meta.get("name", name), + # Waar dit sjabloon vandaan komt. Opslaan rendert de app + # opnieuw uit déze cache; loopt die achter, dan haal je een + # oude versie over je werkende stack heen. + source_repo=_sjabloon_herkomst(meta.get("repo_id", ""))) + + +def _sjabloon_herkomst(rid: str) -> dict: + """Repo, branch en stand van de cache waar een sjabloon uit komt.""" + if not rid: + return {} + repo = next((r for r in cfg.load().get("APP_REPOS", []) + if isinstance(r, dict) and r.get("id") == rid), None) + if not repo: + return {"id": rid, "missing": True} + stand = git.cache_status(rid) + stand["outdated"] = _cache_loopt_achter(stand.get("version", "")) + return {"id": rid, "name": repo.get("name") or rid, + "branch": repo.get("branch") or "main", **stand} @app.route("/api/stacks//reconfigure", methods=["POST"]) @@ -1432,10 +1450,28 @@ def api_repos(): if r["id"] in gezien: continue gezien.add(r["id"]) - result.append({**r, "cloned": git.cache_dir(r["id"]).exists()}) + # Mét de stand van de cache: welke commit, van wanneer, en loopt de + # meegeleverde VERSION achter op wat hier draait? Zonder dat is niet te + # zien dat een app een oud sjabloon rendert. + stand = git.cache_status(r["id"]) + stand["outdated"] = _cache_loopt_achter(stand.get("version", "")) + result.append({**r, "cloned": git.cache_dir(r["id"]).exists(), + "cache": stand}) return jsonify(result) +def _cache_loopt_achter(repo_versie: str) -> bool: + """Is de VERSION in de git-cache ouder dan de draaiende Server Up? + + Alleen zinvol voor repo's die zelf een VERSION meeleveren; voor de rest + zegt dit niets en blijft het antwoord False. + """ + if not repo_versie: + return False + hier, daar = updater._parse(VERSION), updater._parse(repo_versie) + return bool(hier and daar and daar < hier) + + @app.route("/api/repos", methods=["POST"]) def api_repos_add(): d = request.json or {} diff --git a/server-up/core/git.py b/server-up/core/git.py index 8ef348e..8569235 100644 --- a/server-up/core/git.py +++ b/server-up/core/git.py @@ -48,6 +48,44 @@ def cache_dir(repo_id: str) -> Path: return CACHE / repo_id +def cache_status(repo_id: str) -> dict: + """Welke versie van de sjablonen staat er in de cache van deze repo? + + Sjablonen komen hiervandaan en niet uit het image van Server Up. Die twee + kunnen ver uit elkaar lopen: staat de repo op een branch die achterloopt, + dan rendert een app een oud sjabloon terwijl de code splinternieuw is — en + daar is aan niets te zien. + """ + d = cache_dir(repo_id) + if not (d / ".git").exists(): + return {"cloned": False} + uit: dict = {"cloned": True} + try: + r = subprocess.run(["git", "log", "-1", "--format=%h%x1f%cI%x1f%s"], + cwd=str(d), capture_output=True, text=True, timeout=10) + if r.returncode == 0 and "\x1f" in r.stdout: + sha, datum, onderwerp = r.stdout.strip().split("\x1f", 2) + uit.update(commit=sha, commit_date=datum, subject=onderwerp[:120]) + except Exception: + pass + # Wanneer is er voor het laatst opgehaald? FETCH_HEAD wordt bij elke fetch + # aangeraakt; bij een verse kloon bestaat hij nog niet. + for naam in ("FETCH_HEAD", "HEAD"): + p = d / ".git" / naam + if p.exists(): + uit["synced_at"] = p.stat().st_mtime + break + # Repo's die zelf een VERSION meeleveren (zoals deze) verraden daarmee of de + # cache achterloopt op de draaiende versie. + versie = d / "VERSION" + if versie.is_file(): + try: + uit["version"] = versie.read_text(encoding="utf-8").strip()[:40] + except Exception: + pass + return uit + + def clone_or_pull(repo: dict, log_fn=None) -> tuple[bool, str]: _ensure_git_safe() url = repo.get("url", "").strip() diff --git a/server-up/templates/index.html b/server-up/templates/index.html index e4f8e31..223b269 100644 --- a/server-up/templates/index.html +++ b/server-up/templates/index.html @@ -1392,9 +1392,17 @@ tailwind.config = { Git Repositories