diff --git a/ushadow/backend/Dockerfile b/ushadow/backend/Dockerfile index ba1ff299..a30a20f5 100644 --- a/ushadow/backend/Dockerfile +++ b/ushadow/backend/Dockerfile @@ -8,16 +8,20 @@ FROM python:3.12-slim AS builder WORKDIR /app -# Install uv for fast Python package management -COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/ - -# Install build dependencies for Python packages (gcc, python3-dev, libyaml-dev) +# Install build dependencies for Python packages (gcc, python3-dev, libyaml-dev, curl for uv install) RUN apt-get update && apt-get install -y \ gcc \ python3-dev \ libyaml-dev \ + curl \ && rm -rf /var/lib/apt/lists/* +# Install uv for fast Python package management +# Using official installation script (recommended method) +RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ + ln -s /root/.local/bin/uv /usr/local/bin/uv && \ + ln -s /root/.local/bin/uvx /usr/local/bin/uvx + # Copy dependency files first for better layer caching COPY pyproject.toml uv.lock ./ @@ -32,10 +36,7 @@ FROM python:3.12-slim WORKDIR /app -# Install uv for running the app -COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/ - -# Install only runtime dependencies (curl for healthcheck, docker-cli for volume cleanup, tailscale) +# Install runtime dependencies (curl for healthcheck and uv install, docker-cli for volume cleanup, tailscale) RUN apt-get update && apt-get install -y \ curl \ ca-certificates \ @@ -49,6 +50,12 @@ RUN apt-get update && apt-get install -y \ && curl -fsSL https://tailscale.com/install.sh | sh \ && rm -rf /var/lib/apt/lists/* +# Install uv for running the app +# Using official installation script (recommended method) +RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ + ln -s /root/.local/bin/uv /usr/local/bin/uv && \ + ln -s /root/.local/bin/uvx /usr/local/bin/uvx + # Copy virtual environment from builder COPY --from=builder /app/.venv /app/.venv diff --git a/ushadow/backend/main.py b/ushadow/backend/main.py index 22314653..8b69554c 100644 --- a/ushadow/backend/main.py +++ b/ushadow/backend/main.py @@ -45,7 +45,7 @@ # Telemetry configuration TELEMETRY_ENDPOINT = os.environ.get( "TELEMETRY_ENDPOINT", - "https://ushadow-telemetry.your-subdomain.workers.dev" + "https://ushadow-telemetry.stu-6b7.workers.dev" ) diff --git a/ushadow/backend/src/utils/telemetry.py b/ushadow/backend/src/utils/telemetry.py index 5683afd0..d696a44f 100644 --- a/ushadow/backend/src/utils/telemetry.py +++ b/ushadow/backend/src/utils/telemetry.py @@ -23,7 +23,7 @@ class TelemetryClient: def __init__( self, - endpoint: str = "https://ushadow-telemetry.your-subdomain.workers.dev", + endpoint: str = "https://ushadow-telemetry.stu-6b7.workers.dev", app_version: str = "unknown", config_dir: Optional[Path] = None, ): @@ -43,6 +43,16 @@ def __init__( self.machine_id = self._get_or_create_machine_id() self.os_info = self._get_os_info() + def is_telemetry_disabled(self) -> bool: + """ + Check if telemetry has been disabled by the user. + + Returns: + True if telemetry is disabled, False otherwise + """ + telemetry_disabled_file = self.config_dir / "telemetry_disabled" + return telemetry_disabled_file.exists() + def _get_or_create_machine_id(self) -> str: """ Get or create a stable machine identifier. @@ -122,8 +132,12 @@ def send_ping(self, timeout: int = 5) -> bool: timeout: Request timeout in seconds Returns: - True if ping succeeded, False otherwise + True if ping succeeded, False otherwise (including if telemetry is disabled) """ + # Check if telemetry is disabled + if self.is_telemetry_disabled(): + return False + try: data = { 'machine_id': self.machine_id, @@ -157,8 +171,12 @@ def send_install_event(self, install_method: str = "unknown", timeout: int = 5) timeout: Request timeout in seconds Returns: - True if event sent successfully, False otherwise + True if event sent successfully, False otherwise (including if telemetry is disabled) """ + # Check if telemetry is disabled + if self.is_telemetry_disabled(): + return False + try: data = { 'machine_id': self.machine_id, @@ -185,7 +203,7 @@ def send_install_event(self, install_method: str = "unknown", timeout: int = 5) # Convenience function for simple usage def send_telemetry_ping( - endpoint: str = "https://ushadow-telemetry.your-subdomain.workers.dev", + endpoint: str = "https://ushadow-telemetry.stu-6b7.workers.dev", app_version: str = "unknown" ) -> bool: """ @@ -205,7 +223,7 @@ def send_telemetry_ping( if __name__ == "__main__": # Example usage client = TelemetryClient( - endpoint="https://ushadow-telemetry.your-subdomain.workers.dev", + endpoint="https://ushadow-telemetry.stu-6b7.workers.dev", app_version="0.2.4" ) diff --git a/ushadow/backend/uv.lock b/ushadow/backend/uv.lock index 7da1fad6..6ec6ad49 100644 --- a/ushadow/backend/uv.lock +++ b/ushadow/backend/uv.lock @@ -1982,6 +1982,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] +[[package]] +name = "pytest-env" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/12/9c87d0ca45d5992473208bcef2828169fa7d39b8d7fc6e3401f5c08b8bf7/pytest_env-1.2.0.tar.gz", hash = "sha256:475e2ebe8626cee01f491f304a74b12137742397d6c784ea4bc258f069232b80", size = 8973, upload-time = "2025-10-09T19:15:47.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/98/822b924a4a3eb58aacba84444c7439fce32680592f394de26af9c76e2569/pytest_env-1.2.0-py3-none-any.whl", hash = "sha256:d7e5b7198f9b83c795377c09feefa45d56083834e60d04767efd64819fc9da00", size = 6251, upload-time = "2025-10-09T19:15:46.077Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -2653,6 +2665,7 @@ dev = [ { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, + { name = "pytest-env" }, { name = "ruff" }, ] @@ -2689,6 +2702,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.3" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=6.0.0" }, + { name = "pytest-env", marker = "extra == 'dev'", specifier = ">=1.1.0" }, { name = "python-dotenv", specifier = ">=1.0.1" }, { name = "python-jose", extras = ["cryptography"], specifier = ">=3.3.0" }, { name = "python-multipart", specifier = ">=0.0.20" }, diff --git a/ushadow/manager/telemetry.py b/ushadow/manager/telemetry.py index 5683afd0..d696a44f 100644 --- a/ushadow/manager/telemetry.py +++ b/ushadow/manager/telemetry.py @@ -23,7 +23,7 @@ class TelemetryClient: def __init__( self, - endpoint: str = "https://ushadow-telemetry.your-subdomain.workers.dev", + endpoint: str = "https://ushadow-telemetry.stu-6b7.workers.dev", app_version: str = "unknown", config_dir: Optional[Path] = None, ): @@ -43,6 +43,16 @@ def __init__( self.machine_id = self._get_or_create_machine_id() self.os_info = self._get_os_info() + def is_telemetry_disabled(self) -> bool: + """ + Check if telemetry has been disabled by the user. + + Returns: + True if telemetry is disabled, False otherwise + """ + telemetry_disabled_file = self.config_dir / "telemetry_disabled" + return telemetry_disabled_file.exists() + def _get_or_create_machine_id(self) -> str: """ Get or create a stable machine identifier. @@ -122,8 +132,12 @@ def send_ping(self, timeout: int = 5) -> bool: timeout: Request timeout in seconds Returns: - True if ping succeeded, False otherwise + True if ping succeeded, False otherwise (including if telemetry is disabled) """ + # Check if telemetry is disabled + if self.is_telemetry_disabled(): + return False + try: data = { 'machine_id': self.machine_id, @@ -157,8 +171,12 @@ def send_install_event(self, install_method: str = "unknown", timeout: int = 5) timeout: Request timeout in seconds Returns: - True if event sent successfully, False otherwise + True if event sent successfully, False otherwise (including if telemetry is disabled) """ + # Check if telemetry is disabled + if self.is_telemetry_disabled(): + return False + try: data = { 'machine_id': self.machine_id, @@ -185,7 +203,7 @@ def send_install_event(self, install_method: str = "unknown", timeout: int = 5) # Convenience function for simple usage def send_telemetry_ping( - endpoint: str = "https://ushadow-telemetry.your-subdomain.workers.dev", + endpoint: str = "https://ushadow-telemetry.stu-6b7.workers.dev", app_version: str = "unknown" ) -> bool: """ @@ -205,7 +223,7 @@ def send_telemetry_ping( if __name__ == "__main__": # Example usage client = TelemetryClient( - endpoint="https://ushadow-telemetry.your-subdomain.workers.dev", + endpoint="https://ushadow-telemetry.stu-6b7.workers.dev", app_version="0.2.4" )