diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2e5faab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Mark the file as binary, so that Git won't not convert the line endings. +# Otherwise, the file becomes unreadable for bash inside Docker (on Windows platforms). +entrypoint.sh -text + diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index 092e5ae..90e30b3 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -28,18 +28,7 @@ jobs: shell: powershell - name: Check Chrome Version - run: '& "C:\Program Files\Google\Chrome\Application\chrome.exe" --version' - shell: powershell - - - name: Add Chrome to PATH - run: | - $chromePath = "C:\Program Files\Google\Chrome\Application" - echo "Adding $chromePath to PATH" - echo "$chromePath" | Out-File -Append -Encoding utf8 $env:GITHUB_PATH - shell: powershell - - - name: Verify Chrome Installation - run: chrome --version + run: '(Get-Item "C:\Program Files\Google\Chrome\Application\chrome.exe").VersionInfo' shell: powershell - name: Upgrade pip diff --git a/html2print/html2print.py b/html2print/html2print.py index 955b3d0..13c16dd 100644 --- a/html2print/html2print.py +++ b/html2print/html2print.py @@ -109,7 +109,18 @@ def _download_chromedriver( path_to_cached_chrome_driver: str, ) -> str: url = "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json" - response = ChromeDriverManager.send_http_get_request(url).json() + response = ChromeDriverManager.send_http_get_request(url) + if response is None: + raise RuntimeError( + "Could not download known-good-versions-with-downloads.json" + ) + + response = response.json() + if response is None: + raise RuntimeError( + "Could not parse known-good-versions-with-downloads.json" + ) + assert isinstance(response, dict) matching_versions = [ item @@ -118,7 +129,7 @@ def _download_chromedriver( ] if not matching_versions: - raise Exception( + raise RuntimeError( f"No compatible ChromeDriver found for Chrome version {chrome_major_version}" ) @@ -142,6 +153,11 @@ def _download_chromedriver( ) response = ChromeDriverManager.send_http_get_request(driver_url) + if response is None: + raise RuntimeError( + f"Could not download ChromeDriver from {driver_url}" + ) + Path(path_to_driver_cache_dir).mkdir(parents=True, exist_ok=True) zip_path = os.path.join(path_to_driver_cache_dir, "chromedriver.zip") print( # noqa: T201 @@ -179,6 +195,9 @@ def send_http_get_request(url: str) -> Response: f"html2print: " f"failed to get response for URL: {url} with error: {last_error}" ) + raise RuntimeError( + f"GET request failed after 3 attempts: {url}" + ) from last_error @staticmethod def get_chrome_version() -> Optional[str]: @@ -311,21 +330,23 @@ def create_webdriver( ) -> webdriver.Chrome: print("html2print: creating ChromeDriver service.", flush=True) # noqa: T201 - path_to_chrome: str + path_to_chrome_driver: str if chromedriver_argument is None: - path_to_chrome = ChromeDriverManager().get_chrome_driver( + path_to_chrome_driver = ChromeDriverManager().get_chrome_driver( path_to_cache_dir ) else: - path_to_chrome = chromedriver_argument - print(f"html2print: ChromeDriver available at path: {path_to_chrome}") # noqa: T201 + path_to_chrome_driver = chromedriver_argument + print( # noqa: T201 + f"html2print: ChromeDriver available at path: {path_to_chrome_driver}" + ) if debug: service = Service( - path_to_chrome, log_output=PATH_TO_CHROME_DRIVER_DEBUG_LOG + path_to_chrome_driver, log_output=PATH_TO_CHROME_DRIVER_DEBUG_LOG ) else: - service = Service(path_to_chrome) + service = Service(path_to_chrome_driver) webdriver_options = Options() webdriver_options.add_argument("start-maximized") @@ -333,8 +354,16 @@ def create_webdriver( # Doesn't seem to be needed. # webdriver_options.add_argument('--disable-gpu') # noqa: ERA001 webdriver_options.add_argument("--disable-extensions") - webdriver_options.add_argument("--headless=chrome") - # FIXME: This is not nice but otherwise it does not work in Ubuntu 24-based Docker image. + + # Use --headless=new, as it seems to be more stable on Windows (available since Chrome 109). + # see https://www.selenium.dev/blog/2023/headless-is-going-away/ + webdriver_options.add_argument("--headless=new") + + # Docker disables some syscalls that are required for Chrome's sandbox to work. + # https://stackoverflow.com/questions/68855734/how-to-setup-chrome-sandbox-on-docker-container + # We prefer isolation of the container over isolation of tabs within Chrome, + # and thus disable the sandbox. + # See also: # https://github.com/SeleniumHQ/selenium/issues/15327#issuecomment-2689287561 webdriver_options.add_argument("--no-sandbox") diff --git a/tests/integration/lit.cfg b/tests/integration/lit.cfg index 6c8c1a2..c84db86 100644 --- a/tests/integration/lit.cfg +++ b/tests/integration/lit.cfg @@ -23,3 +23,9 @@ config.suffixes = ['.itest', '.c'] config.is_windows = lit_config.isWindows if not lit_config.isWindows: config.available_features.add('PLATFORM_IS_NOT_WINDOWS') + +# In Linux CI, $HOME is required for Chrome as it needs to access things in ~/.local +config.environment['HOME'] = os.environ.get('HOME', '/tmp') + +# In Windows CI, %ProgramW6432% is required for Selenium to properly detect browsers +config.environment['ProgramW6432'] = os.environ.get('ProgramW6432', '')