Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions odev/commands/database/test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Run unit tests on an empty local Odoo database."""

import os
import re
from collections import defaultdict
from collections.abc import Mapping, MutableMapping
Expand All @@ -9,6 +10,7 @@
import requests

from odev.common import args, string
from odev.common.browsers import Chrome
from odev.common.commands import OdoobinCommand
from odev.common.console import TableHeader
from odev.common.databases import LocalDatabase
Expand Down Expand Up @@ -193,6 +195,13 @@ def run(self):
"""Run the command."""
if not self.args.no_auto_tags:
self.apply_auto_tags()

if any(tag in self.test_tags for tag in ["clic_all", "click_all", "tours"]):
chrome = Chrome(self.odev)
chrome_bin = chrome.provision()
wrapper = chrome.get_wrapper(chrome_bin)
os.environ["ODOO_BROWSER_BIN"] = str(wrapper)

self.run_test_database()

def cleanup(self):
Expand Down
120 changes: 120 additions & 0 deletions odev/common/browsers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""Browser provisioning utilities."""

import re
import shutil
import subprocess
from functools import lru_cache
from pathlib import Path

import requests

from odev.common.logging import logging


logger = logging.getLogger(__name__)


class Chrome:
"""Manages Chrome provisioning and wrapper generation."""

VERSION = "145.0.7632.116"
Comment thread
sea-odoo marked this conversation as resolved.
"""Fallback version if Runbot fetching fails."""

@classmethod
@lru_cache(maxsize=1)
def fetch_version(cls) -> str:
"""Fetch the latest Chrome version used by Runbot.

:return: The Chrome version string (e.g., "145.0.7632.116").
"""
url = "https://runbot.odoo.com/runbot/dockerfile/tag/odoo:DockerMaster"
try:
logger.debug(f"Fetching Chrome version from {url}")
response = requests.get(url, timeout=10)
response.raise_for_status()
# Parse version from: # Install chrome with values {"chrome_version": "145.0.7632.116-1"}
match = re.search(r'chrome_version": "([\d\.]+)', response.text)
if match:
return match.group(1)
except requests.RequestException as e:
logger.warning(f"Could not fetch Chrome version from Runbot: {e}")

return cls.VERSION

def __init__(self, odev):
self.odev = odev
self.version = self.fetch_version()
self.base_path = self.odev.home_path / "browsers" / "chrome" / self.version
# Puppeteer structure: <base>/chrome/linux-<version>/chrome-linux64/chrome
self.executable = self.base_path / "chrome" / f"linux-{self.version}" / "chrome-linux64" / "chrome"

def provision(self) -> Path | None:
"""Ensure the specific version of Chrome is installed.

:return: Path to the Chrome executable, or None if provisioning failed.
"""
if not self.executable.exists():
logger.info(f"Provisioning Chrome {self.version} for tours...")
self.base_path.mkdir(parents=True, exist_ok=True)
npx = shutil.which("npx")
if not npx:
logger.warning("npx not found, skipping Chrome provisioning")
return None

try:
subprocess.run( # noqa: S603
[
npx,
"-y",
"@puppeteer/browsers",
"install",
f"chrome@{self.version}",
"--path",
str(self.base_path),
],
check=True,
capture_output=True,
)
except subprocess.CalledProcessError as e:
logger.warning(f"Failed to provision Chrome {self.version}: {e.stderr.decode()}")
return None
except OSError as e:
logger.warning(f"OS error provisioning Chrome {self.version}: {e}")
return None

return self.executable if self.executable.exists() else None

def get_wrapper(self, chrome_bin: Path | None = None) -> Path:
"""Create a Chrome wrapper script with consistent rendering flags.

:param chrome_bin: Optional path to the Chrome binary to use.
:return: Path to the generated wrapper script.
"""
tmp_dir = self.odev.home_path / "tmp"
tmp_dir.mkdir(parents=True, exist_ok=True)
wrapper = tmp_dir / "odoo-chrome-wrapper"

search_bins = "google-chrome chromium chromium-browser google-chrome-stable"
if chrome_bin:
search_bins = f"{chrome_bin} {search_bins}"

wrapper.write_text(
"#!/bin/bash\n"
f"for bin in {search_bins}; do\n"
' real=$(command -v "$bin" 2>/dev/null)\n'
' if [ -n "$real" ]; then\n'
' exec "$real" \\\n'
" --font-render-hinting=none \\\n"
" --force-device-scale-factor=1 \\\n"
" --disable-font-subpixel-positioning \\\n"
" --hide-scrollbars \\\n"
" --window-size=1366,768 \\\n"
" --no-sandbox \\\n"
' "$@"\n'
" fi\n"
"done\n"
'echo "Chrome not found" >&2\n'
"exit 1\n"
)
wrapper.chmod(0o755)
return wrapper
Loading