blob: 054f5c640c6afad1175b80c3ceb721572344d2ef [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Chirayu Desai217ea7d2013-03-01 19:14:38 +053018
Dan Willemsen0745bb22015-08-17 13:41:45 -070019import contextlib
20import errno
Anthony King85b24ac2014-05-06 15:57:48 +010021import json
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
23import re
Łukasz Gardońbed59ce2017-08-08 10:18:11 +020024import ssl
Shawn O. Pearcefb231612009-04-10 18:53:46 -070025import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026import sys
Doug Anderson0048b692010-12-21 13:39:23 -080027try:
28 import threading as _threading
29except ImportError:
30 import dummy_threading as _threading
Shawn O. Pearcefb231612009-04-10 18:53:46 -070031import time
David Pursehouse59bbb582013-05-17 10:49:33 +090032
33from pyversion import is_python3
34if is_python3():
Sarah Owens1f7627f2012-10-31 09:21:55 -070035 import urllib.request
36 import urllib.error
37else:
David Pursehouse59bbb582013-05-17 10:49:33 +090038 import urllib2
Sarah Owens1f7627f2012-10-31 09:21:55 -070039 import imp
40 urllib = imp.new_module('urllib')
41 urllib.request = urllib2
42 urllib.error = urllib2
Shawn O. Pearcef00e0ce2009-08-22 18:39:49 -070043
Shawn O. Pearcefb231612009-04-10 18:53:46 -070044from signal import SIGTERM
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080045from error import GitError, UploadError
Renaud Paquay010fed72016-11-11 14:25:29 -080046import platform_utils
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040047from repo_trace import Trace
David Pursehouseecf8f2b2013-05-24 12:12:23 +090048if is_python3():
49 from http.client import HTTPException
50else:
51 from httplib import HTTPException
Shawn O. Pearceca8c32c2010-05-11 18:21:33 -070052
53from git_command import GitCommand
54from git_command import ssh_sock
55from git_command import terminate_ssh_clients
Zac Livingston9ead97b2017-06-13 08:29:04 -060056from git_refs import R_CHANGES, R_HEADS, R_TAGS
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070057
David Pursehouse1d947b32012-10-25 12:23:11 +090058ID_RE = re.compile(r'^[0-9a-f]{40}$')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070059
Shawn O. Pearce146fe902009-03-25 14:06:43 -070060REVIEW_CACHE = dict()
61
David Pursehouse819827a2020-02-12 15:20:19 +090062
Zac Livingston9ead97b2017-06-13 08:29:04 -060063def IsChange(rev):
64 return rev.startswith(R_CHANGES)
65
David Pursehouse819827a2020-02-12 15:20:19 +090066
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070067def IsId(rev):
68 return ID_RE.match(rev)
69
David Pursehouse819827a2020-02-12 15:20:19 +090070
Zac Livingston9ead97b2017-06-13 08:29:04 -060071def IsTag(rev):
72 return rev.startswith(R_TAGS)
73
David Pursehouse819827a2020-02-12 15:20:19 +090074
Zac Livingston9ead97b2017-06-13 08:29:04 -060075def IsImmutable(rev):
76 return IsChange(rev) or IsId(rev) or IsTag(rev)
77
David Pursehouse819827a2020-02-12 15:20:19 +090078
Shawn O. Pearcef8e32732009-04-17 11:00:31 -070079def _key(name):
80 parts = name.split('.')
81 if len(parts) < 2:
82 return name.lower()
David Pursehouse54a4e602020-02-12 14:31:05 +090083 parts[0] = parts[0].lower()
Shawn O. Pearcef8e32732009-04-17 11:00:31 -070084 parts[-1] = parts[-1].lower()
85 return '.'.join(parts)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070086
David Pursehouse819827a2020-02-12 15:20:19 +090087
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070088class GitConfig(object):
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070089 _ForUser = None
90
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070091 @classmethod
92 def ForUser(cls):
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070093 if cls._ForUser is None:
David Pursehousee5913ae2020-02-12 13:56:59 +090094 cls._ForUser = cls(configfile=os.path.expanduser('~/.gitconfig'))
Shawn O. Pearce90be5c02008-10-29 15:21:24 -070095 return cls._ForUser
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070096
97 @classmethod
98 def ForRepository(cls, gitdir, defaults=None):
David Pursehousee5913ae2020-02-12 13:56:59 +090099 return cls(configfile=os.path.join(gitdir, 'config'),
100 defaults=defaults)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700101
Anthony King85b24ac2014-05-06 15:57:48 +0100102 def __init__(self, configfile, defaults=None, jsonFile=None):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900103 self.file = configfile
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700104 self.defaults = defaults
105 self._cache_dict = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700106 self._section_dict = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700107 self._remotes = {}
108 self._branches = {}
Shawn O. Pearce1b34c912009-05-21 18:52:49 -0700109
Anthony King85b24ac2014-05-06 15:57:48 +0100110 self._json = jsonFile
111 if self._json is None:
112 self._json = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900113 os.path.dirname(self.file),
114 '.repo_' + os.path.basename(self.file) + '.json')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700115
David Pursehousee5913ae2020-02-12 13:56:59 +0900116 def Has(self, name, include_defaults=True):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700117 """Return true if this configuration file has the key.
118 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700119 if _key(name) in self._cache:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700120 return True
121 if include_defaults and self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900122 return self.defaults.Has(name, include_defaults=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700123 return False
124
125 def GetBoolean(self, name):
126 """Returns a boolean from the configuration file.
127 None : The value was not defined, or is not a boolean.
128 True : The value was set to true or yes.
129 False: The value was set to false or no.
130 """
131 v = self.GetString(name)
132 if v is None:
133 return None
134 v = v.lower()
135 if v in ('true', 'yes'):
136 return True
137 if v in ('false', 'no'):
138 return False
139 return None
140
David Pursehouse8a68ff92012-09-24 12:15:13 +0900141 def GetString(self, name, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700142 """Get the first value for a key, or None if it is not defined.
143
144 This configuration file is used first, if the key is not
David Pursehouse8a68ff92012-09-24 12:15:13 +0900145 defined or all_keys = True then the defaults are also searched.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700146 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700147 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700148 v = self._cache[_key(name)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700149 except KeyError:
150 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900151 return self.defaults.GetString(name, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700152 v = []
153
David Pursehouse8a68ff92012-09-24 12:15:13 +0900154 if not all_keys:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700155 if v:
156 return v[0]
157 return None
158
159 r = []
160 r.extend(v)
161 if self.defaults:
David Pursehousee5913ae2020-02-12 13:56:59 +0900162 r.extend(self.defaults.GetString(name, all_keys=True))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700163 return r
164
165 def SetString(self, name, value):
166 """Set the value(s) for a key.
167 Only this configuration file is modified.
168
169 The supplied value should be either a string,
170 or a list of strings (to store multiple values).
171 """
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700172 key = _key(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700173
174 try:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700175 old = self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176 except KeyError:
177 old = []
178
179 if value is None:
180 if old:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700181 del self._cache[key]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700182 self._do('--unset-all', name)
183
184 elif isinstance(value, list):
185 if len(value) == 0:
186 self.SetString(name, None)
187
188 elif len(value) == 1:
189 self.SetString(name, value[0])
190
191 elif old != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700192 self._cache[key] = list(value)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700193 self._do('--replace-all', name, value[0])
Sarah Owensa6053d52012-11-01 13:36:50 -0700194 for i in range(1, len(value)):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700195 self._do('--add', name, value[i])
196
197 elif len(old) != 1 or old[0] != value:
Shawn O. Pearcef8e32732009-04-17 11:00:31 -0700198 self._cache[key] = [value]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700199 self._do('--replace-all', name, value)
200
201 def GetRemote(self, name):
202 """Get the remote.$name.* configuration values as an object.
203 """
204 try:
205 r = self._remotes[name]
206 except KeyError:
207 r = Remote(self, name)
208 self._remotes[r.name] = r
209 return r
210
211 def GetBranch(self, name):
212 """Get the branch.$name.* configuration values as an object.
213 """
214 try:
215 b = self._branches[name]
216 except KeyError:
217 b = Branch(self, name)
218 self._branches[b.name] = b
219 return b
220
Shawn O. Pearce366ad212009-05-19 12:47:37 -0700221 def GetSubSections(self, section):
222 """List all subsection names matching $section.*.*
223 """
224 return self._sections.get(section, set())
225
David Pursehousee5913ae2020-02-12 13:56:59 +0900226 def HasSection(self, section, subsection=''):
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700227 """Does at least one key in section.subsection exist?
228 """
229 try:
230 return subsection in self._sections[section]
231 except KeyError:
232 return False
233
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700234 def UrlInsteadOf(self, url):
235 """Resolve any url.*.insteadof references.
236 """
237 for new_url in self.GetSubSections('url'):
Dan Willemsen4e4d40f2013-10-28 22:28:42 -0700238 for old_url in self.GetString('url.%s.insteadof' % new_url, True):
239 if old_url is not None and url.startswith(old_url):
240 return new_url + url[len(old_url):]
Shawn O. Pearce13111b42011-09-19 11:00:31 -0700241 return url
242
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700243 @property
244 def _sections(self):
245 d = self._section_dict
246 if d is None:
247 d = {}
248 for name in self._cache.keys():
249 p = name.split('.')
250 if 2 == len(p):
251 section = p[0]
252 subsect = ''
253 else:
254 section = p[0]
255 subsect = '.'.join(p[1:-1])
256 if section not in d:
257 d[section] = set()
258 d[section].add(subsect)
259 self._section_dict = d
260 return d
261
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700262 @property
263 def _cache(self):
264 if self._cache_dict is None:
265 self._cache_dict = self._Read()
266 return self._cache_dict
267
268 def _Read(self):
Anthony King85b24ac2014-05-06 15:57:48 +0100269 d = self._ReadJson()
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700270 if d is None:
271 d = self._ReadGit()
Anthony King85b24ac2014-05-06 15:57:48 +0100272 self._SaveJson(d)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700273 return d
274
Anthony King85b24ac2014-05-06 15:57:48 +0100275 def _ReadJson(self):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700276 try:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900277 if os.path.getmtime(self._json) <= os.path.getmtime(self.file):
Renaud Paquay010fed72016-11-11 14:25:29 -0800278 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700279 return None
280 except OSError:
281 return None
282 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100283 Trace(': parsing %s', self.file)
Mike Frysinger3164d402019-11-11 05:40:22 -0500284 with open(self._json) as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100285 return json.load(fd)
Anthony King85b24ac2014-05-06 15:57:48 +0100286 except (IOError, ValueError):
Renaud Paquay010fed72016-11-11 14:25:29 -0800287 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700288 return None
289
Anthony King85b24ac2014-05-06 15:57:48 +0100290 def _SaveJson(self, cache):
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700291 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500292 with open(self._json, 'w') as fd:
Anthony King85b24ac2014-05-06 15:57:48 +0100293 json.dump(cache, fd, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +0100294 except (IOError, TypeError):
Anthony Kingb1d1fd72015-06-03 17:02:26 +0100295 if os.path.exists(self._json):
Renaud Paquay010fed72016-11-11 14:25:29 -0800296 platform_utils.remove(self._json)
Shawn O. Pearcec12c3602009-04-17 21:03:32 -0700297
298 def _ReadGit(self):
David Aguilar438c5472009-06-28 15:09:16 -0700299 """
300 Read configuration data from git.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700301
David Aguilar438c5472009-06-28 15:09:16 -0700302 This internal method populates the GitConfig cache.
303
304 """
David Aguilar438c5472009-06-28 15:09:16 -0700305 c = {}
Shawn O. Pearcec24c7202009-07-02 16:12:57 -0700306 d = self._do('--null', '--list')
307 if d is None:
308 return c
Dylan Denge469a0c2018-06-23 15:02:26 +0800309 if not is_python3():
310 d = d.decode('utf-8')
311 for line in d.rstrip('\0').split('\0'):
David Aguilar438c5472009-06-28 15:09:16 -0700312 if '\n' in line:
David Pursehousec1b86a22012-11-14 11:36:51 +0900313 key, val = line.split('\n', 1)
David Aguilar438c5472009-06-28 15:09:16 -0700314 else:
David Pursehousec1b86a22012-11-14 11:36:51 +0900315 key = line
316 val = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700317
318 if key in c:
319 c[key].append(val)
320 else:
321 c[key] = [val]
322
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700323 return c
324
325 def _do(self, *args):
326 command = ['config', '--file', self.file]
327 command.extend(args)
328
329 p = GitCommand(None,
330 command,
David Pursehousee5913ae2020-02-12 13:56:59 +0900331 capture_stdout=True,
332 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700333 if p.Wait() == 0:
334 return p.stdout
335 else:
336 GitError('git config %s: %s' % (str(args), p.stderr))
337
338
339class RefSpec(object):
340 """A Git refspec line, split into its components:
341
342 forced: True if the line starts with '+'
343 src: Left side of the line
344 dst: Right side of the line
345 """
346
347 @classmethod
348 def FromString(cls, rs):
349 lhs, rhs = rs.split(':', 2)
350 if lhs.startswith('+'):
351 lhs = lhs[1:]
352 forced = True
353 else:
354 forced = False
355 return cls(forced, lhs, rhs)
356
357 def __init__(self, forced, lhs, rhs):
358 self.forced = forced
359 self.src = lhs
360 self.dst = rhs
361
362 def SourceMatches(self, rev):
363 if self.src:
364 if rev == self.src:
365 return True
366 if self.src.endswith('/*') and rev.startswith(self.src[:-1]):
367 return True
368 return False
369
370 def DestMatches(self, ref):
371 if self.dst:
372 if ref == self.dst:
373 return True
374 if self.dst.endswith('/*') and ref.startswith(self.dst[:-1]):
375 return True
376 return False
377
378 def MapSource(self, rev):
379 if self.src.endswith('/*'):
380 return self.dst[:-1] + rev[len(self.src) - 1:]
381 return self.dst
382
383 def __str__(self):
384 s = ''
385 if self.forced:
386 s += '+'
387 if self.src:
388 s += self.src
389 if self.dst:
390 s += ':'
391 s += self.dst
392 return s
393
394
Doug Anderson06d029c2010-10-27 17:06:01 -0700395_master_processes = []
396_master_keys = set()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700397_ssh_master = True
Doug Anderson0048b692010-12-21 13:39:23 -0800398_master_keys_lock = None
399
David Pursehouse819827a2020-02-12 15:20:19 +0900400
Doug Anderson0048b692010-12-21 13:39:23 -0800401def init_ssh():
402 """Should be called once at the start of repo to init ssh master handling.
403
404 At the moment, all we do is to create our lock.
405 """
406 global _master_keys_lock
407 assert _master_keys_lock is None, "Should only call init_ssh once"
408 _master_keys_lock = _threading.Lock()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700409
David Pursehouse819827a2020-02-12 15:20:19 +0900410
Josh Guilfoyle71985722009-08-16 09:44:40 -0700411def _open_ssh(host, port=None):
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700412 global _ssh_master
413
Doug Anderson0048b692010-12-21 13:39:23 -0800414 # Acquire the lock. This is needed to prevent opening multiple masters for
415 # the same host when we're running "repo sync -jN" (for N > 1) _and_ the
416 # manifest <remote fetch="ssh://xyz"> specifies a different host from the
417 # one that was passed to repo init.
418 _master_keys_lock.acquire()
Doug Anderson06d029c2010-10-27 17:06:01 -0700419 try:
Doug Anderson06d029c2010-10-27 17:06:01 -0700420
Doug Anderson0048b692010-12-21 13:39:23 -0800421 # Check to see whether we already think that the master is running; if we
422 # think it's already running, return right away.
423 if port is not None:
424 key = '%s:%s' % (host, port)
425 else:
426 key = host
427
428 if key in _master_keys:
Doug Anderson06d029c2010-10-27 17:06:01 -0700429 return True
Doug Anderson06d029c2010-10-27 17:06:01 -0700430
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900431 if (not _ssh_master
432 or 'GIT_SSH' in os.environ
433 or sys.platform in ('win32', 'cygwin')):
Doug Anderson0048b692010-12-21 13:39:23 -0800434 # failed earlier, or cygwin ssh can't do this
435 #
436 return False
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700437
Doug Anderson0048b692010-12-21 13:39:23 -0800438 # We will make two calls to ssh; this is the common part of both calls.
439 command_base = ['ssh',
David Pursehouseabdf7502020-02-12 14:58:39 +0900440 '-o', 'ControlPath %s' % ssh_sock(),
441 host]
Doug Anderson0048b692010-12-21 13:39:23 -0800442 if port is not None:
David Pursehouse8f62fb72012-11-14 12:09:38 +0900443 command_base[1:1] = ['-p', str(port)]
Doug Anderson0048b692010-12-21 13:39:23 -0800444
445 # Since the key wasn't in _master_keys, we think that master isn't running.
446 # ...but before actually starting a master, we'll double-check. This can
447 # be important because we can't tell that that 'git@myhost.com' is the same
448 # as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
David Pursehouse54a4e602020-02-12 14:31:05 +0900449 check_command = command_base + ['-O', 'check']
Doug Anderson0048b692010-12-21 13:39:23 -0800450 try:
451 Trace(': %s', ' '.join(check_command))
452 check_process = subprocess.Popen(check_command,
453 stdout=subprocess.PIPE,
454 stderr=subprocess.PIPE)
David Pursehouse54a4e602020-02-12 14:31:05 +0900455 check_process.communicate() # read output, but ignore it...
Doug Anderson0048b692010-12-21 13:39:23 -0800456 isnt_running = check_process.wait()
457
458 if not isnt_running:
459 # Our double-check found that the master _was_ infact running. Add to
460 # the list of keys.
461 _master_keys.add(key)
462 return True
463 except Exception:
464 # Ignore excpetions. We we will fall back to the normal command and print
465 # to the log there.
466 pass
467
David Pursehouse0ab95ba2020-02-12 15:01:59 +0900468 command = command_base[:1] + ['-M', '-N'] + command_base[1:]
Doug Anderson0048b692010-12-21 13:39:23 -0800469 try:
470 Trace(': %s', ' '.join(command))
471 p = subprocess.Popen(command)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700472 except Exception as e:
Doug Anderson0048b692010-12-21 13:39:23 -0800473 _ssh_master = False
Sarah Owenscecd1d82012-11-01 22:59:27 -0700474 print('\nwarn: cannot enable ssh control master for %s:%s\n%s'
David Pursehouseabdf7502020-02-12 14:58:39 +0900475 % (host, port, str(e)), file=sys.stderr)
Doug Anderson0048b692010-12-21 13:39:23 -0800476 return False
477
Timo Lotterbach05dc46b2016-07-15 16:48:42 +0200478 time.sleep(1)
479 ssh_died = (p.poll() is not None)
480 if ssh_died:
481 return False
482
Doug Anderson0048b692010-12-21 13:39:23 -0800483 _master_processes.append(p)
484 _master_keys.add(key)
Doug Anderson0048b692010-12-21 13:39:23 -0800485 return True
486 finally:
487 _master_keys_lock.release()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700488
David Pursehouse819827a2020-02-12 15:20:19 +0900489
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700490def close_ssh():
Doug Anderson0048b692010-12-21 13:39:23 -0800491 global _master_keys_lock
492
Shawn O. Pearceca8c32c2010-05-11 18:21:33 -0700493 terminate_ssh_clients()
494
Doug Anderson06d029c2010-10-27 17:06:01 -0700495 for p in _master_processes:
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700496 try:
497 os.kill(p.pid, SIGTERM)
498 p.wait()
Shawn O. Pearcefb5c8fd2009-06-16 14:57:46 -0700499 except OSError:
Shawn O. Pearce26120ca2009-06-16 11:49:10 -0700500 pass
Doug Anderson06d029c2010-10-27 17:06:01 -0700501 del _master_processes[:]
502 _master_keys.clear()
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700503
Nico Sallembien1c85f4e2010-04-27 14:35:27 -0700504 d = ssh_sock(create=False)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700505 if d:
506 try:
Renaud Paquaybed8b622018-09-27 10:46:58 -0700507 platform_utils.rmdir(os.path.dirname(d))
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700508 except OSError:
509 pass
510
Doug Anderson0048b692010-12-21 13:39:23 -0800511 # We're done with the lock, so we can delete it.
512 _master_keys_lock = None
513
David Pursehouse819827a2020-02-12 15:20:19 +0900514
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700515URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):')
Shawn O. Pearce898e12a2012-03-14 15:22:28 -0700516URI_ALL = re.compile(r'^([a-z][a-z+-]*)://([^@/]*@?[^/]*)/')
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700517
David Pursehouse819827a2020-02-12 15:20:19 +0900518
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -0700519def GetSchemeFromUrl(url):
520 m = URI_ALL.match(url)
521 if m:
522 return m.group(1)
523 return None
524
David Pursehouse819827a2020-02-12 15:20:19 +0900525
Dan Willemsen0745bb22015-08-17 13:41:45 -0700526@contextlib.contextmanager
527def GetUrlCookieFile(url, quiet):
528 if url.startswith('persistent-'):
529 try:
530 p = subprocess.Popen(
531 ['git-remote-persistent-https', '-print_config', url],
532 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
533 stderr=subprocess.PIPE)
534 try:
535 cookieprefix = 'http.cookiefile='
536 proxyprefix = 'http.proxy='
537 cookiefile = None
538 proxy = None
539 for line in p.stdout:
Mike Frysingerded477d2020-02-07 23:18:23 -0500540 line = line.strip().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700541 if line.startswith(cookieprefix):
Daichi Ueurace7e0262018-02-26 08:49:36 +0900542 cookiefile = os.path.expanduser(line[len(cookieprefix):])
Dan Willemsen0745bb22015-08-17 13:41:45 -0700543 if line.startswith(proxyprefix):
544 proxy = line[len(proxyprefix):]
545 # Leave subprocess open, as cookie file may be transient.
546 if cookiefile or proxy:
547 yield cookiefile, proxy
548 return
549 finally:
550 p.stdin.close()
551 if p.wait():
Mike Frysingerded477d2020-02-07 23:18:23 -0500552 err_msg = p.stderr.read().decode('utf-8')
Dan Willemsen0745bb22015-08-17 13:41:45 -0700553 if ' -print_config' in err_msg:
554 pass # Persistent proxy doesn't support -print_config.
555 elif not quiet:
556 print(err_msg, file=sys.stderr)
557 except OSError as e:
558 if e.errno == errno.ENOENT:
559 pass # No persistent proxy.
560 raise
Daichi Ueurace7e0262018-02-26 08:49:36 +0900561 cookiefile = GitConfig.ForUser().GetString('http.cookiefile')
562 if cookiefile:
563 cookiefile = os.path.expanduser(cookiefile)
564 yield cookiefile, None
Dan Willemsen0745bb22015-08-17 13:41:45 -0700565
David Pursehouse819827a2020-02-12 15:20:19 +0900566
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700567def _preconnect(url):
568 m = URI_ALL.match(url)
569 if m:
570 scheme = m.group(1)
571 host = m.group(2)
572 if ':' in host:
573 host, port = host.split(':')
Shawn O. Pearce896d5df2009-04-21 14:51:04 -0700574 else:
Josh Guilfoyle71985722009-08-16 09:44:40 -0700575 port = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700576 if scheme in ('ssh', 'git+ssh', 'ssh+git'):
577 return _open_ssh(host, port)
578 return False
579
580 m = URI_SCP.match(url)
581 if m:
582 host = m.group(1)
Josh Guilfoyle71985722009-08-16 09:44:40 -0700583 return _open_ssh(host)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700584
Shawn O. Pearce7b4f4352009-06-12 09:06:35 -0700585 return False
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700586
David Pursehouse819827a2020-02-12 15:20:19 +0900587
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700588class Remote(object):
589 """Configuration options related to a remote.
590 """
David Pursehouse819827a2020-02-12 15:20:19 +0900591
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700592 def __init__(self, config, name):
593 self._config = config
594 self.name = name
595 self.url = self._Get('url')
Steve Raed6480452016-08-10 15:00:00 -0700596 self.pushUrl = self._Get('pushurl')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700597 self.review = self._Get('review')
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800598 self.projectname = self._Get('projectname')
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530599 self.fetch = list(map(RefSpec.FromString,
David Pursehouseabdf7502020-02-12 14:58:39 +0900600 self._Get('fetch', all_keys=True)))
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800601 self._review_url = None
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800602
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100603 def _InsteadOf(self):
604 globCfg = GitConfig.ForUser()
605 urlList = globCfg.GetSubSections('url')
606 longest = ""
607 longestUrl = ""
608
609 for url in urlList:
610 key = "url." + url + ".insteadOf"
David Pursehouse8a68ff92012-09-24 12:15:13 +0900611 insteadOfList = globCfg.GetString(key, all_keys=True)
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100612
613 for insteadOf in insteadOfList:
David Pursehouse16a5c3a2020-02-12 15:54:26 +0900614 if (self.url.startswith(insteadOf)
615 and len(insteadOf) > len(longest)):
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100616 longest = insteadOf
617 longestUrl = url
618
619 if len(longest) == 0:
620 return self.url
621
622 return self.url.replace(longest, longestUrl, 1)
623
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700624 def PreConnectFetch(self):
Ulrik Sjolinb6ea3bf2010-01-03 18:20:17 +0100625 connectionUrl = self._InsteadOf()
626 return _preconnect(connectionUrl)
Shawn O. Pearcefb231612009-04-10 18:53:46 -0700627
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200628 def ReviewUrl(self, userEmail, validate_certs):
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800629 if self._review_url is None:
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800630 if self.review is None:
631 return None
632
633 u = self.review
Conley Owens7e12e0a2014-10-23 15:40:00 -0700634 if u.startswith('persistent-'):
635 u = u[len('persistent-'):]
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100636 if u.split(':')[0] not in ('http', 'https', 'sso', 'ssh'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800637 u = 'http://%s' % u
Shawn O. Pearce13cc3842009-03-25 13:54:54 -0700638 if u.endswith('/Gerrit'):
639 u = u[:len(u) - len('/Gerrit')]
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800640 if u.endswith('/ssh_info'):
641 u = u[:len(u) - len('/ssh_info')]
642 if not u.endswith('/'):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900643 u += '/'
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800644 http_url = u
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800645
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700646 if u in REVIEW_CACHE:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800647 self._review_url = REVIEW_CACHE[u]
Shawn O. Pearce1a68dc52011-10-11 14:12:46 -0700648 elif 'REPO_HOST_PORT_INFO' in os.environ:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800649 host, port = os.environ['REPO_HOST_PORT_INFO'].split()
650 self._review_url = self._SshReviewUrl(userEmail, host, port)
651 REVIEW_CACHE[u] = self._review_url
Christian Koestlin2ec2a5d2016-12-05 20:32:45 +0100652 elif u.startswith('sso:') or u.startswith('ssh:'):
Steve Pucci143d8a72014-01-30 09:45:53 -0800653 self._review_url = u # Assume it's right
654 REVIEW_CACHE[u] = self._review_url
Timo Lotterbacheec726c2016-10-07 10:52:08 +0200655 elif 'REPO_IGNORE_SSH_INFO' in os.environ:
656 self._review_url = http_url
657 REVIEW_CACHE[u] = self._review_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700658 else:
659 try:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800660 info_url = u + 'ssh_info'
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200661 if not validate_certs:
662 context = ssl._create_unverified_context()
663 info = urllib.request.urlopen(info_url, context=context).read()
664 else:
665 info = urllib.request.urlopen(info_url).read()
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400666 if info == b'NOT_AVAILABLE' or b'<' in info:
Conley Owens745a39b2013-06-05 13:16:18 -0700667 # If `info` contains '<', we assume the server gave us some sort
668 # of HTML response back, like maybe a login page.
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700669 #
Conley Owens745a39b2013-06-05 13:16:18 -0700670 # Assume HTTP if SSH is not enabled or ssh_info doesn't look right.
Conley Owens2cd38a02014-02-04 15:32:29 -0800671 self._review_url = http_url
Shawn O. Pearce146fe902009-03-25 14:06:43 -0700672 else:
Mike Frysinger1b9adab2019-07-04 17:54:54 -0400673 info = info.decode('utf-8')
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800674 host, port = info.split()
Dan Willemsen16889ba2016-09-22 16:39:06 +0000675 self._review_url = self._SshReviewUrl(userEmail, host, port)
Sarah Owens1f7627f2012-10-31 09:21:55 -0700676 except urllib.error.HTTPError as e:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800677 raise UploadError('%s: %s' % (self.review, str(e)))
Sarah Owens1f7627f2012-10-31 09:21:55 -0700678 except urllib.error.URLError as e:
Shawn O. Pearcebf1fbb22011-10-11 09:31:58 -0700679 raise UploadError('%s: %s' % (self.review, str(e)))
David Pursehouseecf8f2b2013-05-24 12:12:23 +0900680 except HTTPException as e:
681 raise UploadError('%s: %s' % (self.review, e.__class__.__name__))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800682
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800683 REVIEW_CACHE[u] = self._review_url
684 return self._review_url + self.projectname
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800685
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800686 def _SshReviewUrl(self, userEmail, host, port):
Shawn O. Pearce3575b8f2010-07-15 17:00:14 -0700687 username = self._config.GetString('review.%s.username' % self.review)
688 if username is None:
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800689 username = userEmail.split('@')[0]
690 return 'ssh://%s@%s:%s/' % (username, host, port)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700691
692 def ToLocal(self, rev):
693 """Convert a remote revision string to something we have locally.
694 """
Yann Droneaud936183a2013-09-12 10:51:18 +0200695 if self.name == '.' or IsId(rev):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700696 return rev
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700697
698 if not rev.startswith('refs/'):
699 rev = R_HEADS + rev
700
701 for spec in self.fetch:
702 if spec.SourceMatches(rev):
703 return spec.MapSource(rev)
Nasser Grainawi909d58b2014-09-19 12:13:04 -0600704
705 if not rev.startswith(R_HEADS):
706 return rev
707
Mike Frysinger1f2462e2019-08-03 01:57:09 -0400708 raise GitError('%s: remote %s does not have %s' %
709 (self.projectname, self.name, rev))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700710
711 def WritesTo(self, ref):
712 """True if the remote stores to the tracking ref.
713 """
714 for spec in self.fetch:
715 if spec.DestMatches(ref):
716 return True
717 return False
718
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800719 def ResetFetch(self, mirror=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700720 """Set the fetch refspec to its default value.
721 """
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800722 if mirror:
723 dst = 'refs/heads/*'
724 else:
725 dst = 'refs/remotes/%s/*' % self.name
726 self.fetch = [RefSpec(True, 'refs/heads/*', dst)]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700727
728 def Save(self):
729 """Save this remote to the configuration.
730 """
731 self._Set('url', self.url)
Steve Raed6480452016-08-10 15:00:00 -0700732 if self.pushUrl is not None:
733 self._Set('pushurl', self.pushUrl + '/' + self.projectname)
734 else:
735 self._Set('pushurl', self.pushUrl)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700736 self._Set('review', self.review)
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800737 self._Set('projectname', self.projectname)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530738 self._Set('fetch', list(map(str, self.fetch)))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700739
740 def _Set(self, key, value):
741 key = 'remote.%s.%s' % (self.name, key)
742 return self._config.SetString(key, value)
743
David Pursehouse8a68ff92012-09-24 12:15:13 +0900744 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745 key = 'remote.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900746 return self._config.GetString(key, all_keys=all_keys)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747
748
749class Branch(object):
750 """Configuration options related to a single branch.
751 """
David Pursehouse819827a2020-02-12 15:20:19 +0900752
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700753 def __init__(self, config, name):
754 self._config = config
755 self.name = name
756 self.merge = self._Get('merge')
757
758 r = self._Get('remote')
759 if r:
760 self.remote = self._config.GetRemote(r)
761 else:
762 self.remote = None
763
764 @property
765 def LocalMerge(self):
766 """Convert the merge spec to a local name.
767 """
768 if self.remote and self.merge:
769 return self.remote.ToLocal(self.merge)
770 return None
771
772 def Save(self):
773 """Save this branch back into the configuration.
774 """
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700775 if self._config.HasSection('branch', self.name):
776 if self.remote:
777 self._Set('remote', self.remote.name)
778 else:
779 self._Set('remote', None)
780 self._Set('merge', self.merge)
781
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700782 else:
Mike Frysinger3164d402019-11-11 05:40:22 -0500783 with open(self._config.file, 'a') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -0700784 fd.write('[branch "%s"]\n' % self.name)
785 if self.remote:
786 fd.write('\tremote = %s\n' % self.remote.name)
787 if self.merge:
788 fd.write('\tmerge = %s\n' % self.merge)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700789
790 def _Set(self, key, value):
791 key = 'branch.%s.%s' % (self.name, key)
792 return self._config.SetString(key, value)
793
David Pursehouse8a68ff92012-09-24 12:15:13 +0900794 def _Get(self, key, all_keys=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700795 key = 'branch.%s.%s' % (self.name, key)
David Pursehousee5913ae2020-02-12 13:56:59 +0900796 return self._config.GetString(key, all_keys=all_keys)