Windows uninstaller: remove the app's own folders (control, host, node, harness) recursively; files the app wrote there while it ran (Python bytecode caches under control) are not in the installer's file list, so those folders and the program folder stayed behind after use (found on strix with a standard user after the process fix); test

This commit is contained in:
Niko1221 committed 2026-10-25 13:56:36 +01:00
1 parent af197f96db
commit 4b9e05a2a8
2 files changed
+37 -1

No files matched your search

+14
View File
@@ -718,6 +718,18 @@ SectionEnd
!macroend
; STRATA-STOP-END
; STRATA-CLEAN-BEGIN
; The app writes into its own folders while it runs (Python bytecode caches under control\, for one), and the file-by-file delete below does not know them,
; so those folders stayed behind with a few dozen files. These four folders hold only what the installer put there and what the app made there;
; user data is never in them (it lives in the data folder), and the install folder itself is still removed with a plain RMDir.
!macro StrataRemoveAppFolders INSTALLDIR
RMDir /r "${INSTALLDIR}\control"
RMDir /r "${INSTALLDIR}\host"
RMDir /r "${INSTALLDIR}\node"
RMDir /r "${INSTALLDIR}\harness"
!macroend
; STRATA-CLEAN-END
Section Install
SetOutPath $INSTDIR
@@ -897,6 +909,8 @@ Section Uninstall
{{/each}}
!insertmacro StrataRemoveAppFolders "$INSTDIR"
; Delete uninstaller
Delete "$INSTDIR\uninstall.exe"
+23 -1
View File
@@ -56,7 +56,7 @@ class TemplateTests(unittest.TestCase):
class MigrationTests(unittest.TestCase):
def run_nsis(self, body: str, work: Path):
template = TEMPLATE.read_text(encoding="utf-8")
block = chr(10).join(re.search(rf"; STRATA-{name}-BEGIN.*?; STRATA-{name}-END", template, re.S).group(0) for name in ("MIGRATE", "STOP"))
block = chr(10).join(re.search(rf"; STRATA-{name}-BEGIN.*?; STRATA-{name}-END", template, re.S).group(0) for name in ("MIGRATE", "STOP", "CLEAN"))
script = work / "t.nsi"
script.write_text('Unicode true\nOutFile "t.exe"\nRequestExecutionLevel user\nSilentInstall silent\n' + block + "\nSection\n" + body + "\nSectionEnd\n", encoding="utf-8")
done = subprocess.run([str(MAKENSIS), "/V1", str(script)], cwd=work, capture_output=True, text=True)
@@ -136,6 +136,28 @@ class MigrationTests(unittest.TestCase):
proc.wait()
def test_files_the_app_made_in_its_own_folders_are_removed_and_the_data_beside_them_is_not(self):
"""Strix, standard user: after the app had run, Python bytecode caches under control\\ kept the program folder alive."""
with tempfile.TemporaryDirectory() as tmp:
base = Path(tmp)
inst = base / "Programs" / "Strata"
touch(inst, ["strata-desktop.exe", "control/python/python.exe", "control/python/Lib/__pycache__/argparse.cpython-312.pyc",
"control/serve/__pycache__/server.cpython-312.pyc", "host/server/index.mjs", "host/logs/made-at-run.txt", "node/node.exe",
"harness/BUNDLE.json", "components/engine/keep.txt", "runtime/keep.json"])
self.run_nsis(f'!insertmacro StrataRemoveAppFolders "{inst}"', base)
for folder in ("control", "host", "node", "harness"):
self.assertFalse((inst / folder).exists(), folder)
for name in ("components/engine/keep.txt", "runtime/keep.json", "strata-desktop.exe"):
self.assertTrue((inst / name).is_file(), f"kept: {name}")
def test_the_uninstaller_clears_those_folders_before_the_last_rmdir(self):
text = TEMPLATE.read_text(encoding="utf-8")
uninstall = text[text.index("Section Uninstall"):]
self.assertIn('!insertmacro StrataRemoveAppFolders "$INSTDIR"', uninstall)
self.assertLess(uninstall.index("StrataRemoveAppFolders"), uninstall.index('RMDir "$INSTDIR"'))
self.assertNotRegex(uninstall, r'RMDir /r "\$INSTDIR"')
class StopOrderTests(unittest.TestCase):
def test_the_uninstaller_stops_them_before_it_removes_files(self):
text = TEMPLATE.read_text(encoding="utf-8")