illuscape/uninstall.py
pyr0ball 8e245dfe61 feat: cross-platform installer (Windows port)
- install.py / uninstall.py: full Python installer for Linux, macOS, Windows
  - detect_platform.py: shared Inkscape config path detection
  - Windows config path: %APPDATA%\inkscape (native/winget/scoop) or
    %LOCALAPPDATA%\Packages\org.inkscape.Inkscape* (MS Store)
  - Windows desktop: Start Menu .lnk via PowerShell WScript.Shell COM object
  - macOS: XDG / ~/Library/Application Support detection, no desktop step
- install.sh / uninstall.sh: reduced to one-line exec python3 wrappers
  - eliminates SC1091 (source not followed) and SC2155 (declare+assign)
- scripts/detect_platform.sh: export INKSCAPE_CONFIG / INKSCAPE_INSTALL_METHOD
  - fixes SC2034 (variables appear unused to shellcheck)
- assets/illuscape.ico: pre-rendered 16/32/48/64/128/256px ICO (ImageMagick)
- tests/test_install.py: pytest integration tests replacing test_install.bats
  - cross-platform fake inkscape injected via PATH
  - 12 tests covering backup, palettes, templates, prefs, uninstall restore
- ci.yml: add windows-latest matrix leg; shellcheck/inkscape Linux-only;
  bats removed in favour of pytest integration tests; 27 tests total
2026-05-27 11:26:16 -07:00

151 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""
Illuscape uninstaller — cross-platform (Linux, Windows, macOS).
Usage:
python3 uninstall.py
./uninstall.sh (Linux / macOS)
"""
from __future__ import annotations
import os
import platform
import shutil
import subprocess
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
sys.path.insert(0, str(SCRIPT_DIR / "scripts"))
from detect_platform import detect_config # noqa: E402
ICON_SIZES = (16, 32, 48, 64, 128, 256, 512)
# All files installed by install.py, keyed by subdirectory
_INSTALLED_FILES: dict[str, list[str]] = {
"keys": [
"illustrator-cc.xml",
"illustrator-cs6.xml",
],
"palettes": [
"Illustrator-Defaults.gpl",
"Illustrator-Grays.gpl",
"Illustrator-Earth.gpl",
],
"templates": [
"Letter.svg",
"A4.svg",
"Web-1920x1080.svg",
"Web-1280x720.svg",
"Print-CMYK-Letter.svg",
"Print-CMYK-A4.svg",
],
"symbols": [
"illuscape-common.svg",
],
"splashscreens": [
"illuscape-splash.png",
],
}
# ── Backup discovery ──────────────────────────────────────────────────────────
def find_backup(config_path: Path) -> Path | None:
"""Return the most recent illuscape backup directory, or None."""
backups = sorted(config_path.parent.glob(config_path.name + ".bak-illuscape-*"))
return backups[-1] if backups else None
# ── Restore ───────────────────────────────────────────────────────────────────
def restore_prefs(backup: Path, config_path: Path) -> None:
"""Copy preferences.xml back from the backup directory."""
src = backup / "preferences.xml"
dst = config_path / "preferences.xml"
if src.exists():
shutil.copy2(src, dst)
print(f"→ preferences.xml restored from: {backup}")
else:
print("⚠ No preferences.xml in backup — skipping restore")
# ── File removal ──────────────────────────────────────────────────────────────
def remove_files(config_path: Path) -> None:
"""Delete all files that install.py placed in the Inkscape config dir."""
for subdir, names in _INSTALLED_FILES.items():
for name in names:
(config_path / subdir / name).unlink(missing_ok=True)
(config_path / "illuscape.ico").unlink(missing_ok=True)
print("→ Illuscape config files removed")
# ── Desktop cleanup ───────────────────────────────────────────────────────────
def remove_desktop(install_method: str) -> None:
"""Remove the desktop launcher / Start Menu shortcut for the current platform."""
system = platform.system()
if system == "Linux" and install_method == "native":
_remove_linux_desktop()
elif system == "Windows":
_remove_windows_desktop()
def _remove_linux_desktop() -> None:
lnk = Path.home() / ".local/share/applications/org.inkscape.Inkscape.desktop"
lnk.unlink(missing_ok=True)
for size in ICON_SIZES:
icon = (
Path.home()
/ f".local/share/icons/hicolor/{size}x{size}/apps/illuscape.png"
)
icon.unlink(missing_ok=True)
app_dir = Path.home() / ".local/share/applications"
subprocess.run(["update-desktop-database", str(app_dir)], capture_output=True)
print("→ Desktop launcher removed")
def _remove_windows_desktop() -> None:
lnk = (
Path(os.environ.get("APPDATA", ""))
/ "Microsoft/Windows/Start Menu/Programs/Illuscape.lnk"
)
lnk.unlink(missing_ok=True)
print("→ Start Menu shortcut removed")
# ── Main ──────────────────────────────────────────────────────────────────────
def main() -> None:
print("╔══════════════════════════════╗")
print("║ Illuscape Uninstaller ║")
print("╚══════════════════════════════╝")
print()
config_path, install_method = detect_config()
print(f"→ Inkscape config: {config_path} ({install_method})")
backup = find_backup(config_path)
if backup is None:
print(f"⚠ No Illuscape backup found at {config_path}.bak-illuscape-*")
print(" Cannot restore original preferences.xml automatically.")
print(" Removing only Illuscape-installed files.")
else:
restore_prefs(backup, config_path)
remove_files(config_path)
remove_desktop(install_method)
print()
print("✓ Illuscape uninstalled.")
print()
print(" Note: Your Inkscape preferences may have changed since Illuscape was")
print(" installed. If anything looks wrong, review:")
print(f" {config_path}/preferences.xml")
print()
if __name__ == "__main__":
main()