taskcluster/taskgraph/transforms/diffoscope.py
author Mike Hommey <mh+mozilla@glandium.org>
Tue, 12 Mar 2019 20:21:14 +0000
changeset 521620 01712a69cb3960e602d452a39ed71396d60bfc80
parent 521619 fc4d0ac008223e2d800819892e660aaceb271db6
child 530845 cef374e9b2985d5ee4e79e9e3984e94a32d72dd1
permissions -rw-r--r--
Bug 1534500 - Allow diffoscope tasks to run commands before running the diff. r=dustin Depends on D23074 Differential Revision: https://phabricator.services.mozilla.com/D23075

# 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/.
"""
This transform construct tasks to perform diffs between builds, as
defined in kind.yml
"""

from __future__ import absolute_import, print_function, unicode_literals

from taskgraph.transforms.base import TransformSequence
from taskgraph.util.schema import (
    Schema,
)
from taskgraph.util.taskcluster import get_artifact_path
from voluptuous import (
    Any,
    Optional,
    Required,
)

index_or_string = Any(
    basestring,
    {Required('index-search'): basestring},
)

diff_description_schema = Schema({
    # Name of the diff task.
    Required('name'): basestring,

    # Treeherder symbol.
    Required('symbol'): basestring,

    # relative path (from config.path) to the file the task was defined in.
    Optional('job-from'): basestring,

    # Original and new builds to compare.
    Required('original'): index_or_string,
    Required('new'): index_or_string,

    # Arguments to pass to diffoscope, used for job-defaults in
    # taskcluster/ci/diffoscope/kind.yml
    Optional('args'): basestring,

    # Extra arguments to pass to diffoscope, that can be set per job.
    Optional('extra-args'): basestring,

    # Fail the task when differences are detected.
    Optional('fail-on-diff'): bool,

    # Whether to unpack first. Diffoscope can normally work without unpacking,
    # but when one needs to --exclude some contents, that doesn't work out well
    # if said content is packed (e.g. in omni.ja).
    Optional('unpack'): bool,

    # Commands to run before performing the diff.
    Optional('pre-diff-commands'): [basestring],
})

transforms = TransformSequence()
transforms.add_validate(diff_description_schema)


@transforms.add
def fill_template(config, tasks):
    dummy_tasks = {}

    for task in tasks:
        name = task['name']

        deps = {}
        urls = {}
        previous_artifact = None
        for k in ('original', 'new'):
            value = task[k]
            if isinstance(value, basestring):
                deps[k] = value
                dep_name = k
                os_hint = value
            else:
                index = value['index-search']
                if index not in dummy_tasks:
                    dummy_tasks[index] = {
                        'label': 'index-search-' + index,
                        'description': index,
                        'worker-type': 'invalid/always-optimized',
                        'run': {
                            'using': 'always-optimized',
                        },
                        'optimization': {
                            'index-search': [index],
                        }
                    }
                    yield dummy_tasks[index]
                deps[index] = 'index-search-' + index
                dep_name = index
                os_hint = index.split('.')[-1]
            if 'linux' in os_hint:
                artifact = 'target.tar.bz2'
            elif 'macosx' in os_hint:
                artifact = 'target.dmg'
            elif 'android' in os_hint:
                artifact = 'target.apk'
            elif 'win' in os_hint:
                artifact = 'target.zip'
            else:
                raise Exception(
                    'Cannot figure out the OS for {!r}'.format(value))
            if previous_artifact is not None and previous_artifact != artifact:
                raise Exception(
                    'Cannot compare builds from different OSes')
            urls[k] = {
                'artifact-reference': '<{}/{}>'.format(
                    dep_name, get_artifact_path(task, artifact)),
            }
            previous_artifact = artifact

        taskdesc = {
            'label': 'diff-' + name,
            'description': name,
            'treeherder': {
                'symbol': task['symbol'],
                'platform': 'diff/opt',
                'kind': 'other',
                'tier': 2,
            },
            'worker-type': 'aws-provisioner-v1/gecko-{}-b-linux'.format(
               config.params['level']),
            'worker': {
                'docker-image': {'in-tree': 'diffoscope'},
                'artifacts': [{
                    'type': 'file',
                    'path': '/builds/worker/diff.html',
                    'name': 'public/diff.html',
                }, {
                    'type': 'file',
                    'path': '/builds/worker/diff.txt',
                    'name': 'public/diff.txt',
                }],
                'env': {
                    'ORIG_URL': urls['original'],
                    'NEW_URL': urls['new'],
                    'DIFFOSCOPE_ARGS': ' '.join(
                        task[k] for k in ('args', 'extra-args') if k in task),
                    'PRE_DIFF': '; '.join(task.get('pre-diff-commands', [])),
                },
                'max-run-time': 1800,
            },
            'run': {
                'using': 'run-task',
                'checkout': task.get('unpack', False),
                'command': '/builds/worker/bin/get_and_diffoscope{}{}'.format(
                    ' --unpack' if task.get('unpack') else '',
                    ' --fail' if task.get('fail-on-diff') else '',
                ),
            },
            'dependencies': deps,
        }

        if artifact.endswith('.dmg'):
            taskdesc['toolchains'] = [
                'linux64-cctools-port',
                'linux64-libdmg',
            ]

        yield taskdesc