hg.py (4304B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 6 import logging 7 import subprocess 8 9 import requests 10 from mozbuild.util import memoize 11 from redo import retry 12 13 logger = logging.getLogger(__name__) 14 15 PUSHLOG_CHANGESET_TMPL = ( 16 "{repository}/json-pushes?version=2&changeset={revision}&tipsonly=1" 17 ) 18 PUSHLOG_PUSHES_TMPL = ( 19 "{repository}/json-pushes/?version=2&startID={push_id_start}&endID={push_id_end}" 20 ) 21 22 23 def _query_pushlog(url): 24 response = retry( 25 requests.get, 26 attempts=5, 27 sleeptime=10, 28 args=(url,), 29 kwargs={"timeout": 60, "headers": {"User-Agent": "TaskCluster"}}, 30 ) 31 32 return response.json()["pushes"] 33 34 35 def find_hg_revision_push_info(repository, revision): 36 """Given the parameters for this action and a revision, find the 37 pushlog_id of the revision.""" 38 url = PUSHLOG_CHANGESET_TMPL.format(repository=repository, revision=revision) 39 40 pushes = _query_pushlog(url) 41 42 if len(pushes) != 1: 43 raise RuntimeError( 44 f"Found {len(pushes)} pushlog_ids, expected 1, for {repository} revision {revision}: {pushes}" 45 ) 46 47 pushid = list(pushes.keys())[0] 48 return { 49 "pushdate": pushes[pushid]["date"], 50 "pushid": pushid, 51 "user": pushes[pushid]["user"], 52 } 53 54 55 @memoize 56 def get_push_data(repository, project, push_id_start, push_id_end): 57 url = PUSHLOG_PUSHES_TMPL.format( 58 repository=repository, 59 push_id_start=push_id_start - 1, 60 push_id_end=push_id_end, 61 ) 62 63 try: 64 pushes = _query_pushlog(url) 65 66 return { 67 push_id: pushes[str(push_id)] 68 for push_id in range(push_id_start, push_id_end + 1) 69 } 70 71 # In the event of request times out, requests will raise a TimeoutError. 72 except requests.exceptions.Timeout: 73 logger.warning("json-pushes timeout") 74 75 # In the event of a network problem (e.g. DNS failure, refused connection, etc), 76 # requests will raise a ConnectionError. 77 except requests.exceptions.ConnectionError: 78 logger.warning("json-pushes connection error") 79 80 # In the event of the rare invalid HTTP response(e.g 404, 401), 81 # requests will raise an HTTPError exception 82 except requests.exceptions.HTTPError: 83 logger.warning("Bad Http response") 84 85 # When we get invalid JSON (i.e. 500 error), it results in a ValueError (bug 1313426) 86 except ValueError as error: 87 logger.warning(f"Invalid JSON, possible server error: {error}") 88 89 # We just print the error out as a debug message if we failed to catch the exception above 90 except requests.exceptions.RequestException as error: 91 logger.warning(error) 92 93 return None 94 95 96 @memoize 97 def get_json_pushchangedfiles(repository, revision): 98 url = "{}/json-pushchangedfiles/{}".format(repository.rstrip("/"), revision) 99 logger.debug("Querying version control for metadata: %s", url) 100 101 def get_pushchangedfiles(): 102 response = requests.get(url, timeout=60) 103 response.raise_for_status() 104 return response.json() 105 106 return retry(get_pushchangedfiles, attempts=10, sleeptime=10) 107 108 109 def get_hg_revision_branch(root, revision): 110 """Given the parameters for a revision, find the hg_branch (aka 111 relbranch) of the revision.""" 112 return get_hg_revision_info(root, revision, "branch") 113 114 115 def get_hg_revision_info(root, revision, info): 116 return subprocess.check_output( 117 [ 118 "hg", 119 "identify", 120 "-T", 121 f"{{{info}}}", 122 "--rev", 123 revision, 124 ], 125 cwd=root, 126 universal_newlines=True, 127 ) 128 129 130 # For these functions, we assume that run-task has correctly checked out the 131 # revision indicated by GECKO_HEAD_REF, so all that remains is to see what the 132 # current revision is. Mercurial refers to that as `.`. 133 def get_hg_commit_message(root, rev="."): 134 return subprocess.check_output( 135 ["hg", "log", "-r", rev, "-T", "{desc}"], cwd=root, universal_newlines=True 136 ) 137 138 139 def calculate_head_rev(root): 140 return subprocess.check_output( 141 ["hg", "log", "-r", ".", "-T", "{node}"], cwd=root, universal_newlines=True 142 )