blob: a750a230c624e504f1cb31de22a02fb235477cb3 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
Anthony King85b24ac2014-05-06 15:57:48 +010017import json
David Pursehouse86d973d2012-08-24 10:21:02 +090018import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070019from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import os
21import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
David Pursehouse59bbb582013-05-17 10:49:33 +090028
29from pyversion import is_python3
30if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070031 import http.cookiejar as cookielib
32 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070034 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpc.client
36else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070037 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090041 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070043 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053044 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053046 xmlrpc = imp.new_module('xmlrpc')
47 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070048
Roy Lee18afd7f2010-05-09 04:32:08 +080049try:
50 import threading as _threading
51except ImportError:
52 import dummy_threading as _threading
53
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070054try:
55 import resource
56 def _rlimit_nofile():
57 return resource.getrlimit(resource.RLIMIT_NOFILE)
58except ImportError:
59 def _rlimit_nofile():
60 return (256, 256)
61
Dave Borowitz18857212012-10-23 17:02:59 -070062try:
63 import multiprocessing
64except ImportError:
65 multiprocessing = None
66
Dave Borowitze2152672012-10-31 12:24:38 -070067from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090068from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090069from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070070import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070071from project import Project
72from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080073from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000074from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070075from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070076from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080077from wrapper import Wrapper
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070078
Dave Borowitz67700e92012-10-23 15:00:54 -070079_ONE_DAY_S = 24 * 60 * 60
80
Doug Andersonfc06ced2011-03-16 15:49:18 -070081class _FetchError(Exception):
82 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
83 pass
84
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080085class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080086 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070087 common = True
88 helpSummary = "Update working tree to the latest revision"
89 helpUsage = """
90%prog [<project>...]
91"""
92 helpDescription = """
93The '%prog' command synchronizes local project directories
94with the remote repositories specified in the manifest. If a local
95project does not yet exist, it will clone a new local directory from
96the remote repository and set up tracking branches as specified in
97the manifest. If the local project already exists, '%prog'
98will update the remote branches and rebase any new local changes
99on top of the new remote changes.
100
101'%prog' will synchronize all projects listed at the command
102line. Projects can be specified either by name, or by a relative
103or absolute path to the project's local directory. If no projects
104are specified, '%prog' will synchronize all projects listed in
105the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700106
107The -d/--detach option can be used to switch specified projects
108back to the manifest revision. This option is especially helpful
109if the project is currently on a topic branch, but the manifest
110revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700111
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700112The -s/--smart-sync option can be used to sync to a known good
113build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200114manifest. The -t/--smart-tag option is similar and allows you to
115specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700116
David Pursehousecf76b1b2012-09-14 10:31:42 +0900117The -u/--manifest-server-username and -p/--manifest-server-password
118options can be used to specify a username and password to authenticate
119with the manifest server when using the -s or -t option.
120
121If -u and -p are not specified when using the -s or -t option, '%prog'
122will attempt to read authentication credentials for the manifest server
123from the user's .netrc file.
124
125'%prog' will not use authentication credentials from -u/-p or .netrc
126if the manifest server specified in the manifest file already includes
127credentials.
128
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500129The -f/--force-broken option can be used to proceed with syncing
130other projects if a project sync fails.
131
Kevin Degiabaa7f32014-11-12 11:27:45 -0700132The --force-sync option can be used to overwrite existing git
133directories if they have previously been linked to a different
134object direcotry. WARNING: This may cause data to be lost since
135refs may be removed when overwriting.
136
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700137The --no-clone-bundle option disables any attempt to use
138$URL/clone.bundle to bootstrap a new Git repository from a
139resumeable bundle file on a content delivery network. This
140may be necessary if there are problems with the local Python
141HTTP client or proxy configuration, but the Git binary works.
142
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800143The --fetch-submodules option enables fetching Git submodules
144of a project from server.
145
David Pursehousef2fad612015-01-29 14:36:28 +0900146The -c/--current-branch option can be used to only fetch objects that
147are on the branch specified by a project's revision.
148
David Pursehouseb1553542014-09-04 21:28:09 +0900149The --optimized-fetch option can be used to only fetch projects that
150are fixed to a sha1 revision if the sha1 revision does not already
151exist locally.
152
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700153SSH Connections
154---------------
155
156If at least one project remote URL uses an SSH connection (ssh://,
157git+ssh://, or user@host:path syntax) repo will automatically
158enable the SSH ControlMaster option when connecting to that host.
159This feature permits other projects in the same '%prog' session to
160reuse the same SSH tunnel, saving connection setup overheads.
161
162To disable this behavior on UNIX platforms, set the GIT_SSH
163environment variable to 'ssh'. For example:
164
165 export GIT_SSH=ssh
166 %prog
167
168Compatibility
169~~~~~~~~~~~~~
170
171This feature is automatically disabled on Windows, due to the lack
172of UNIX domain socket support.
173
174This feature is not compatible with url.insteadof rewrites in the
175user's ~/.gitconfig. '%prog' is currently not able to perform the
176rewrite early enough to establish the ControlMaster tunnel.
177
178If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
179later is required to fix a server side protocol bug.
180
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181"""
182
Nico Sallembien6623b212010-05-11 12:57:01 -0700183 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000184 try:
185 self.jobs = self.manifest.default.sync_j
186 except ManifestParseError:
187 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700188
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500189 p.add_option('-f', '--force-broken',
190 dest='force_broken', action='store_true',
191 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700192 p.add_option('--force-sync',
193 dest='force_sync', action='store_true',
194 help="overwrite an existing git directory if it needs to "
195 "point to a different object directory. WARNING: this "
196 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900197 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700198 dest='local_only', action='store_true',
199 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900200 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700201 dest='network_only', action='store_true',
202 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900203 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700204 dest='detach_head', action='store_true',
205 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900206 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700207 dest='current_branch_only', action='store_true',
208 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700210 dest='quiet', action='store_true',
211 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800213 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700214 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500215 p.add_option('-m', '--manifest-name',
216 dest='manifest_name',
217 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700218 p.add_option('--no-clone-bundle',
219 dest='no_clone_bundle', action='store_true',
220 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800221 p.add_option('-u', '--manifest-server-username', action='store',
222 dest='manifest_server_username',
223 help='username to authenticate with the manifest server')
224 p.add_option('-p', '--manifest-server-password', action='store',
225 dest='manifest_server_password',
226 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800227 p.add_option('--fetch-submodules',
228 dest='fetch_submodules', action='store_true',
229 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700230 p.add_option('--no-tags',
231 dest='no_tags', action='store_true',
232 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900233 p.add_option('--optimized-fetch',
234 dest='optimized_fetch', action='store_true',
235 help='only fetch projects fixed to sha1 if revision does not exist locally')
Nico Sallembien6623b212010-05-11 12:57:01 -0700236 if show_smart:
237 p.add_option('-s', '--smart-sync',
238 dest='smart_sync', action='store_true',
239 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200240 p.add_option('-t', '--smart-tag',
241 dest='smart_tag', action='store',
242 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700243
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700244 g = p.add_option_group('repo Version options')
245 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246 dest='no_repo_verify', action='store_true',
247 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700248 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800249 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700250 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251
natalie.chen451dca82015-12-09 21:24:16 +0800252 def _FetchProjectList(self, opt, projects, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900253 """Main function of the fetch threads when jobs are > 1.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700254
David James8d201162013-10-11 17:03:19 -0700255 Delegates most of the work to _FetchHelper.
256
257 Args:
258 opt: Program options returned from optparse. See _Options().
259 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800260 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700261 _FetchHelper docstring for details.
262 """
natalie.chen451dca82015-12-09 21:24:16 +0800263 try:
264 for project in projects:
265 success = self._FetchHelper(opt, project, lock, fetched, pm, sem, err_event)
266 if not success and not opt.force_broken:
267 break
268 finally:
269 sem.release()
David James8d201162013-10-11 17:03:19 -0700270
271 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
272 """Fetch git objects for a single project.
273
David Pursehousec1b86a22012-11-14 11:36:51 +0900274 Args:
275 opt: Program options returned from optparse. See _Options().
276 project: Project object for the project to fetch.
277 lock: Lock for accessing objects that are shared amongst multiple
278 _FetchHelper() threads.
279 fetched: set object that we will add project.gitdir to when we're done
280 (with our lock held).
281 pm: Instance of a Project object. We will call pm.update() (with our
282 lock held).
283 sem: We'll release() this semaphore when we exit so that another thread
284 can be started up.
285 err_event: We'll set this event in the case of an error (after printing
286 out info about the error).
David James8d201162013-10-11 17:03:19 -0700287
288 Returns:
289 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900290 """
291 # We'll set to true once we've locked the lock.
292 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700293
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530294 if not opt.quiet:
295 print('Fetching project %s' % project.name)
296
David Pursehousec1b86a22012-11-14 11:36:51 +0900297 # Encapsulate everything in a try/except/finally so that:
298 # - We always set err_event in the case of an exception.
299 # - We always make sure we call sem.release().
300 # - We always make sure we unlock the lock if we locked it.
301 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700302 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900303 start = time.time()
304 success = project.Sync_NetworkHalf(
305 quiet=opt.quiet,
306 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700307 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700308 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900309 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
310 optimized_fetch=opt.optimized_fetch)
David Pursehousec1b86a22012-11-14 11:36:51 +0900311 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700312
David Pursehousec1b86a22012-11-14 11:36:51 +0900313 # Lock around all the rest of the code, since printing, updating a set
314 # and Progress.update() are not thread safe.
315 lock.acquire()
316 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700317
David Pursehousec1b86a22012-11-14 11:36:51 +0900318 if not success:
319 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
320 if opt.force_broken:
321 print('warn: --force-broken, continuing to sync',
322 file=sys.stderr)
323 else:
324 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800325
David Pursehousec1b86a22012-11-14 11:36:51 +0900326 fetched.add(project.gitdir)
327 pm.update()
328 except _FetchError:
329 err_event.set()
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400330 except Exception as e:
331 print('error: Cannot fetch %s (%s: %s)' \
332 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 err_event.set()
334 raise
335 finally:
336 if did_lock:
337 lock.release()
natalie.chen451dca82015-12-09 21:24:16 +0800338 #sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800339
David James8d201162013-10-11 17:03:19 -0700340 return success
341
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700342 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700343 fetched = set()
David James89ece422014-01-09 18:51:58 -0800344 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700345 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800346
David James89ece422014-01-09 18:51:58 -0800347 objdir_project_map = dict()
348 for project in projects:
349 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700350
David James89ece422014-01-09 18:51:58 -0800351 threads = set()
352 sem = _threading.Semaphore(self.jobs)
353 err_event = _threading.Event()
354 for project_list in objdir_project_map.values():
355 # Check for any errors before running any more tasks.
356 # ...we'll let existing threads finish, though.
357 if err_event.isSet() and not opt.force_broken:
358 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700359
David James89ece422014-01-09 18:51:58 -0800360 sem.acquire()
361 kwargs = dict(opt=opt,
362 projects=project_list,
363 lock=lock,
364 fetched=fetched,
365 pm=pm,
366 sem=sem,
367 err_event=err_event)
368 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700369 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800370 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200371 # Ensure that Ctrl-C will not freeze the repo process.
372 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800373 threads.add(t)
374 t.start()
David James89ece422014-01-09 18:51:58 -0800375 else:
376 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800377
David James89ece422014-01-09 18:51:58 -0800378 for t in threads:
379 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800380
David James89ece422014-01-09 18:51:58 -0800381 # If we saw an error, exit with code 1 so that other scripts can check.
382 if err_event.isSet():
383 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
384 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700385
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700386 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700387 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700388
Julien Campergue335f5ef2013-10-16 11:02:35 +0200389 if not self.manifest.IsArchive:
390 self._GCProjects(projects)
391
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700392 return fetched
393
Dave Borowitz18857212012-10-23 17:02:59 -0700394 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700395 gitdirs = {}
396 for project in projects:
397 gitdirs[project.gitdir] = project.bare_git
398
Dave Borowitze2152672012-10-31 12:24:38 -0700399 has_dash_c = git_require((1, 7, 2))
400 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700401 cpu_count = multiprocessing.cpu_count()
402 else:
403 cpu_count = 1
404 jobs = min(self.jobs, cpu_count)
405
406 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700407 for bare_git in gitdirs.values():
408 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700409 return
410
411 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
412
413 threads = set()
414 sem = _threading.Semaphore(jobs)
415 err_event = _threading.Event()
416
David James8d201162013-10-11 17:03:19 -0700417 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700418 try:
419 try:
David James8d201162013-10-11 17:03:19 -0700420 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700421 except GitError:
422 err_event.set()
423 except:
424 err_event.set()
425 raise
426 finally:
427 sem.release()
428
David James8d201162013-10-11 17:03:19 -0700429 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700430 if err_event.isSet():
431 break
432 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700433 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700434 t.daemon = True
435 threads.add(t)
436 t.start()
437
438 for t in threads:
439 t.join()
440
441 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700442 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700443 sys.exit(1)
444
Tim Kilbourn07669002013-03-08 15:02:49 -0800445 def _ReloadManifest(self, manifest_name=None):
446 if manifest_name:
447 # Override calls _Unload already
448 self.manifest.Override(manifest_name)
449 else:
450 self.manifest._Unload()
451
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700452 def UpdateProjectList(self):
453 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700454 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700455 if project.relpath:
456 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700457 file_name = 'project.list'
458 file_path = os.path.join(self.manifest.repodir, file_name)
459 old_project_paths = []
460
461 if os.path.exists(file_path):
462 fd = open(file_path, 'r')
463 try:
464 old_project_paths = fd.read().split('\n')
465 finally:
466 fd.close()
467 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700468 if not path:
469 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700470 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900471 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400472 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700473 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900474 project = Project(
475 manifest = self.manifest,
476 name = path,
477 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700478 gitdir = gitdir,
479 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900480 worktree = os.path.join(self.manifest.topdir, path),
481 relpath = path,
482 revisionExpr = 'HEAD',
483 revisionId = None,
484 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400485
David Pursehousec1b86a22012-11-14 11:36:51 +0900486 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900487 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900488 'are present' % project.relpath, file=sys.stderr)
489 print(' commit changes, then run sync again',
490 file=sys.stderr)
491 return -1
492 else:
493 print('Deleting obsolete path %s' % project.worktree,
494 file=sys.stderr)
495 shutil.rmtree(project.worktree)
496 # Try deleting parent subdirs if they are empty
497 project_dir = os.path.dirname(project.worktree)
498 while project_dir != self.manifest.topdir:
499 try:
500 os.rmdir(project_dir)
501 except OSError:
502 break
503 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700504
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700505 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700506 fd = open(file_path, 'w')
507 try:
508 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700509 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700510 finally:
511 fd.close()
512 return 0
513
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700514 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800515 if opt.jobs:
516 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700517 if self.jobs > 1:
518 soft_limit, _ = _rlimit_nofile()
519 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
520
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700521 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700522 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700523 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700524 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700525 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700526 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500527 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700528 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500529 sys.exit(1)
530 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700531 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500532 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900533 if opt.manifest_server_username or opt.manifest_server_password:
534 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700535 print('error: -u and -p may only be combined with -s or -t',
536 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900537 sys.exit(1)
538 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700539 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900540 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500541
542 if opt.manifest_name:
543 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700544
Chirayu Desaia892b102013-06-11 14:18:46 +0530545 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900546 smart_sync_manifest_name = "smart_sync_override.xml"
547 smart_sync_manifest_path = os.path.join(
548 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530549
Victor Boivie08c880d2011-04-19 10:32:52 +0200550 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700551 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900552 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700553 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700554 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900555
556 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900557 if not opt.quiet:
558 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900559
David Pursehouse86d973d2012-08-24 10:21:02 +0900560 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900561 username = None
562 password = None
563 if opt.manifest_server_username and opt.manifest_server_password:
564 username = opt.manifest_server_username
565 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900566 else:
567 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900568 info = netrc.netrc()
569 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900570 # .netrc file does not exist or could not be opened
571 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900572 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900573 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530574 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900575 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900576 auth = info.authenticators(parse_result.hostname)
577 if auth:
578 username, _account, password = auth
579 else:
580 print('No credentials found for %s in .netrc'
581 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700582 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700583 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900584
585 if (username and password):
586 manifest_server = manifest_server.replace('://', '://%s:%s@' %
587 (username, password),
588 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900589
Dan Willemsen0745bb22015-08-17 13:41:45 -0700590 transport = PersistentTransport(manifest_server)
591 if manifest_server.startswith('persistent-'):
592 manifest_server = manifest_server[len('persistent-'):]
593
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700594 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700595 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200596 if opt.smart_sync:
597 p = self.manifest.manifestProject
598 b = p.GetBranch(p.CurrentBranch)
599 branch = b.merge
600 if branch.startswith(R_HEADS):
601 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700602
Victor Boivie08c880d2011-04-19 10:32:52 +0200603 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700604 if 'SYNC_TARGET' in env:
605 target = env['SYNC_TARGET']
606 [success, manifest_str] = server.GetApprovedManifest(branch, target)
607 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200608 target = '%s-%s' % (env['TARGET_PRODUCT'],
609 env['TARGET_BUILD_VARIANT'])
610 [success, manifest_str] = server.GetApprovedManifest(branch, target)
611 else:
612 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700613 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200614 assert(opt.smart_tag)
615 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700616
617 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900618 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700619 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900620 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700621 try:
622 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700623 finally:
624 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900625 except IOError as e:
626 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900627 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700628 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700629 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100630 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700631 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900632 print('error: manifest server RPC call failed: %s' %
633 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700634 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530635 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700636 print('error: cannot connect to manifest server %s:\n%s'
637 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900638 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530639 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700640 print('error: cannot connect to manifest server %s:\n%d %s'
641 % (self.manifest.manifest_server, e.errcode, e.errmsg),
642 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700643 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900644 else: # Not smart sync or smart tag mode
645 if os.path.isfile(smart_sync_manifest_path):
646 try:
647 os.remove(smart_sync_manifest_path)
648 except OSError as e:
649 print('error: failed to remove existing smart sync override manifest: %s' %
650 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700651
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700652 rp = self.manifest.repoProject
653 rp.PreSync()
654
655 mp = self.manifest.manifestProject
656 mp.PreSync()
657
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800658 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700659 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800660
Nico Sallembien9bb18162009-12-07 15:38:01 -0800661 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700662 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700663 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900664 no_tags=opt.no_tags,
665 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800666
667 if mp.HasChanges:
668 syncbuf = SyncBuffer(mp.config)
669 mp.Sync_LocalHalf(syncbuf)
670 if not syncbuf.Finish():
671 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100672 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700673 if opt.jobs is None:
674 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700675
Dan Willemsen03755232015-09-09 21:43:32 +0000676 # TODO (sbasi) - Add support for manifest changes, aka projects
677 # have been added or deleted from the manifest.
Simran Basib9a1b732015-08-20 12:19:28 -0700678 if self.gitc_manifest:
679 gitc_manifest_projects = self.GetProjects(args,
Dan Willemsen03755232015-09-09 21:43:32 +0000680 manifest=self.gitc_manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700681 missing_ok=True)
682 gitc_projects = []
683 opened_projects = []
684 for project in gitc_manifest_projects:
Dan Willemsen03755232015-09-09 21:43:32 +0000685 if not project.old_revision:
686 gitc_projects.append(project)
Simran Basib9a1b732015-08-20 12:19:28 -0700687 else:
Dan Willemsen03755232015-09-09 21:43:32 +0000688 opened_projects.append(project)
Simran Basib9a1b732015-08-20 12:19:28 -0700689
Dan Willemsen03755232015-09-09 21:43:32 +0000690 if gitc_projects and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700691 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen03755232015-09-09 21:43:32 +0000692 gitc_utils.generate_gitc_manifest(self.gitc_manifest.gitc_client_dir,
Simran Basib9a1b732015-08-20 12:19:28 -0700693 self.gitc_manifest,
694 gitc_projects)
695 print('GITC client successfully synced.')
696
697 # The opened projects need to be synced as normal, therefore we
698 # generate a new args list to represent the opened projects.
Dan Willemsen03755232015-09-09 21:43:32 +0000699 args = []
700 for proj in opened_projects:
701 args.append(os.path.relpath(proj.worktree, os.getcwd()))
Simran Basib9a1b732015-08-20 12:19:28 -0700702 if not args:
703 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800704 all_projects = self.GetProjects(args,
705 missing_ok=True,
706 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700707
Dave Borowitz67700e92012-10-23 15:00:54 -0700708 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700709 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700710 to_fetch = []
711 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700712 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700713 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900714 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700715 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700716
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800717 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700718 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700719 if opt.network_only:
720 # bail out now; the rest touches the working tree
721 return
722
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800723 # Iteratively fetch missing and/or nested unregistered submodules
724 previously_missing_set = set()
725 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100726 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800727 all_projects = self.GetProjects(args,
728 missing_ok=True,
729 submodules_ok=opt.fetch_submodules)
730 missing = []
731 for project in all_projects:
732 if project.gitdir not in fetched:
733 missing.append(project)
734 if not missing:
735 break
736 # Stop us from non-stopped fetching actually-missing repos: If set of
737 # missing repos has not been changed from last fetch, we break.
738 missing_set = set(p.name for p in missing)
739 if previously_missing_set == missing_set:
740 break
741 previously_missing_set = missing_set
742 fetched.update(self._Fetch(missing, opt))
743
Julien Campergue335f5ef2013-10-16 11:02:35 +0200744 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700745 # bail out now, we have no working tree
746 return
747
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700748 if self.UpdateProjectList():
749 sys.exit(1)
750
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700751 syncbuf = SyncBuffer(mp.config,
752 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900753 pm = Progress('Syncing work tree', len(all_projects))
754 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700755 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800756 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700757 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700758 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700759 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700760 if not syncbuf.Finish():
761 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700762
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700763 # If there's a notice that's supposed to print at the end of the sync, print
764 # it now...
765 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700766 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700767
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700768def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800769 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700770 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700771 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800772 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700773 if project.Exists:
774 project.PostRepoUpgrade()
775
776def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
777 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700778 print('info: A new version of repo is available', file=sys.stderr)
779 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700780 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700781 syncbuf = SyncBuffer(rp.config)
782 rp.Sync_LocalHalf(syncbuf)
783 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700784 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700785 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700786 raise RepoChangedException(['--repo-upgraded'])
787 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700788 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700789 else:
790 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700791 print('repo version %s is current' % rp.work_git.describe(HEAD),
792 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700793
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700794def _VerifyTag(project):
795 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
796 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700797 print('warning: GnuPG was not available during last "repo init"\n'
798 'warning: Cannot automatically authenticate repo."""',
799 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700800 return True
801
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700802 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700803 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700804 except GitError:
805 cur = None
806
807 if not cur \
808 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700809 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700810 if rev.startswith(R_HEADS):
811 rev = rev[len(R_HEADS):]
812
Sarah Owenscecd1d82012-11-01 22:59:27 -0700813 print(file=sys.stderr)
814 print("warning: project '%s' branch '%s' is not signed"
815 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700816 return False
817
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800818 env = os.environ.copy()
819 env['GIT_DIR'] = project.gitdir.encode()
820 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821
822 cmd = [GIT, 'tag', '-v', cur]
823 proc = subprocess.Popen(cmd,
824 stdout = subprocess.PIPE,
825 stderr = subprocess.PIPE,
826 env = env)
827 out = proc.stdout.read()
828 proc.stdout.close()
829
830 err = proc.stderr.read()
831 proc.stderr.close()
832
833 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700834 print(file=sys.stderr)
835 print(out, file=sys.stderr)
836 print(err, file=sys.stderr)
837 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700838 return False
839 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700840
841class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700842 _ALPHA = 0.5
843
Dave Borowitz67700e92012-10-23 15:00:54 -0700844 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100845 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700846 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700847 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700848
849 def Get(self, project):
850 self._Load()
851 return self._times.get(project.name, _ONE_DAY_S)
852
853 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700854 self._Load()
855 name = project.name
856 old = self._times.get(name, t)
857 self._seen.add(name)
858 a = self._ALPHA
859 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700860
861 def _Load(self):
862 if self._times is None:
863 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100864 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700865 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100866 self._times = json.load(f)
867 finally:
868 f.close()
869 except (IOError, ValueError):
870 try:
871 os.remove(self._path)
872 except OSError:
873 pass
874 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700875
876 def Save(self):
877 if self._times is None:
878 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700879
880 to_delete = []
881 for name in self._times:
882 if name not in self._seen:
883 to_delete.append(name)
884 for name in to_delete:
885 del self._times[name]
886
Dave Borowitz67700e92012-10-23 15:00:54 -0700887 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100888 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700889 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100890 json.dump(self._times, f, indent=2)
891 finally:
892 f.close()
893 except (IOError, TypeError):
894 try:
895 os.remove(self._path)
896 except OSError:
897 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700898
899# This is a replacement for xmlrpc.client.Transport using urllib2
900# and supporting persistent-http[s]. It cannot change hosts from
901# request to request like the normal transport, the real url
902# is passed during initialization.
903class PersistentTransport(xmlrpc.client.Transport):
904 def __init__(self, orig_host):
905 self.orig_host = orig_host
906
907 def request(self, host, handler, request_body, verbose=False):
908 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
909 # Python doesn't understand cookies with the #HttpOnly_ prefix
910 # Since we're only using them for HTTP, copy the file temporarily,
911 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700912 if cookiefile:
913 tmpcookiefile = tempfile.NamedTemporaryFile()
914 try:
915 with open(cookiefile) as f:
916 for line in f:
917 if line.startswith("#HttpOnly_"):
918 line = line[len("#HttpOnly_"):]
919 tmpcookiefile.write(line)
920 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700921
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700922 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
923 cookiejar.load()
924 finally:
925 tmpcookiefile.close()
926 else:
927 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700928
929 proxyhandler = urllib.request.ProxyHandler
930 if proxy:
931 proxyhandler = urllib.request.ProxyHandler({
932 "http": proxy,
933 "https": proxy })
934
935 opener = urllib.request.build_opener(
936 urllib.request.HTTPCookieProcessor(cookiejar),
937 proxyhandler)
938
939 url = urllib.parse.urljoin(self.orig_host, handler)
940 parse_results = urllib.parse.urlparse(url)
941
942 scheme = parse_results.scheme
943 if scheme == 'persistent-http':
944 scheme = 'http'
945 if scheme == 'persistent-https':
946 # If we're proxying through persistent-https, use http. The
947 # proxy itself will do the https.
948 if proxy:
949 scheme = 'http'
950 else:
951 scheme = 'https'
952
953 # Parse out any authentication information using the base class
954 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
955
956 url = urllib.parse.urlunparse((
957 scheme,
958 host,
959 parse_results.path,
960 parse_results.params,
961 parse_results.query,
962 parse_results.fragment))
963
964 request = urllib.request.Request(url, request_body)
965 if extra_headers is not None:
966 for (name, header) in extra_headers:
967 request.add_header(name, header)
968 request.add_header('Content-Type', 'text/xml')
969 try:
970 response = opener.open(request)
971 except urllib.error.HTTPError as e:
972 if e.code == 501:
973 # We may have been redirected through a login process
974 # but our POST turned into a GET. Retry.
975 response = opener.open(request)
976 else:
977 raise
978
979 p, u = xmlrpc.client.getparser()
980 while 1:
981 data = response.read(1024)
982 if not data:
983 break
984 p.feed(data)
985 p.close()
986 return u.close()
987
988 def close(self):
989 pass
990