tools/tryselect/push.py
author Tudor-Gabriel Vîjială <tvijiala@mozilla.com>
Mon, 06 Aug 2018 10:44:29 +0100
changeset 431392 5312e71473c402f520db9e24f5c7e5eb6795cbcf
parent 426739 8c423f75eb7723a68179dfe3ba1602b5c4c637d9
child 431691 138014f66617183f27d3f5da44acf5bcaa6f1450
permissions -rw-r--r--
Bug 1480187 - Fix mach try again when using multiple source checkouts. r=ahal MozReview-Commit-ID: AV7ynmTSY1x

# 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 __future__ import absolute_import, print_function

import hashlib
import json
import os
import sys

from mozboot.util import get_state_dir
from mozbuild.base import MozbuildObject
from mozversioncontrol import get_repository_object, MissingVCSExtension

GIT_CINNABAR_NOT_FOUND = """
Could not detect `git-cinnabar`.

The `mach try` command requires git-cinnabar to be installed when
pushing from git. For more information and installation instruction,
please see:

    https://github.com/glandium/git-cinnabar
""".lstrip()

HG_PUSH_TO_TRY_NOT_FOUND = """
Could not detect `push-to-try`.

The `mach try` command requires the push-to-try extension enabled
when pushing from hg. Please install it by running:

    $ ./mach mercurial-setup
""".lstrip()

VCS_NOT_FOUND = """
Could not detect version control. Only `hg` or `git` are supported.
""".strip()

UNCOMMITTED_CHANGES = """
ERROR please commit changes before continuing
""".strip()

MAX_HISTORY = 10

here = os.path.abspath(os.path.dirname(__file__))
build = MozbuildObject.from_environment(cwd=here)
vcs = get_repository_object(build.topsrcdir)
topsrcdir_hash = hashlib.sha256(os.path.abspath(build.topsrcdir)).hexdigest()
history_path = os.path.join(get_state_dir()[0], 'history', topsrcdir_hash, 'try_task_configs.json')
old_history_path = os.path.join(get_state_dir()[0], 'history', 'try_task_configs.json')


def write_task_config(try_task_config):
    config_path = os.path.join(vcs.path, 'try_task_config.json')
    with open(config_path, 'w') as fh:
        json.dump(try_task_config, fh, indent=2, separators=(',', ':'))
        fh.write('\n')
    return config_path


def write_task_config_history(msg, try_task_config):
    if not os.path.isfile(history_path):
        if not os.path.isdir(os.path.dirname(history_path)):
            os.makedirs(os.path.dirname(history_path))
        history = []
    else:
        with open(history_path, 'r') as fh:
            history = fh.read().strip().splitlines()

    history.insert(0, json.dumps([msg, try_task_config]))
    history = history[:MAX_HISTORY]
    with open(history_path, 'w') as fh:
        fh.write('\n'.join(history))


def check_working_directory(push=True):
    if not push:
        return

    if not vcs.working_directory_clean():
        print(UNCOMMITTED_CHANGES)
        sys.exit(1)


def push_to_try(method, msg, labels=None, templates=None, try_task_config=None,
                push=True, closed_tree=False):
    check_working_directory(push)

    # Format the commit message
    closed_tree_string = " ON A CLOSED TREE" if closed_tree else ""
    commit_message = ('%s%s\n\nPushed via `mach try %s`' %
                      (msg, closed_tree_string, method))

    if labels or labels == []:
        try_task_config = {'tasks': sorted(labels)}
        if templates:
            try_task_config['templates'] = templates
        if push:
            write_task_config_history(msg, try_task_config)

    config = None
    if try_task_config:
        config = write_task_config(try_task_config)

    try:
        if not push:
            print("Commit message:")
            print(commit_message)
            if config:
                print("Calculated try_task_config.json:")
                with open(config) as fh:
                    print(fh.read())
            return

        if config:
            vcs.add_remove_files(config)

        try:
            vcs.push_to_try(commit_message)
        except MissingVCSExtension as e:
            if e.ext == 'push-to-try':
                print(HG_PUSH_TO_TRY_NOT_FOUND)
            elif e.ext == 'cinnabar':
                print(GIT_CINNABAR_NOT_FOUND)
            else:
                raise
            sys.exit(1)
    finally:
        if config and os.path.isfile(config):
            os.remove(config)