diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9a8c83bee..df3af27b5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,3 +64,10 @@ jobs: - name: Run Flake8 run: poetry run flake8 --exclude=alembic,.venv,venv,contrib,.cache,.git + + # Guard against import-time breakage (missing/renamed imports, bad lazy + # imports) -- the class of error that dominates GlitchTip. See + # GLITCHTIP_FIX_PLAN.md. TODO: extend to a Windows matrix to cover the + # pywinauto import path. + - name: Import-smoke (core modules must import cleanly) + run: poetry run python -c "import openadapt, openadapt.config, openadapt.utils, openadapt.models, openadapt.db.db, openadapt.window, openadapt.error_reporting; print('imports ok')" diff --git a/.gitignore b/.gitignore index 9e7beb0dc..0150674cb 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ build/ OpenAdapt.spec build_scripts/OpenAdapt.iss + +# Private keys (never commit) +*.pem diff --git a/openadapt/config.py b/openadapt/config.py index ca3c01801..d8b700aac 100644 --- a/openadapt/config.py +++ b/openadapt/config.py @@ -124,9 +124,11 @@ class SegmentationAdapter(str, Enum): # Error reporting ERROR_REPORTING_ENABLED: bool = True + # Reports to the live GlitchTip project openadaptai/openadapt (id 3798). + # (Project 8771, previously set here, is not in the org and is unreachable, + # so its events were never visible in the dashboard.) ERROR_REPORTING_DSN: ClassVar = ( - # "https://dcf5d7889a3b4b47ae12a3af9ffcbeb7@app.glitchtip.com/3798" - "https://5d24fc5a2e674ea6b42275e5702499ce@app.glitchtip.com/8771" + "https://dcf5d7889a3b4b47ae12a3af9ffcbeb7@app.glitchtip.com/3798" ) ERROR_REPORTING_BRANCH: ClassVar = "main" diff --git a/openadapt/error_reporting.py b/openadapt/error_reporting.py index 2dee6a616..0527a6228 100644 --- a/openadapt/error_reporting.py +++ b/openadapt/error_reporting.py @@ -60,10 +60,50 @@ def show_alert() -> None: msg.exec() +# Errors from environments where OpenAdapt cannot run -- headless / no display, +# unsupported platform, or a broken/partial native dependency set -- are not +# actionable bugs. They otherwise flood the dashboard from CI and automated +# headless environments and bury real issues. Drop them. See GLITCHTIP_FIX_PLAN.md. +_UNSUPPORTED_ENV_SUBSTRINGS = ( + "failed to acquire X connection", + "Bad display name", + "DISPLAY not set", + "this platform is not supported", # pynput import on headless Linux + "No module named 'pywinauto'", # non-Windows / old releases (guarded on main) + "module 'pywinauto' has no attribute", + "_ARRAY_API not found", # numpy 1.x/2.x ABI mismatch in a broken env + "crop_active_window should return an image", # headless screenshot returned None +) + + +def is_unsupported_environment_error(event: Any, hint: Any) -> bool: + """Return True if the event is an unactionable unsupported-environment error.""" + texts = [] + exc_info = hint.get("exc_info") if hint else None + if exc_info and exc_info[1] is not None: + texts.append(f"{type(exc_info[1]).__name__}: {exc_info[1]}") + for value in (event.get("exception", {}) or {}).get("values", []) or []: + texts.append(f"{value.get('type')}: {value.get('value')}") + blob = " ".join(texts) + return any(substring in blob for substring in _UNSUPPORTED_ENV_SUBSTRINGS) + + def before_send_event(event: Any, hint: Any) -> Any: - """Handle the event before sending it to Sentry.""" + """Filter events before sending to GlitchTip. + + Drops unactionable unsupported-environment errors (headless, wrong platform, + broken native deps) that otherwise flood the dashboard from CI/automated + environments. For genuine errors, surfaces a user alert -- but only when a GUI + is actually running, never headless, and never for filtered noise. + """ + if is_unsupported_environment_error(event, hint): + return None # drop -- not an actionable bug + try: - show_alert() + from PySide6.QtWidgets import QApplication + + if QApplication.instance() is not None: + show_alert() except Exception: pass return event