diff options
Diffstat (limited to 'taskcluster/gecko_taskgraph/util/hg.py')
-rw-r--r-- | taskcluster/gecko_taskgraph/util/hg.py | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/taskcluster/gecko_taskgraph/util/hg.py b/taskcluster/gecko_taskgraph/util/hg.py new file mode 100644 index 0000000000..18a92fbd0d --- /dev/null +++ b/taskcluster/gecko_taskgraph/util/hg.py @@ -0,0 +1,139 @@ +# 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/. + + +import logging +import subprocess + +import requests +from mozbuild.util import memoize +from redo import retry + +logger = logging.getLogger(__name__) + +PUSHLOG_CHANGESET_TMPL = ( + "{repository}/json-pushes?version=2&changeset={revision}&tipsonly=1" +) +PUSHLOG_PUSHES_TMPL = ( + "{repository}/json-pushes/?version=2&startID={push_id_start}&endID={push_id_end}" +) + + +def _query_pushlog(url): + response = retry( + requests.get, + attempts=5, + sleeptime=10, + args=(url,), + kwargs={"timeout": 60, "headers": {"User-Agent": "TaskCluster"}}, + ) + + return response.json()["pushes"] + + +def find_hg_revision_push_info(repository, revision): + """Given the parameters for this action and a revision, find the + pushlog_id of the revision.""" + url = PUSHLOG_CHANGESET_TMPL.format(repository=repository, revision=revision) + + pushes = _query_pushlog(url) + + if len(pushes) != 1: + raise RuntimeError( + "Found {} pushlog_ids, expected 1, for {} revision {}: {}".format( + len(pushes), repository, revision, pushes + ) + ) + + pushid = list(pushes.keys())[0] + return { + "pushdate": pushes[pushid]["date"], + "pushid": pushid, + "user": pushes[pushid]["user"], + } + + +@memoize +def get_push_data(repository, project, push_id_start, push_id_end): + url = PUSHLOG_PUSHES_TMPL.format( + repository=repository, + push_id_start=push_id_start - 1, + push_id_end=push_id_end, + ) + + try: + pushes = _query_pushlog(url) + + return { + push_id: pushes[str(push_id)] + for push_id in range(push_id_start, push_id_end + 1) + } + + # In the event of request times out, requests will raise a TimeoutError. + except requests.exceptions.Timeout: + logger.warning("json-pushes timeout") + + # In the event of a network problem (e.g. DNS failure, refused connection, etc), + # requests will raise a ConnectionError. + except requests.exceptions.ConnectionError: + logger.warning("json-pushes connection error") + + # In the event of the rare invalid HTTP response(e.g 404, 401), + # requests will raise an HTTPError exception + except requests.exceptions.HTTPError: + logger.warning("Bad Http response") + + # When we get invalid JSON (i.e. 500 error), it results in a ValueError (bug 1313426) + except ValueError as error: + logger.warning(f"Invalid JSON, possible server error: {error}") + + # We just print the error out as a debug message if we failed to catch the exception above + except requests.exceptions.RequestException as error: + logger.warning(error) + + return None + + +@memoize +def get_json_automationrelevance(repository, revision): + url = "{}/json-automationrelevance/{}".format(repository.rstrip("/"), revision) + logger.debug("Querying version control for metadata: %s", url) + + def get_automationrelevance(): + response = requests.get(url, timeout=30) + return response.json() + + return retry(get_automationrelevance, attempts=10, sleeptime=10) + + +def get_hg_revision_branch(root, revision): + """Given the parameters for a revision, find the hg_branch (aka + relbranch) of the revision.""" + return subprocess.check_output( + [ + "hg", + "identify", + "-T", + "{branch}", + "--rev", + revision, + ], + cwd=root, + universal_newlines=True, + ) + + +# For these functions, we assume that run-task has correctly checked out the +# revision indicated by GECKO_HEAD_REF, so all that remains is to see what the +# current revision is. Mercurial refers to that as `.`. +def get_hg_commit_message(root, rev="."): + return subprocess.check_output( + ["hg", "log", "-r", rev, "-T", "{desc}"], cwd=root, universal_newlines=True + ) + + +def calculate_head_rev(root): + return subprocess.check_output( + ["hg", "log", "-r", ".", "-T", "{node}"], cwd=root, universal_newlines=True + ) |