fix(windows): der Deinstallierer beendet jetzt auch den laufenden Dienst
Ampel / ampel (push) Failing after 54s
Ampel / ampel (push) Failing after 54s
Beim Deinstallieren im laufenden Betrieb blieben liegen:
rippy.db, rippy.db-shm, rippy.db-wal
(Der Prozess kann nicht auf die Datei zugreifen, da sie von einem
anderen Prozess verwendet wird)
Der Dienst lief weiter und hielt die Datenbank offen. Damit waere der Fehler
von vorhin durch eine andere Tuer zurueckgekommen: Die naechste Installation
haette wieder den alten Zustand samt setup.done geerbt, und der
Ersteinrichtungs-Assistent waere wieder verschwunden.
Aufgefallen ist es nur, weil der Deinstallierer seit heute NENNT, was er
nicht wegbekommen hat. Vorher stand dort ein `except OSError: pass` und die
Meldung "Rippy wurde entfernt".
Gefiltert wird ueber den Installationsordner, nicht ueber den Namen:
`Rippy.exe` heisst auch die Datei, die den Auftrag gerade ausfuehrt. Die
eigene Prozesskennung und die des PyInstaller-Starters sind ausgenommen.
Nachgewiesen am laufenden Prozess: beendet, Datenbankdateien danach
loeschbar. In der Entwicklungsumgebung schlaegt der Pfadvergleich uebrigens
immer fehl -- die Werkzeuge laufen in einem App-Container, der
%LOCALAPPDATA% umleitet, waehrend die Registry den echten Pfad meldet. Das
steht jetzt als Hinweis im Docstring, damit es niemand ein zweites Mal sucht.
Ampel lokal: 713 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8e65411189
commit
3d5f82b786
@@ -233,6 +233,38 @@ def test_die_testsuite_ruehrt_den_ECHTEN_eintrag_nicht_an(tmp_path, monkeypatch)
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Den laufenden Dienst zuerst beenden ─────────────────────────────────
|
||||||
|
def test_beenden_filtert_ueber_den_ordner():
|
||||||
|
"""DER Befund vom 28.08.2026: Beim Deinstallieren im laufenden Betrieb
|
||||||
|
blieben rippy.db, rippy.db-shm und rippy.db-wal liegen — „von einem
|
||||||
|
anderen Prozess verwendet". Der Dienst lief weiter."""
|
||||||
|
s = windows_app.beenden_skript("C:" + BACKSLASH + "Rippy", [123, 456])
|
||||||
|
assert "Rippy.exe" in s
|
||||||
|
assert "C:" + BACKSLASH + "Rippy" + BACKSLASH + "*" in s
|
||||||
|
assert "Stop-Process" in s
|
||||||
|
|
||||||
|
|
||||||
|
def test_der_eigene_prozess_wird_ausgenommen():
|
||||||
|
"""`Rippy.exe` heisst auch die Datei, die den Auftrag gerade ausfuehrt —
|
||||||
|
die darf sich nicht selbst abschiessen."""
|
||||||
|
s = windows_app.beenden_skript("C:" + BACKSLASH + "R", [4711, 4712])
|
||||||
|
assert "4711" in s and "4712" in s
|
||||||
|
assert s.index("notin") < s.index("Stop-Process"), "erst filtern, dann beenden"
|
||||||
|
|
||||||
|
|
||||||
|
def test_ohne_eigene_pids_wird_nichts_wildes_erzeugt():
|
||||||
|
"""Eine leere Liste duerfte keine kaputte Bedingung ergeben."""
|
||||||
|
s = windows_app.beenden_skript("C:" + BACKSLASH + "R", [])
|
||||||
|
assert "@()" not in s, "leere Menge waere eine unbrauchbare Bedingung"
|
||||||
|
assert "@(0)" in s
|
||||||
|
|
||||||
|
|
||||||
|
def test_apostroph_im_ordner_bricht_das_skript_nicht():
|
||||||
|
s = windows_app.beenden_skript("C:" + BACKSLASH + "O'Brien", [1])
|
||||||
|
assert "O''Brien" in s
|
||||||
|
assert s.count("'") % 2 == 0
|
||||||
|
|
||||||
|
|
||||||
# ── Die Datenbank am richtigen Ort ──────────────────────────────────────
|
# ── Die Datenbank am richtigen Ort ──────────────────────────────────────
|
||||||
def test_datenbank_liegt_im_installationsordner():
|
def test_datenbank_liegt_im_installationsordner():
|
||||||
"""DER Befund vom 28.08.2026: „Es fehlt der 1st run wizzard wenn man es
|
"""DER Befund vom 28.08.2026: „Es fehlt der 1st run wizzard wenn man es
|
||||||
|
|||||||
@@ -184,6 +184,79 @@ def installieren(ziel: str = None, autostart: bool = True,
|
|||||||
return ziel
|
return ziel
|
||||||
|
|
||||||
|
|
||||||
|
def beenden_skript(ordner: str, eigene_pids: list) -> str:
|
||||||
|
"""Das PowerShell-Skript, das die ANDEREN Rippy-Prozesse beendet.
|
||||||
|
|
||||||
|
Reine Funktion, damit der Filter prüfbar ist, ohne etwas zu beenden.
|
||||||
|
|
||||||
|
## Warum das nötig ist (gemessen 28.08.2026)
|
||||||
|
|
||||||
|
Beim Deinstallieren im laufenden Betrieb blieben liegen:
|
||||||
|
|
||||||
|
rippy.db, rippy.db-shm, rippy.db-wal
|
||||||
|
(von einem anderen Prozess verwendet)
|
||||||
|
|
||||||
|
Der Dienst lief weiter und hielt die Datenbank offen. Damit wäre der
|
||||||
|
Fehler von vorhin durch eine andere Tür zurückgekommen: Die nächste
|
||||||
|
Installation hätte wieder den alten Zustand geerbt.
|
||||||
|
|
||||||
|
## Warum über den ORDNER gefiltert wird
|
||||||
|
|
||||||
|
`Rippy.exe` heißt auch die Setup-Datei, die den Auftrag gerade ausführt —
|
||||||
|
und die darf sich nicht selbst abschießen. Beendet wird nur, was aus dem
|
||||||
|
Installationsordner läuft, und die eigenen Prozesskennungen sind
|
||||||
|
ausgenommen.
|
||||||
|
|
||||||
|
## Hinweis für spätere Sitzungen
|
||||||
|
|
||||||
|
In der Entwicklungsumgebung schlägt der Vergleich zuverlässig fehl, und
|
||||||
|
das ist KEIN Fehler im Code: Die Werkzeuge laufen dort in einem
|
||||||
|
App-Container, der Schreibzugriffe auf `%LOCALAPPDATA%` nach
|
||||||
|
|
||||||
|
…\AppData\Local\Packages\<app>\LocalCache\Local\Rippy
|
||||||
|
|
||||||
|
umleitet. Der Registry-Eintrag `InstallLocation` zeigt dagegen auf den
|
||||||
|
ECHTEN Pfad — Prozess und Ordner passen dann nie zusammen. Auf einer
|
||||||
|
normalen Installation liegen beide am selben Ort. Die Mechanik wurde am
|
||||||
|
28.08.2026 mit dem tatsächlichen Ordner nachgewiesen: Prozess beendet,
|
||||||
|
Datenbankdateien danach löschbar.
|
||||||
|
"""
|
||||||
|
ort = (ordner or "").replace("'", "''").rstrip("\\/")
|
||||||
|
pids = ",".join(str(int(p)) for p in eigene_pids if p)
|
||||||
|
return (
|
||||||
|
"Get-CimInstance Win32_Process -Filter \"Name='Rippy.exe'\" "
|
||||||
|
"| Where-Object { $_.ExecutablePath -like '%s\\*' "
|
||||||
|
"-and $_.ProcessId -notin @(%s) } "
|
||||||
|
"| ForEach-Object { Stop-Process -Id $_.ProcessId -Force "
|
||||||
|
"-ErrorAction SilentlyContinue }" % (ort, pids or "0")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def dienst_beenden(ordner: str) -> bool:
|
||||||
|
"""Beendet die anderen Rippy-Prozesse aus diesem Ordner. Wirft nicht."""
|
||||||
|
if os.name != "nt" or not ordner:
|
||||||
|
return False
|
||||||
|
from rippy.platform.winlauf import OHNE_FENSTER
|
||||||
|
|
||||||
|
eigene = [os.getpid()]
|
||||||
|
try:
|
||||||
|
eigene.append(os.getppid()) # der PyInstaller-Starter
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
ergebnis = subprocess.run(
|
||||||
|
["powershell", "-NoProfile", "-NonInteractive", "-Command",
|
||||||
|
beenden_skript(ordner, eigene)],
|
||||||
|
capture_output=True, text=True, timeout=60,
|
||||||
|
creationflags=OHNE_FENSTER)
|
||||||
|
except (OSError, subprocess.SubprocessError):
|
||||||
|
return False
|
||||||
|
# Kurz Luft lassen: Windows gibt die Dateihandles nicht im selben
|
||||||
|
# Augenblick frei, in dem der Prozess endet.
|
||||||
|
time.sleep(2)
|
||||||
|
return ergebnis.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def alte_orte() -> list:
|
def alte_orte() -> list:
|
||||||
"""Ordner ausserhalb des Installationsordners, die Rippy einmal angelegt hat.
|
"""Ordner ausserhalb des Installationsordners, die Rippy einmal angelegt hat.
|
||||||
|
|
||||||
@@ -332,6 +405,13 @@ def deinstallieren(still: bool = False) -> None:
|
|||||||
|
|
||||||
fenster_modul.helfer_beenden(os.path.join(ordner, "fenster"))
|
fenster_modul.helfer_beenden(os.path.join(ordner, "fenster"))
|
||||||
|
|
||||||
|
# ⚠️ Und den DIENST selbst (Befund 28.08.2026). Lief er weiter, blieben
|
||||||
|
# rippy.db, rippy.db-shm und rippy.db-wal liegen — „von einem anderen
|
||||||
|
# Prozess verwendet". Die naechste Installation haette damit wieder den
|
||||||
|
# alten Zustand geerbt, also genau den Fehler von vorhin durch eine
|
||||||
|
# andere Tuer.
|
||||||
|
dienst_beenden(ordner)
|
||||||
|
|
||||||
# Der ALTE Datenbank-Ort (bis 28.08.2026 %ProgramData%\Rippy) liegt
|
# Der ALTE Datenbank-Ort (bis 28.08.2026 %ProgramData%\Rippy) liegt
|
||||||
# ausserhalb des Installationsordners. Blieb er stehen, erbte die naechste
|
# ausserhalb des Installationsordners. Blieb er stehen, erbte die naechste
|
||||||
# Installation den alten Zustand — genau daran fehlte dem Commander der
|
# Installation den alten Zustand — genau daran fehlte dem Commander der
|
||||||
|
|||||||
Reference in New Issue
Block a user