illuscape/scripts/detect_platform.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

82 lines
2.6 KiB
Python

"""
detect_platform.py — Inkscape config-path detection, cross-platform.
Shared by install.py and uninstall.py.
"""
from __future__ import annotations
import os
import platform
import subprocess
from pathlib import Path
def _cmd_contains(cmd: list[str], pattern: str) -> bool:
"""Run *cmd*; return True if stdout contains *pattern*."""
try:
result = subprocess.run(
cmd, capture_output=True, text=True, timeout=5
)
return pattern in result.stdout
except (FileNotFoundError, subprocess.TimeoutExpired):
return False
def detect_config() -> tuple[Path, str]:
"""
Return (inkscape_config_path, install_method).
install_method is one of: "flatpak", "snap", "native", "store"
"""
system = platform.system()
if system == "Windows":
return _detect_windows()
if system == "Darwin":
return _detect_macos()
return _detect_linux()
def _detect_linux() -> tuple[Path, str]:
# Flatpak takes priority — most common modern Linux install
flatpak_config = Path.home() / ".var/app/org.inkscape.Inkscape/config/inkscape"
if flatpak_config.is_dir() or _cmd_contains(
["flatpak", "list"], "org.inkscape.Inkscape"
):
return flatpak_config, "flatpak"
# Snap
snap_config = Path.home() / "snap/inkscape/current/.config/inkscape"
if snap_config.is_dir() or _cmd_contains(["snap", "list"], "inkscape"):
return snap_config, "snap"
# Native / XDG
xdg = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "inkscape"
return xdg, "native"
def _detect_macos() -> tuple[Path, str]:
# App Bundle (DMG install) stores config under ~/Library/Application Support
app_support = (
Path.home()
/ "Library/Application Support/org.inkscape.Inkscape/config/inkscape"
)
if app_support.is_dir():
return app_support, "native"
# Homebrew / XDG fallback
xdg = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "inkscape"
return xdg, "native"
def _detect_windows() -> tuple[Path, str]:
# MS Store — glob for the Packages directory
local_appdata = os.environ.get("LOCALAPPDATA", "")
if local_appdata:
packages = Path(local_appdata) / "Packages"
if packages.is_dir():
matches = list(packages.glob("org.inkscape.Inkscape*"))
if matches:
return matches[0] / "LocalCache/Roaming/inkscape", "store"
# Native (winget / Scoop / chocolatey / official installer)
appdata = Path(os.environ.get("APPDATA", Path.home() / "AppData/Roaming"))
return appdata / "inkscape", "native"