From 842615a562a494e87af1787da8e4c5406efecd37 Mon Sep 17 00:00:00 2001 From: Ramon Date: Mon, 3 Aug 2026 18:55:02 +0200 Subject: [PATCH] v0.7.97-beta - repo's bijwerken werkte niet meer - `git pull --rebase --autostash` stopte met "Cannot rebase onto multiple branches" zodra branch..merge meer dan een waarde had; daarmee werkte geen enkele repo meer bij en bleef ook de app store leeg, want die leest uit dezelfde cache - tweede fout in dezelfde regel: `git pull` zonder argumenten haalt de tak op waarop ooit gekloond is, dus de branch uit de repo-instellingen werd genegeerd. Van main naar dev omzetten deed stilzwijgend niets - het bijwerken haalt nu de ingestelde tak op en zet de cache daar hard op: ongevoelig voor de upstream-instellingen, volgt een gewijzigde branch, en laat zich niet blokkeren door lokale wijzigingen - --doctor vraagt Server Up zelf welke LIBRARY_DIR en DATA_DIR hij gebruikt, hoeveel stacks hij daar ziet en hoeveel apps er in de store-cache zitten. Dat verschil tussen "wat staat er op de host" en "waar kijkt de app" was nergens zichtbaar Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Q9eqpADJSRs49SoGGr4NAy --- CHANGELOG.md | 25 +++++++++++ VERSION | 2 +- install.sh | 49 ++++++++++++++++++++++ server-up/core/git.py | 22 +++++++++- tests/test_repo_urls.py | 93 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 188 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 303d612..55b5525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# v0.7.97-beta — Repo's bijwerken werkte niet meer + +**`fatal: Cannot rebase onto multiple branches.`** Het bijwerken van een +app-repo gebruikte `git pull --rebase --autostash`, en dat commando leunt op de +upstream-instellingen van de lokale kloon. Staan er meerdere merge-doelen in +`branch..merge`, dan stopt git ermee en werkt er niets meer bij — ook de +app store niet, want die leest uit diezelfde cache. + +Bij het naspelen bleek er een tweede fout in dezelfde regel te zitten: **`git +pull` zonder argumenten haalt de tak op waarop ooit gekloond is.** De branch uit +de repo-instellingen werd dus genegeerd. Zette je die van `main` naar `dev`, dan +bleef je stilzwijgend `main` krijgen. + +De cache is van Server Up zelf; er staat niets in dat bewaard hoeft te blijven. +Het bijwerken haalt nu de ingestelde tak op en zet de cache daar hard op. Dat is +ongevoelig voor ontspoorde upstream-instellingen, volgt een gewijzigde branch +wél, en laat zich niet blokkeren door lokale wijzigingen in de cache. + +**`--doctor` vraagt Server Up nu zelf naar zijn paden.** Wat er op de host staat +is één ding; waar de app kijkt is een ander, en die twee kunnen uit elkaar lopen +— een `LIBRARY_DIR` uit een oude configuratie wint van de standaard. Dan zie je +een lege stacklijst zonder dat ergens staat waarom. Hij toont nu welke +`LIBRARY_DIR` en `DATA_DIR` de app gebruikt, hoeveel stacks hij daar ziet, en +hoeveel apps er in de store-cache zitten. + # v0.7.96-beta — Backups eindelijk écht getest, en terugrollen **Backups waren nooit tegen een echte Docker-daemon gedraaid.** Alle diff --git a/VERSION b/VERSION index 292edbb..ea12cce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.96-beta +0.7.97-beta diff --git a/install.sh b/install.sh index f95eea0..36d6fbb 100755 --- a/install.sh +++ b/install.sh @@ -1147,6 +1147,55 @@ doorlichten() { [ -n "$_vrij" ] && log "Vrije ruimte: $_vrij" fi + # Wat de mappen op de host zijn is één ding; waar Server Up zélf kijkt is + # een ander. Die twee kunnen uit elkaar lopen — een LIBRARY_DIR uit een oude + # config wint van de standaard — en dan zie je een lege stacklijst zonder + # dat ergens staat waarom. + stap "Wat Server Up zelf ziet" + _eigen="$($SUDO docker exec -i server-up python - 2>/dev/null <<'PYEOF' || true +import json, sys +sys.path.insert(0, "/app") +try: + import core + c = core.load() + print(json.dumps({k: c.get(k, "") for k in + ("BASE_DIR", "LIBRARY_DIR", "DATA_DIR", "BACKUP_DIR")})) +except Exception as e: + print(json.dumps({"fout": str(e)[:200]})) +PYEOF +)" + if [ -z "$_eigen" ]; then + dok_waar "Kon Server Up niet vragen welke paden hij gebruikt" + else + _lib="$(printf '%s' "$_eigen" | sed -n 's/.*"LIBRARY_DIR": *"\([^"]*\)".*/\1/p')" + _dat="$(printf '%s' "$_eigen" | sed -n 's/.*"DATA_DIR": *"\([^"]*\)".*/\1/p')" + log "Stacks: $_lib" + log "Appdata: $_dat" + if [ -n "$_lib" ]; then + _n="$($SUDO docker exec server-up sh -c \ + "ls -1 '$_lib' 2>/dev/null | wc -l" 2>/dev/null || echo 0)" + _n="$(printf '%s' "$_n" | tr -d '[:space:]')" + if [ "${_n:-0}" -gt 0 ] 2>/dev/null; then + dok_ok "$_n stack(s) zichtbaar voor Server Up" + else + dok_fout "Server Up ziet geen enkele stack in $_lib" + [ "$_lib" != "$_basis/stacks" ] \ + && log " Je stacks staan waarschijnlijk in $_basis/stacks." \ + && log " Pas LIBRARY_DIR aan bij Instellingen → Paden." + fi + fi + # De app store leest uit de git-cache; is die leeg, dan is de lijst leeg. + _apps="$($SUDO docker exec server-up sh -c \ + 'find /data/git -name template.json 2>/dev/null | wc -l' \ + 2>/dev/null || echo 0)" + _apps="$(printf '%s' "$_apps" | tr -d '[:space:]')" + if [ "${_apps:-0}" -gt 0 ] 2>/dev/null; then + dok_ok "$_apps app(s) in de store-cache" + else + dok_fout "De store-cache is leeg; synchroniseer de repo's opnieuw" + fi + fi + stap "Bijwerken vanuit de interface" _werkmap="$(_dok_inspect '{{index .Config.Labels "com.docker.compose.project.working_dir"}}')" if [ -z "$_werkmap" ]; then diff --git a/server-up/core/git.py b/server-up/core/git.py index 0dd88c2..5a64241 100644 --- a/server-up/core/git.py +++ b/server-up/core/git.py @@ -62,10 +62,28 @@ def clone_or_pull(repo: dict, log_fn=None) -> tuple[bool, str]: try: if dest.exists() and (dest / ".git").exists(): if log_fn: - log_fn(f"Pull {rid}…") - r = subprocess.run(["git", "pull", "--rebase", "--autostash"], + log_fn(f"Bijwerken {rid} ({branch})…") + # Geen `git pull --rebase`. Dat commando leunt op de + # upstream-instellingen van de lokale kloon, en die kunnen + # ontsporen: staan er meerdere merge-doelen in `branch..merge`, + # dan stopt git met "Cannot rebase onto multiple branches" en werkt + # er niets meer bij. + # + # Belangrijker nog: `git pull` zonder argumenten haalt de tak op + # waarop ooit gekloond is. De branch uit de instellingen werd dus + # genegeerd — wijzigde je die van main naar dev, dan bleef je + # stilzwijgend main krijgen. + # + # Dit is een cache die Server Up zelf beheert; er staat niets in dat + # bewaard hoeft te blijven. Ophalen wat is ingesteld en daar hard op + # gaan staan is daarom zowel eenvoudiger als juister. + r = subprocess.run(["git", "fetch", "--depth", "1", "origin", branch], cwd=str(dest), capture_output=True, text=True, env=env, timeout=60) + if r.returncode == 0: + r = subprocess.run(["git", "reset", "--hard", "FETCH_HEAD"], + cwd=str(dest), capture_output=True, text=True, + env=env, timeout=60) else: if log_fn: log_fn(f"Clone {url}…") diff --git a/tests/test_repo_urls.py b/tests/test_repo_urls.py index a921757..5a3c928 100644 --- a/tests/test_repo_urls.py +++ b/tests/test_repo_urls.py @@ -80,3 +80,96 @@ def test_settings_put_kan_auth_niet_overschrijven(client, env): import core.auth as auth assert auth.settings()["mode"] == "local" assert "tester" in auth.settings()["users"] + + +# ── Bijwerken van de cache ─────────────────────────────────────────────────── +# `git pull --rebase` leunt op de upstream-instellingen van de lokale kloon. +# Die kunnen ontsporen, en de branch uit de instellingen werd er sowieso niet +# door gevolgd. + +def _repo_met_twee_takken(tmp_path): + import subprocess as sp + bron = tmp_path / "bron" + bron.mkdir() + + def g(*a): + return sp.run(["git", *a], cwd=str(bron), capture_output=True, + text=True, check=True) + + g("init", "-qb", "main", ".") + g("config", "user.email", "t@t") + g("config", "user.name", "t") + (bron / "app.txt").write_text("van main") + g("add", "app.txt") + g("commit", "-qm", "main") + g("checkout", "-qb", "dev") + (bron / "app.txt").write_text("van dev") + g("commit", "-qam", "dev") + g("checkout", "-q", "main") + return bron + + +def test_bijwerken_volgt_de_ingestelde_branch(env, tmp_path, monkeypatch): + """`git pull` haalde altijd de tak op waarop ooit gekloond was. Zette je de + branch in de instellingen om, dan gebeurde er stilzwijgend niets.""" + import shutil as _shutil + from core import git + if not _shutil.which("git"): + pytest.skip("git niet aanwezig") + bron = _repo_met_twee_takken(tmp_path) + # file:// is normaal niet toegestaan; hier gaat het om het bijwerken zelf. + monkeypatch.setattr("core.valid_repo_url", lambda u: True) + repo = {"id": "proef", "url": f"file://{bron}", "branch": "main"} + + ok, msg = git.clone_or_pull(repo) + assert ok, msg + cache = git.cache_dir("proef") + assert (cache / "app.txt").read_text() == "van main" + + repo["branch"] = "dev" + ok, msg = git.clone_or_pull(repo) + assert ok, msg + assert (cache / "app.txt").read_text() == "van dev", \ + "de gewijzigde branch werd genegeerd" + + +def test_bijwerken_overleeft_een_ontspoorde_upstream(env, tmp_path, monkeypatch): + """Twee waarden in `branch..merge` lieten git stoppen met "Cannot + rebase onto multiple branches" — en daarmee werkte er niets meer bij.""" + import shutil as _shutil + import subprocess as sp + from core import git + if not _shutil.which("git"): + pytest.skip("git niet aanwezig") + bron = _repo_met_twee_takken(tmp_path) + monkeypatch.setattr("core.valid_repo_url", lambda u: True) + repo = {"id": "proef", "url": f"file://{bron}", "branch": "main"} + assert git.clone_or_pull(repo)[0] + + cache = git.cache_dir("proef") + sp.run(["git", "config", "remote.origin.fetch", + "+refs/heads/*:refs/remotes/origin/*"], cwd=str(cache), check=True) + sp.run(["git", "config", "--add", "branch.main.merge", "refs/heads/dev"], + cwd=str(cache), check=True) + + ok, msg = git.clone_or_pull(repo) + assert ok, f"bijwerken struikelt over de upstream-instellingen: {msg}" + assert "multiple branches" not in msg + + +def test_lokale_rommel_in_de_cache_blokkeert_niets(env, tmp_path, monkeypatch): + """De cache is van Server Up; wat er lokaal in gewijzigd is mag het + bijwerken niet tegenhouden.""" + import shutil as _shutil + from core import git + if not _shutil.which("git"): + pytest.skip("git niet aanwezig") + bron = _repo_met_twee_takken(tmp_path) + monkeypatch.setattr("core.valid_repo_url", lambda u: True) + repo = {"id": "proef", "url": f"file://{bron}", "branch": "main"} + assert git.clone_or_pull(repo)[0] + + (git.cache_dir("proef") / "app.txt").write_text("met de hand gewijzigd") + ok, msg = git.clone_or_pull(repo) + assert ok, msg + assert (git.cache_dir("proef") / "app.txt").read_text() == "van main"