mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-03 17:58:55 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			316 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			316 lines
		
	
	
	
		
			11 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/.
 | 
						|
"""
 | 
						|
Transform the repackage task into an actual task description.
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
import copy
 | 
						|
 | 
						|
from taskgraph.transforms.base import TransformSequence
 | 
						|
from taskgraph.util.dependencies import get_primary_dependency
 | 
						|
from taskgraph.util.schema import Schema, optionally_keyed_by, resolve_keyed_by
 | 
						|
from taskgraph.util.taskcluster import get_artifact_prefix
 | 
						|
from voluptuous import Optional, Required
 | 
						|
 | 
						|
from gecko_taskgraph.transforms.repackage import (
 | 
						|
    PACKAGE_FORMATS as PACKAGE_FORMATS_VANILLA,
 | 
						|
)
 | 
						|
from gecko_taskgraph.transforms.task import task_description_schema
 | 
						|
from gecko_taskgraph.util.attributes import copy_attributes_from_dependent_job
 | 
						|
from gecko_taskgraph.util.partners import get_partner_config_by_kind
 | 
						|
from gecko_taskgraph.util.platforms import archive_format, executable_extension
 | 
						|
from gecko_taskgraph.util.workertypes import worker_type_implementation
 | 
						|
 | 
						|
# When repacking the stub installer we need to pass a zip file and package name to the
 | 
						|
# repackage task. This is not needed for vanilla stub but analogous to the full installer.
 | 
						|
PACKAGE_FORMATS = copy.deepcopy(PACKAGE_FORMATS_VANILLA)
 | 
						|
PACKAGE_FORMATS["installer-stub"]["inputs"]["package"] = "target-stub{archive_format}"
 | 
						|
PACKAGE_FORMATS["installer-stub"]["args"].extend(["--package-name", "{package-name}"])
 | 
						|
 | 
						|
packaging_description_schema = Schema(
 | 
						|
    {
 | 
						|
        # unique label to describe this repackaging task
 | 
						|
        Optional("label"): str,
 | 
						|
        # Routes specific to this task, if defined
 | 
						|
        Optional("routes"): [str],
 | 
						|
        # passed through directly to the job description
 | 
						|
        Optional("extra"): task_description_schema["extra"],
 | 
						|
        # Shipping product and phase
 | 
						|
        Optional("shipping-product"): task_description_schema["shipping-product"],
 | 
						|
        Optional("shipping-phase"): task_description_schema["shipping-phase"],
 | 
						|
        Required("package-formats"): optionally_keyed_by(
 | 
						|
            "build-platform", "build-type", [str]
 | 
						|
        ),
 | 
						|
        # All l10n jobs use mozharness
 | 
						|
        Required("mozharness"): {
 | 
						|
            # Config files passed to the mozharness script
 | 
						|
            Required("config"): optionally_keyed_by("build-platform", [str]),
 | 
						|
            # Additional paths to look for mozharness configs in. These should be
 | 
						|
            # relative to the base of the source checkout
 | 
						|
            Optional("config-paths"): [str],
 | 
						|
            # if true, perform a checkout of a comm-central based branch inside the
 | 
						|
            # gecko checkout
 | 
						|
            Optional("comm-checkout"): bool,
 | 
						|
        },
 | 
						|
        # Override the default priority for the project
 | 
						|
        Optional("priority"): task_description_schema["priority"],
 | 
						|
        Optional("task-from"): task_description_schema["task-from"],
 | 
						|
        Optional("attributes"): task_description_schema["attributes"],
 | 
						|
        Optional("dependencies"): task_description_schema["dependencies"],
 | 
						|
    }
 | 
						|
)
 | 
						|
 | 
						|
transforms = TransformSequence()
 | 
						|
 | 
						|
 | 
						|
@transforms.add
 | 
						|
def remove_name(config, jobs):
 | 
						|
    for job in jobs:
 | 
						|
        if "name" in job:
 | 
						|
            del job["name"]
 | 
						|
        yield job
 | 
						|
 | 
						|
 | 
						|
transforms.add_validate(packaging_description_schema)
 | 
						|
 | 
						|
 | 
						|
@transforms.add
 | 
						|
def copy_in_useful_magic(config, jobs):
 | 
						|
    """Copy attributes from upstream task to be used for keyed configuration."""
 | 
						|
    for job in jobs:
 | 
						|
        dep = get_primary_dependency(config, job)
 | 
						|
        assert dep
 | 
						|
 | 
						|
        job["build-platform"] = dep.attributes.get("build_platform")
 | 
						|
        yield job
 | 
						|
 | 
						|
 | 
						|
@transforms.add
 | 
						|
def handle_keyed_by(config, jobs):
 | 
						|
    """Resolve fields that can be keyed by platform, etc."""
 | 
						|
    fields = [
 | 
						|
        "mozharness.config",
 | 
						|
        "package-formats",
 | 
						|
    ]
 | 
						|
    for job in jobs:
 | 
						|
        job = copy.deepcopy(job)  # don't overwrite dict values here
 | 
						|
        for field in fields:
 | 
						|
            resolve_keyed_by(item=job, field=field, item_name="?")
 | 
						|
        yield job
 | 
						|
 | 
						|
 | 
						|
@transforms.add
 | 
						|
def make_repackage_description(config, jobs):
 | 
						|
    for job in jobs:
 | 
						|
        dep_job = get_primary_dependency(config, job)
 | 
						|
        assert dep_job
 | 
						|
 | 
						|
        label = job.get("label", dep_job.label.replace("signing-", "repackage-"))
 | 
						|
        job["label"] = label
 | 
						|
 | 
						|
        yield job
 | 
						|
 | 
						|
 | 
						|
@transforms.add
 | 
						|
def make_job_description(config, jobs):
 | 
						|
    for job in jobs:
 | 
						|
        dep_job = get_primary_dependency(config, job)
 | 
						|
        assert dep_job
 | 
						|
 | 
						|
        attributes = copy_attributes_from_dependent_job(dep_job)
 | 
						|
        build_platform = attributes["build_platform"]
 | 
						|
 | 
						|
        if job["build-platform"].startswith("win"):
 | 
						|
            if dep_job.kind.endswith("signing"):
 | 
						|
                continue
 | 
						|
        if job["build-platform"].startswith("macosx"):
 | 
						|
            if dep_job.kind.endswith("repack"):
 | 
						|
                continue
 | 
						|
        dependencies = {dep_job.attributes.get("kind"): dep_job.label}
 | 
						|
        dependencies.update(dep_job.dependencies)
 | 
						|
 | 
						|
        signing_task = None
 | 
						|
        for dependency in dependencies.keys():
 | 
						|
            if build_platform.startswith("macosx") and dependency.endswith("signing"):
 | 
						|
                signing_task = dependency
 | 
						|
            elif build_platform.startswith("win") and dependency.endswith("repack"):
 | 
						|
                signing_task = dependency
 | 
						|
 | 
						|
        attributes["repackage_type"] = "repackage"
 | 
						|
 | 
						|
        repack_id = job["extra"]["repack_id"]
 | 
						|
 | 
						|
        partner_config = get_partner_config_by_kind(config, config.kind)
 | 
						|
        partner, subpartner, _ = repack_id.split("/")
 | 
						|
        repack_stub_installer = partner_config[partner][subpartner].get(
 | 
						|
            "repack_stub_installer"
 | 
						|
        )
 | 
						|
        if build_platform.startswith("win32") and repack_stub_installer:
 | 
						|
            job["package-formats"].append("installer-stub")
 | 
						|
 | 
						|
        repackage_config = []
 | 
						|
        for format in job.get("package-formats"):
 | 
						|
            command = copy.deepcopy(PACKAGE_FORMATS[format])
 | 
						|
            substs = {
 | 
						|
                "archive_format": archive_format(build_platform),
 | 
						|
                "executable_extension": executable_extension(build_platform),
 | 
						|
            }
 | 
						|
            command["inputs"] = {
 | 
						|
                name: filename.format(**substs)
 | 
						|
                for name, filename in command["inputs"].items()
 | 
						|
            }
 | 
						|
            repackage_config.append(command)
 | 
						|
 | 
						|
        run = job.get("mozharness", {})
 | 
						|
        run.update(
 | 
						|
            {
 | 
						|
                "using": "mozharness",
 | 
						|
                "script": "mozharness/scripts/repackage.py",
 | 
						|
                "job-script": "taskcluster/scripts/builder/repackage.sh",
 | 
						|
                "actions": ["setup", "repackage"],
 | 
						|
                "extra-config": {
 | 
						|
                    "repackage_config": repackage_config,
 | 
						|
                },
 | 
						|
            }
 | 
						|
        )
 | 
						|
 | 
						|
        worker = {
 | 
						|
            "chain-of-trust": True,
 | 
						|
            "max-run-time": 3600,
 | 
						|
            "taskcluster-proxy": True if get_artifact_prefix(dep_job) else False,
 | 
						|
            "env": {
 | 
						|
                "REPACK_ID": repack_id,
 | 
						|
            },
 | 
						|
            # Don't add generic artifact directory.
 | 
						|
            "skip-artifacts": True,
 | 
						|
        }
 | 
						|
 | 
						|
        worker_type = "b-linux-gcp"
 | 
						|
 | 
						|
        worker["artifacts"] = _generate_task_output_files(
 | 
						|
            dep_job,
 | 
						|
            worker_type_implementation(config.graph_config, config.params, worker_type),
 | 
						|
            repackage_config,
 | 
						|
            partner=repack_id,
 | 
						|
        )
 | 
						|
 | 
						|
        description = (
 | 
						|
            "Repackaging for repack_id '{repack_id}' for build '"
 | 
						|
            "{build_platform}/{build_type}'".format(
 | 
						|
                repack_id=job["extra"]["repack_id"],
 | 
						|
                build_platform=attributes.get("build_platform"),
 | 
						|
                build_type=attributes.get("build_type"),
 | 
						|
            )
 | 
						|
        )
 | 
						|
 | 
						|
        task = {
 | 
						|
            "label": job["label"],
 | 
						|
            "description": description,
 | 
						|
            "worker-type": worker_type,
 | 
						|
            "dependencies": dependencies,
 | 
						|
            "attributes": attributes,
 | 
						|
            "scopes": ["queue:get-artifact:releng/partner/*"],
 | 
						|
            "run-on-projects": dep_job.attributes.get("run_on_projects"),
 | 
						|
            "routes": job.get("routes", []),
 | 
						|
            "extra": job.get("extra", {}),
 | 
						|
            "worker": worker,
 | 
						|
            "run": run,
 | 
						|
            "fetches": _generate_download_config(
 | 
						|
                dep_job,
 | 
						|
                build_platform,
 | 
						|
                signing_task,
 | 
						|
                partner=repack_id,
 | 
						|
                project=config.params["project"],
 | 
						|
                repack_stub_installer=repack_stub_installer,
 | 
						|
            ),
 | 
						|
        }
 | 
						|
 | 
						|
        # we may have reduced the priority for partner jobs, otherwise task.py will set it
 | 
						|
        if job.get("priority"):
 | 
						|
            task["priority"] = job["priority"]
 | 
						|
        if build_platform.startswith("macosx"):
 | 
						|
            task.setdefault("fetches", {}).setdefault("toolchain", []).extend(
 | 
						|
                [
 | 
						|
                    "linux64-libdmg",
 | 
						|
                    "linux64-hfsplus",
 | 
						|
                    "linux64-node",
 | 
						|
                ]
 | 
						|
            )
 | 
						|
        yield task
 | 
						|
 | 
						|
 | 
						|
def _generate_download_config(
 | 
						|
    task,
 | 
						|
    build_platform,
 | 
						|
    signing_task,
 | 
						|
    partner=None,
 | 
						|
    project=None,
 | 
						|
    repack_stub_installer=False,
 | 
						|
):
 | 
						|
    locale_path = f"{partner}/" if partner else ""
 | 
						|
 | 
						|
    if build_platform.startswith("macosx"):
 | 
						|
        return {
 | 
						|
            signing_task: [
 | 
						|
                {
 | 
						|
                    "artifact": f"{locale_path}target.tar.gz",
 | 
						|
                    "extract": False,
 | 
						|
                },
 | 
						|
            ],
 | 
						|
        }
 | 
						|
    if build_platform.startswith("win"):
 | 
						|
        download_config = [
 | 
						|
            {
 | 
						|
                "artifact": f"{locale_path}target.zip",
 | 
						|
                "extract": False,
 | 
						|
            },
 | 
						|
            f"{locale_path}setup.exe",
 | 
						|
        ]
 | 
						|
        if build_platform.startswith("win32") and repack_stub_installer:
 | 
						|
            download_config.extend(
 | 
						|
                [
 | 
						|
                    {
 | 
						|
                        "artifact": f"{locale_path}target-stub.zip",
 | 
						|
                        "extract": False,
 | 
						|
                    },
 | 
						|
                    f"{locale_path}setup-stub.exe",
 | 
						|
                ]
 | 
						|
            )
 | 
						|
        return {signing_task: download_config}
 | 
						|
 | 
						|
    raise NotImplementedError(f'Unsupported build_platform: "{build_platform}"')
 | 
						|
 | 
						|
 | 
						|
def _generate_task_output_files(task, worker_implementation, repackage_config, partner):
 | 
						|
    """We carefully generate an explicit list here, but there's an artifacts directory
 | 
						|
    too, courtesy of generic_worker_add_artifacts() (windows) or docker_worker_add_artifacts().
 | 
						|
    Any errors here are likely masked by that.
 | 
						|
    """
 | 
						|
    partner_output_path = f"{partner}/"
 | 
						|
    artifact_prefix = get_artifact_prefix(task)
 | 
						|
 | 
						|
    if worker_implementation == ("docker-worker", "linux"):
 | 
						|
        local_prefix = "/builds/worker/workspace/"
 | 
						|
    elif worker_implementation == ("generic-worker", "windows"):
 | 
						|
        local_prefix = "workspace/"
 | 
						|
    else:
 | 
						|
        raise NotImplementedError(
 | 
						|
            f'Unsupported worker implementation: "{worker_implementation}"'
 | 
						|
        )
 | 
						|
 | 
						|
    output_files = []
 | 
						|
    for config in repackage_config:
 | 
						|
        output_files.append(
 | 
						|
            {
 | 
						|
                "type": "file",
 | 
						|
                "path": "{}outputs/{}{}".format(
 | 
						|
                    local_prefix, partner_output_path, config["output"]
 | 
						|
                ),
 | 
						|
                "name": "{}/{}{}".format(
 | 
						|
                    artifact_prefix, partner_output_path, config["output"]
 | 
						|
                ),
 | 
						|
            }
 | 
						|
        )
 | 
						|
    return output_files
 |