fix(windows): leere Symbole und der verschwundene Eintrag in "Installierte Apps"
Ampel / ampel (push) Successful in 54s
Ampel / ampel (push) Successful in 54s
Beides vom Commander gemeldet, beides gemessen statt vermutet.
## 1. Weisses leeres Blatt statt Symbol
In deploy/worker-windows/rippy.ico steckte GENAU EIN Bild:
Bilder = 1
256x256 32 bit 5657 Bytes PNG
Windows holt sich fuer jede Stelle die passende Groesse -- 16x16 fuer die
Taskleiste, 32/48 fuer Desktop und Startmenue. Fehlt sie, skaliert die Shell
nicht zuverlaessig selbst, sondern zeigt das leere Blatt. Bei einer Datei mit
nur einem PNG-komprimierten 256er passiert das regelmaessig.
packaging/windows/icon.py baut die Datei jetzt mit neun Groessen
(16/20/24/32/40/48/64/128/256, LANCZOS beim Verkleinern). Nachgemessen an
der neuen EXE: 16x16 mit 138 Farben, 32x32 mit 264 -- kein leeres Blatt mehr.
build.py laesst ein unvollstaendiges Symbol gar nicht mehr durch, und
test_icon.py haelt die Pflichtgroessen fest (mit dem alten Stand rot gesehen).
## 2. Rippy stand nicht in "Installierte Apps"
Die Ursache war MEINE Testsuite. Gemessen:
Rippy.exe --nicht-starten -> Eintrag: DA (Rippy 2.0.0)
pytest -q -> Eintrag: WEG
test_installation_legt_die_dateien_an_... schrieb in den ECHTEN
Uninstall-Schluessel und loeschte ihn im finally wieder -- also auch den des
Commanders. Der Autostart-Eintrag ging denselben Weg.
Der Schluesselname stand als VORGABEWERT in jeder Signatur, und Vorgabewerte
wertet Python zur Definitionszeit aus: Ein Test konnte ihn gar nicht umbiegen.
Jetzt steht dort None, aufgeloest zur Laufzeit -- damit wirkt ein
monkeypatch.setattr(reg, "SCHLUESSEL", ...) ueberall.
Dazu ein Waechter (test_die_testsuite_ruehrt_den_ECHTEN_eintrag_nicht_an):
Er merkt sich den echten Zustand vorher, laesst eine vollstaendige
Test-Installation samt Autostart laufen und prueft danach, dass sich am
echten Eintrag nichts geaendert hat.
Das ist derselbe Fehler wie vorhin bei den Verknuepfungen, nur an anderer
Stelle. Die Regel gehoert in den Code, nicht in meinen Kopf: **Ein Test
raeumt nur weg, was er selbst angelegt hat.**
Ampel lokal: 590 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e1984dc543
commit
60c0f93237
@@ -47,6 +47,19 @@ import sys
|
||||
# Der Schlüsselname unter Uninstall. Muss über Versionen hinweg GLEICH bleiben —
|
||||
# ändert er sich, entsteht bei einem Update ein zweiter Eintrag, und der alte
|
||||
# bleibt als Leiche stehen.
|
||||
# ## Warum der Name ueberschreibbar sein MUSS (28.08.2026)
|
||||
#
|
||||
# Er stand als Vorgabewert direkt in jeder Signatur. Vorgabewerte wertet
|
||||
# Python zur DEFINITIONSZEIT aus — ein Test konnte den Namen also gar nicht
|
||||
# umbiegen und arbeitete zwangslaeufig am ECHTEN System.
|
||||
#
|
||||
# Gemessen, was das anrichtet: Nach `Rippy.exe` (installiert) stand der
|
||||
# Eintrag da; nach EINEM `pytest -q` war er weg. Der Commander sah Rippy
|
||||
# danach nicht mehr unter „Installierte Apps" — sein Autostart-Eintrag ging
|
||||
# denselben Weg. Der Test raeumte auf, was er nie angelegt hatte.
|
||||
#
|
||||
# Deshalb: `None` in der Signatur, aufgeloest zur LAUFZEIT. Damit wirkt ein
|
||||
# `monkeypatch.setattr(reg, "SCHLUESSEL", ...)` ueberall.
|
||||
SCHLUESSEL = "Rippy"
|
||||
|
||||
UNINSTALL_PFAD = r"Software\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||
@@ -56,8 +69,8 @@ ANZEIGENAME = "Rippy"
|
||||
HERAUSGEBER = "Rippy"
|
||||
|
||||
|
||||
def uninstall_pfad(schluessel: str = SCHLUESSEL) -> str:
|
||||
return UNINSTALL_PFAD + "\\" + schluessel
|
||||
def uninstall_pfad(schluessel: str = None) -> str:
|
||||
return UNINSTALL_PFAD + "\\" + (schluessel or SCHLUESSEL)
|
||||
|
||||
|
||||
def _quoten(pfad: str) -> str:
|
||||
@@ -118,7 +131,7 @@ def _winreg():
|
||||
|
||||
|
||||
def eintragen(installationsordner: str, deinstaller: str, version: str,
|
||||
icon: str = "", schluessel: str = SCHLUESSEL) -> None:
|
||||
icon: str = "", schluessel: str = None) -> None:
|
||||
"""Schreibt den Eintrag für „Programme und Features"."""
|
||||
winreg = _winreg()
|
||||
werte = eintrag_werte(
|
||||
@@ -133,7 +146,7 @@ def eintragen(installationsordner: str, deinstaller: str, version: str,
|
||||
winreg.SetValueEx(k, name, 0, winreg.REG_SZ, str(wert))
|
||||
|
||||
|
||||
def austragen(schluessel: str = SCHLUESSEL) -> bool:
|
||||
def austragen(schluessel: str = None) -> bool:
|
||||
"""Entfernt den Eintrag. False, wenn es ihn gar nicht gab."""
|
||||
winreg = _winreg()
|
||||
try:
|
||||
@@ -143,7 +156,7 @@ def austragen(schluessel: str = SCHLUESSEL) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def ist_eingetragen(schluessel: str = SCHLUESSEL) -> bool:
|
||||
def ist_eingetragen(schluessel: str = None) -> bool:
|
||||
winreg = _winreg()
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, uninstall_pfad(schluessel)):
|
||||
@@ -152,7 +165,7 @@ def ist_eingetragen(schluessel: str = SCHLUESSEL) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def gelesene_werte(schluessel: str = SCHLUESSEL) -> dict:
|
||||
def gelesene_werte(schluessel: str = None) -> dict:
|
||||
"""Was WIRKLICH in der Registry steht — für `rippy doctor` und Tests.
|
||||
|
||||
Nachsehen statt glauben: Ein Schreibvorgang, der quittiert wurde, ist
|
||||
@@ -176,28 +189,28 @@ def gelesene_werte(schluessel: str = SCHLUESSEL) -> dict:
|
||||
|
||||
|
||||
# ── Autostart ───────────────────────────────────────────────────────────
|
||||
def autostart_setzen(befehl: str, name: str = SCHLUESSEL) -> None:
|
||||
def autostart_setzen(befehl: str, name: str = None) -> None:
|
||||
winreg = _winreg()
|
||||
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, RUN_PFAD) as k:
|
||||
winreg.SetValueEx(k, name, 0, winreg.REG_SZ, befehl)
|
||||
winreg.SetValueEx(k, name or SCHLUESSEL, 0, winreg.REG_SZ, befehl)
|
||||
|
||||
|
||||
def autostart_entfernen(name: str = SCHLUESSEL) -> bool:
|
||||
def autostart_entfernen(name: str = None) -> bool:
|
||||
winreg = _winreg()
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_PFAD, 0,
|
||||
winreg.KEY_ALL_ACCESS) as k:
|
||||
winreg.DeleteValue(k, name)
|
||||
winreg.DeleteValue(k, name or SCHLUESSEL)
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
|
||||
def autostart_wert(name: str = SCHLUESSEL) -> str:
|
||||
def autostart_wert(name: str = None) -> str:
|
||||
winreg = _winreg()
|
||||
try:
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_PFAD) as k:
|
||||
return winreg.QueryValueEx(k, name)[0]
|
||||
return winreg.QueryValueEx(k, name or SCHLUESSEL)[0]
|
||||
except (FileNotFoundError, OSError):
|
||||
return ""
|
||||
|
||||
|
||||
@@ -61,8 +61,23 @@ def test_ordner_ohne_abschliessenden_trenner():
|
||||
# ── Installation (echt, in einem Testordner) ────────────────────────────
|
||||
@nur_windows
|
||||
def test_installation_legt_die_dateien_an_und_meldet_sich_bei_windows(tmp_path, monkeypatch):
|
||||
"""Der ganze Kreislauf. Der Registry-Schluessel ist der ECHTE — deshalb
|
||||
wird am Ende immer aufgeraeumt, auch wenn der Test scheitert.
|
||||
"""Der ganze Kreislauf — vollstaendig in einem eigenen Sandkasten.
|
||||
|
||||
## ⚠️ WARUM HIER EIN EIGENER REGISTRY-SCHLUESSEL STEHT (28.08.2026)
|
||||
|
||||
Weil hier vorher der ECHTE stand. Der Test schrieb hinein und loeschte ihn
|
||||
im `finally` wieder — auch den des Commanders. Gemessen:
|
||||
|
||||
Rippy.exe --nicht-starten -> Eintrag: DA (Rippy 2.0.0)
|
||||
pytest -q -> Eintrag: WEG
|
||||
|
||||
Rippy verschwand danach aus „Installierte Apps", und der
|
||||
Autostart-Eintrag ging denselben Weg. Der Commander hat es gemeldet,
|
||||
bevor ich es bemerkt habe.
|
||||
|
||||
Das ist derselbe Fehler wie unten bei den Verknuepfungen, nur an einer
|
||||
anderen Stelle: **Ein Test raeumt nur weg, was er selbst angelegt hat.**
|
||||
Wenn „aufraeumen" fremden Zustand trifft, ist es kein Aufraeumen.
|
||||
|
||||
## ⚠️ WARUM HIER KEINE VERKNUEPFUNGEN ANGELEGT WERDEN (28.08.2026)
|
||||
|
||||
@@ -90,6 +105,14 @@ def test_installation_legt_die_dateien_an_und_meldet_sich_bei_windows(tmp_path,
|
||||
from rippy.platform import verknuepfungen as vk
|
||||
from rippy.platform import win_registry as reg
|
||||
|
||||
# Sicherung 3 (28.08.2026): ein EIGENER Registry-Schluessel.
|
||||
# Vorher schrieb dieser Test in den ECHTEN Eintrag und loeschte ihn im
|
||||
# finally wieder -- also auch den des Commanders. Gemessen: nach
|
||||
# `Rippy.exe` stand der Eintrag da, nach EINEM `pytest -q` war er weg,
|
||||
# und Rippy verschwand aus "Installierte Apps". Der Autostart-Eintrag
|
||||
# ging denselben Weg.
|
||||
monkeypatch.setattr(reg, "SCHLUESSEL", "RippyTestInstallation")
|
||||
|
||||
# Sicherung 2: Selbst ein versehentliches `verknuepfen=True` bliebe folgenlos.
|
||||
angelegt = []
|
||||
monkeypatch.setattr(
|
||||
@@ -137,6 +160,8 @@ def test_installation_ruehrt_die_echten_verknuepfungen_nicht_an(tmp_path, monkey
|
||||
from rippy.platform import verknuepfungen as vk
|
||||
from rippy.platform import win_registry as reg
|
||||
|
||||
monkeypatch.setattr(reg, "SCHLUESSEL", "RippyTestWaechter")
|
||||
|
||||
aufrufe = []
|
||||
monkeypatch.setattr(vk, "anlegen",
|
||||
lambda *a, **k: aufrufe.append(a) or True)
|
||||
@@ -156,6 +181,49 @@ def test_installation_ruehrt_die_echten_verknuepfungen_nicht_an(tmp_path, monkey
|
||||
assert aufrufe == []
|
||||
|
||||
|
||||
@nur_windows
|
||||
def test_die_testsuite_ruehrt_den_ECHTEN_eintrag_nicht_an(tmp_path, monkeypatch):
|
||||
"""Der Waechter gegen den Vorfall vom 28.08.2026.
|
||||
|
||||
Er sieht VORHER nach, ob Rippy auf dieser Maschine installiert ist,
|
||||
laesst dann eine vollstaendige Test-Installation samt Autostart laufen —
|
||||
und prueft danach, dass sich am echten Eintrag NICHTS geaendert hat.
|
||||
|
||||
Ohne diesen Waechter reicht ein vergessenes `monkeypatch` in einem
|
||||
kuenftigen Test, und Rippy verschwindet beim naechsten `pytest -q`
|
||||
wieder aus „Installierte Apps". Das ist schon einmal passiert, und der
|
||||
Commander musste es melden.
|
||||
"""
|
||||
from rippy.platform import verknuepfungen as vk
|
||||
from rippy.platform import win_registry as reg
|
||||
|
||||
echt_vorher = reg.ist_eingetragen("Rippy")
|
||||
autostart_vorher = reg.autostart_wert("Rippy")
|
||||
|
||||
monkeypatch.setattr(reg, "SCHLUESSEL", "RippyTestUnberuehrt")
|
||||
monkeypatch.setattr(vk, "alle_anlegen", lambda *a, **k: {})
|
||||
|
||||
attrappe = tmp_path / "q" / "RippySetup.exe"
|
||||
attrappe.parent.mkdir()
|
||||
attrappe.write_bytes(b"x" * 16)
|
||||
monkeypatch.setattr(windows_app, "_eigene_datei", lambda: str(attrappe))
|
||||
|
||||
try:
|
||||
windows_app.installieren(ziel=str(tmp_path / "i"), autostart=True,
|
||||
starten_danach=False, verknuepfen=False)
|
||||
finally:
|
||||
reg.autostart_entfernen()
|
||||
reg.austragen()
|
||||
|
||||
assert reg.ist_eingetragen("Rippy") == echt_vorher, (
|
||||
"Die Testsuite hat den ECHTEN Uninstall-Eintrag veraendert — genau "
|
||||
"dadurch verschwand Rippy aus 'Installierte Apps'."
|
||||
)
|
||||
assert reg.autostart_wert("Rippy") == autostart_vorher, (
|
||||
"Die Testsuite hat den ECHTEN Autostart-Eintrag veraendert."
|
||||
)
|
||||
|
||||
|
||||
# ── Rueckmeldung ohne Konsole ───────────────────────────────────────────
|
||||
def test_ohne_konsole_kommt_ein_fenster(monkeypatch):
|
||||
"""Seit die EXE ohne Konsole gebaut wird (Wunsch des Commanders am
|
||||
|
||||
Reference in New Issue
Block a user