File size: 3,348 Bytes
9756075
 
 
81e9ff5
9756075
 
81e9ff5
9756075
2b065a1
 
 
81e9ff5
2b065a1
 
 
 
81e9ff5
9756075
 
 
81e9ff5
9756075
 
81e9ff5
9756075
 
 
 
 
 
 
81e9ff5
 
 
 
 
 
2b065a1
 
81e9ff5
 
9756075
 
 
81e9ff5
9756075
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81e9ff5
9756075
81e9ff5
9756075
 
 
81e9ff5
9756075
 
 
81e9ff5
9756075
 
81e9ff5
9756075
2b065a1
81e9ff5
 
 
 
2b065a1
 
81e9ff5
 
 
 
 
 
9756075
2b065a1
81e9ff5
 
 
 
9756075
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# src/rotator_library/utils/headless_detection.py

import os
import sys
import logging

lib_logger = logging.getLogger("rotator_library")

# Import console for user-visible output
try:
    from rich.console import Console

    console = Console()
except ImportError:
    console = None


def is_headless_environment() -> bool:
    """
    Detects if the current environment is headless (no GUI available).

    Returns:
        True if headless environment is detected, False otherwise

    Detection logic:
    - Linux/Unix: Check DISPLAY environment variable
    - SSH detection: Check SSH_CONNECTION or SSH_CLIENT
    - CI environments: Check common CI environment variables
    - Windows: Check SESSIONNAME for service/headless indicators
    """
    headless_indicators = []

    # Check DISPLAY for Linux GUI availability (skip on Windows and macOS)
    # NOTE: DISPLAY is an X11 (X Window System) variable used on Linux.
    # macOS uses its native Quartz windowing system, NOT X11, so DISPLAY is
    # typically unset on macOS even with a full GUI. Only check DISPLAY on Linux.
    if os.name != "nt" and sys.platform != "darwin":  # Linux only
        display = os.getenv("DISPLAY")
        if display is None or display.strip() == "":
            headless_indicators.append("No DISPLAY variable (Linux headless)")

    # Check for SSH connection
    if os.getenv("SSH_CONNECTION") or os.getenv("SSH_CLIENT") or os.getenv("SSH_TTY"):
        headless_indicators.append("SSH connection detected")

    # Check for CI environments
    ci_vars = [
        "CI",  # Generic CI indicator
        "GITHUB_ACTIONS",  # GitHub Actions
        "GITLAB_CI",  # GitLab CI
        "JENKINS_URL",  # Jenkins
        "CIRCLECI",  # CircleCI
        "TRAVIS",  # Travis CI
        "BUILDKITE",  # Buildkite
        "DRONE",  # Drone CI
        "TEAMCITY_VERSION",  # TeamCity
        "TF_BUILD",  # Azure Pipelines
        "CODEBUILD_BUILD_ID",  # AWS CodeBuild
    ]
    for var in ci_vars:
        if os.getenv(var):
            headless_indicators.append(f"CI environment detected ({var})")
            break

    # Check Windows session type
    if os.name == "nt":  # Windows
        session_name = os.getenv("SESSIONNAME", "").lower()
        if session_name in ["services", "rdp-tcp"]:
            headless_indicators.append(f"Windows headless session ({session_name})")

    # Detect Docker/container environment
    if os.path.exists("/.dockerenv") or os.path.exists("/run/.containerenv"):
        headless_indicators.append("Container environment detected")

    # Determine if headless
    is_headless = len(headless_indicators) > 0

    if is_headless:
        # Log to logger
        lib_logger.info(
            f"Headless environment detected: {'; '.join(headless_indicators)}"
        )

        # Print to console for user visibility
        if console:
            console.print(
                f"[yellow]ℹ Headless environment detected:[/yellow] {'; '.join(headless_indicators)}"
            )
            console.print(
                "[yellow]→ Browser will NOT open automatically. Please use the URL below.[/yellow]\n"
            )
    else:
        # Only log to debug, no console output
        lib_logger.debug(
            "GUI environment detected, browser auto-open will be attempted"
        )

    return is_headless