154 lines
5.5 KiB
Python
154 lines
5.5 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
from typing import Optional
|
|
|
|
from taskgraph.transforms.base import TransformSequence
|
|
from taskgraph.util.schema import Schema
|
|
from taskgraph.util.templates import merge
|
|
|
|
_FX_CMD = "sh browser/installer/linux/script/install-firefox.sh"
|
|
|
|
|
|
class TaskSchema(Schema, forbid_unknown_fields=False, kw_only=True, rename="kebab"):
|
|
install_method: Optional[str] = None
|
|
pre_install: Optional[str] = None
|
|
distro_symbol: Optional[str] = None
|
|
channels: Optional[list[str]] = None
|
|
locales: Optional[list[str]] = None
|
|
platform: Optional[str] = None
|
|
|
|
|
|
transforms = TransformSequence()
|
|
transforms.add_validate(TaskSchema)
|
|
|
|
|
|
def _resolve_docker_image(distro_name, distro_cfg, arch_name):
|
|
docker_image_cfg = distro_cfg["docker-image"]
|
|
if not isinstance(docker_image_cfg, dict):
|
|
raise ValueError(
|
|
f"Distro '{distro_name}' docker-image must be a dict, got: {docker_image_cfg!r}"
|
|
)
|
|
if arch_name in docker_image_cfg:
|
|
return docker_image_cfg[arch_name]
|
|
raise ValueError(
|
|
f"Distro '{distro_name}' has no docker-image entry for arch '{arch_name}'. "
|
|
f"Add it or restrict architectures."
|
|
)
|
|
|
|
|
|
def _build_worker_config(distro_cfg, docker_image):
|
|
worker_cfg = {"docker-image": docker_image}
|
|
if distro_cfg.get("max-run-time") is not None:
|
|
worker_cfg["max-run-time"] = distro_cfg["max-run-time"]
|
|
return worker_cfg
|
|
|
|
|
|
@transforms.add
|
|
def expand_distros(config, tasks):
|
|
kind_config = config.config
|
|
distros = kind_config.pop("distros", {})
|
|
architectures = kind_config.pop("architectures", {})
|
|
channels = kind_config.pop("channels", [])
|
|
locales = kind_config.pop("locales", [])
|
|
defaults = kind_config.get("task-defaults", {})
|
|
|
|
explicit_names = set(kind_config.get("tasks", {}).keys())
|
|
|
|
for task in tasks:
|
|
yield task
|
|
|
|
for distro_name, distro_cfg in distros.items():
|
|
allowed_arches = distro_cfg.get("architectures", list(architectures.keys()))
|
|
for arch_name, arch_cfg in architectures.items():
|
|
if arch_name not in allowed_arches:
|
|
continue
|
|
docker_image = _resolve_docker_image(distro_name, distro_cfg, arch_name)
|
|
worker_cfg = _build_worker_config(distro_cfg, docker_image)
|
|
for method in distro_cfg["methods"]:
|
|
name = f"{distro_name}-{arch_name}-{method}"
|
|
if name in explicit_names:
|
|
raise ValueError(
|
|
f"Generated task '{name}' conflicts with an explicit task in kind.yml"
|
|
)
|
|
task = {
|
|
"name": name,
|
|
"distro-symbol": distro_cfg["symbol"],
|
|
"install-method": method,
|
|
"worker-type": arch_cfg["worker-type"],
|
|
"worker": worker_cfg,
|
|
"platform": arch_cfg["platform"],
|
|
"channels": distro_cfg.get("channels", channels),
|
|
"locales": distro_cfg.get("locales", locales),
|
|
}
|
|
if distro_cfg.get("pre-install") is not None:
|
|
task["pre-install"] = distro_cfg["pre-install"]
|
|
if defaults:
|
|
task = merge(defaults, task)
|
|
yield task
|
|
|
|
|
|
@transforms.add
|
|
def set_treeherder_platform(config, tasks):
|
|
for task in tasks:
|
|
platform = task.pop("platform", None)
|
|
if platform:
|
|
task.setdefault("treeherder", {})["platform"] = platform
|
|
yield task
|
|
|
|
|
|
@transforms.add
|
|
def build_run_command(config, tasks):
|
|
for task in tasks:
|
|
method = task.get("install-method")
|
|
pre_install = task.pop("pre-install", None)
|
|
|
|
if method is None or task.get("run", {}).get("command"):
|
|
yield task
|
|
continue
|
|
|
|
# {{...}} produces a literal {matrix[channel]} placeholder substituted by taskgraph.transforms.matrix.
|
|
fx_args = f"--channel {{matrix[channel]}} --install-method {method} --lang {{matrix[locale]}}"
|
|
cmd = f"{_FX_CMD} {fx_args}"
|
|
parts = [pre_install, cmd] if pre_install else [cmd]
|
|
task.setdefault("run", {})["command"] = " && ".join(parts)
|
|
|
|
yield task
|
|
|
|
|
|
@transforms.add
|
|
def inject_matrix(config, tasks):
|
|
for task in tasks:
|
|
channels = task.pop("channels", None)
|
|
locales = task.pop("locales", None)
|
|
|
|
if channels is not None:
|
|
# Deliberate runtime check: the schema allows both fields to be
|
|
# absent, so this guards against a distro entry that sets channels
|
|
# without locales — caught at graph-generation time, not YAML-parse time.
|
|
if locales is None:
|
|
raise ValueError(
|
|
f"Task '{task.get('label', task.get('name', '?'))}' has channels but no locales"
|
|
)
|
|
task["matrix"] = {
|
|
"substitution-fields": ["run.command"],
|
|
"channel": channels,
|
|
"locale": locales,
|
|
}
|
|
yield task
|
|
|
|
|
|
@transforms.add
|
|
def stash_distro_attributes(config, tasks):
|
|
for task in tasks:
|
|
method = task.pop("install-method", None)
|
|
distro_symbol = task.pop("distro-symbol", None)
|
|
|
|
if distro_symbol is not None:
|
|
attrs = task.setdefault("attributes", {})
|
|
attrs["distro-symbol"] = distro_symbol
|
|
attrs["install-method"] = method
|
|
|
|
yield task
|