Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

164
tools/run-host.py Normal file
View file

@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""Prepare and run the independent Gaia desktop on a pinned Mozilla runtime."""
import argparse
import fcntl
import json
import os
from pathlib import Path
import shutil
import socket
import subprocess
import sys
import time
ROOT = Path(__file__).resolve().parents[1]
from project import validate_assets
def prepare(lock, test=False):
runtime = ROOT / lock["runtime"]
if not (runtime / "firefox").is_file():
raise RuntimeError(
"Moteur absent. Exécuter ./setup.sh pour installer le moteur verrouillé."
)
validate_assets()
for name in ["host", "services", "adapters"] + (["tests"] if test else []):
shutil.copytree(ROOT / name, runtime / "vulpes" / name, dirs_exist_ok=True)
(runtime / "defaults/pref").mkdir(parents=True, exist_ok=True)
shutil.copy2(ROOT / "host/bootstrap/vulpes.cfg", runtime / "vulpes.cfg")
shutil.copy2(
ROOT / "host/bootstrap/vulpes-autoconfig.js", runtime / "defaults/pref/vulpes-autoconfig.js"
)
return runtime
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--candidate", action="store_true")
parser.add_argument(
"--legacy-apps", action="store_true", help="Trois paquets archivés, profil séparé"
)
parser.add_argument(
"--test", action="store_true", help="Marionette 2857, profil de test et fenêtre auxiliaire"
)
parser.add_argument("--prepare-only", action="store_true")
args = parser.parse_args()
lock = json.loads(
(ROOT / ("candidate-engine.json" if args.candidate else "engine-lock.json")).read_text()
)
(ROOT / "logs").mkdir(exist_ok=True)
# One host owns the loopback origins/registry. Never attach silently to an
# unknown HTTP listener or to another application's profile.
with (ROOT / "logs/host.lock").open("w") as guard:
try:
fcntl.flock(guard, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError:
raise RuntimeError(
"Un hôte Gecko actuel est déjà ouvert. Fermer sa fenêtre avant de relancer."
)
if args.legacy_apps:
subprocess.run([sys.executable, str(ROOT / "tools/prepare-legacy-apps.py")], check=True)
runtime = prepare(lock, args.test)
if args.prepare_only:
print("Adaptateur synchronisé :", runtime)
return
probe = socket.socket()
probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
probe.bind(("127.0.0.1", 8765))
except OSError:
raise RuntimeError("Port Gaia 8765 déjà occupé. Fermer le précédent hôte/serveur Gaia.")
finally:
probe.close()
profile = (
ROOT
/ "profiles"
/ (
("host-" if args.test else "desktop-")
+ ("legacy-" if args.legacy_apps else "")
+ lock["version"]
)
)
profile.mkdir(parents=True, exist_ok=True)
prefs = {
"browser.shell.checkDefaultBrowser": False,
"browser.dom.window.dump.enabled": True,
"browser.startup.homepage_override.mstone": "ignore",
"browser.aboutwelcome.enabled": False,
"font.default.x-western": "sans-serif",
"font.name.sans-serif.x-western": "Fira Sans",
"marionette.enabled": args.test,
"datareporting.policy.dataSubmissionEnabled": False,
}
if args.test:
prefs["marionette.port"] = 2857
(profile / "user.js").write_text(
"\n".join(f"user_pref({json.dumps(k)}, {json.dumps(v)});" for k, v in prefs.items())
+ "\n"
)
env = os.environ.copy()
for name in list(env):
if name.startswith("MOZ_DISABLE_") or name in (
"B2G_HOMESCREEN",
"MOZ_MARIONETTE",
"MOZ_HEADLESS",
):
env.pop(name)
env.update(VULPES_HOST_ROOT=str(runtime / "vulpes/host"), VULPES_WORKSPACE_ROOT=str(ROOT))
command = [
str(runtime / "firefox"),
"-no-remote",
"-purgecaches",
"-profile",
str(profile),
"-chrome",
"chrome://vulpes/content/shell.xhtml",
]
if args.test:
command += ["-marionette", "--remote-allow-system-access", "-new-window", "about:blank"]
proc = None
with (ROOT / "logs/gaia-server.log").open("w") as output:
server = subprocess.Popen(
[
sys.executable,
str(ROOT / "tools/serve-gaia.py"),
*(["--legacy-apps"] if args.legacy_apps else []),
],
stdout=output,
stderr=subprocess.STDOUT,
)
try:
for _ in range(900):
if server.poll() is not None:
raise RuntimeError("Échec du serveur Gaia : logs/gaia-server.log")
try:
with socket.create_connection(("127.0.0.1", 8765), timeout=0.2):
break
except OSError:
time.sleep(0.05)
else:
raise RuntimeError("Le serveur Gaia ne répond pas.")
print(f'Vulpes / Gecko {lock["version"]} — profil {profile}', flush=True)
proc = subprocess.Popen(command, env=env)
result = proc.wait()
if result:
raise RuntimeError(f"Gecko a quitté avec le code {result}")
finally:
if proc and proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=10)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait()
server.terminate()
server.wait(timeout=10)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass
except (RuntimeError, OSError) as error:
sys.exit(str(error))