blob: 6c2f320582af169b2f147cd69ba90f97781e4110 [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
natalie.chen3e01e3a2016-07-20 10:45:57 +080029from urllib import quote as url_quote
30
David Pursehouse59bbb582013-05-17 10:49:33 +090031from pyversion import is_python3
32if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070033 import http.cookiejar as cookielib
34 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053035 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070036 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090037 import xmlrpc.client
38else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070041 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090043 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053044 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053046 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070047 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053048 xmlrpc = imp.new_module('xmlrpc')
49 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070050
Roy Lee18afd7f2010-05-09 04:32:08 +080051try:
52 import threading as _threading
53except ImportError:
54 import dummy_threading as _threading
55
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070056try:
57 import resource
58 def _rlimit_nofile():
59 return resource.getrlimit(resource.RLIMIT_NOFILE)
60except ImportError:
61 def _rlimit_nofile():
62 return (256, 256)
63
Dave Borowitz18857212012-10-23 17:02:59 -070064try:
65 import multiprocessing
66except ImportError:
67 multiprocessing = None
68
Dave Borowitze2152672012-10-31 12:24:38 -070069from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090070from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090071from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070072import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070073from project import Project
74from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080075from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000076from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070077from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070078from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080079from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070080from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081
Dave Borowitz67700e92012-10-23 15:00:54 -070082_ONE_DAY_S = 24 * 60 * 60
83
Doug Andersonfc06ced2011-03-16 15:49:18 -070084class _FetchError(Exception):
85 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
86 pass
87
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080088class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080089 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070090 common = True
91 helpSummary = "Update working tree to the latest revision"
92 helpUsage = """
93%prog [<project>...]
94"""
95 helpDescription = """
96The '%prog' command synchronizes local project directories
97with the remote repositories specified in the manifest. If a local
98project does not yet exist, it will clone a new local directory from
99the remote repository and set up tracking branches as specified in
100the manifest. If the local project already exists, '%prog'
101will update the remote branches and rebase any new local changes
102on top of the new remote changes.
103
104'%prog' will synchronize all projects listed at the command
105line. Projects can be specified either by name, or by a relative
106or absolute path to the project's local directory. If no projects
107are specified, '%prog' will synchronize all projects listed in
108the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700109
110The -d/--detach option can be used to switch specified projects
111back to the manifest revision. This option is especially helpful
112if the project is currently on a topic branch, but the manifest
113revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700114
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700115The -s/--smart-sync option can be used to sync to a known good
116build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200117manifest. The -t/--smart-tag option is similar and allows you to
118specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700119
David Pursehousecf76b1b2012-09-14 10:31:42 +0900120The -u/--manifest-server-username and -p/--manifest-server-password
121options can be used to specify a username and password to authenticate
122with the manifest server when using the -s or -t option.
123
124If -u and -p are not specified when using the -s or -t option, '%prog'
125will attempt to read authentication credentials for the manifest server
126from the user's .netrc file.
127
128'%prog' will not use authentication credentials from -u/-p or .netrc
129if the manifest server specified in the manifest file already includes
130credentials.
131
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500132The -f/--force-broken option can be used to proceed with syncing
133other projects if a project sync fails.
134
Kevin Degiabaa7f32014-11-12 11:27:45 -0700135The --force-sync option can be used to overwrite existing git
136directories if they have previously been linked to a different
137object direcotry. WARNING: This may cause data to be lost since
138refs may be removed when overwriting.
139
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700140The --no-clone-bundle option disables any attempt to use
141$URL/clone.bundle to bootstrap a new Git repository from a
142resumeable bundle file on a content delivery network. This
143may be necessary if there are problems with the local Python
144HTTP client or proxy configuration, but the Git binary works.
145
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800146The --fetch-submodules option enables fetching Git submodules
147of a project from server.
148
David Pursehousef2fad612015-01-29 14:36:28 +0900149The -c/--current-branch option can be used to only fetch objects that
150are on the branch specified by a project's revision.
151
David Pursehouseb1553542014-09-04 21:28:09 +0900152The --optimized-fetch option can be used to only fetch projects that
153are fixed to a sha1 revision if the sha1 revision does not already
154exist locally.
155
David Pursehouse74cfd272015-10-14 10:50:15 +0900156The --prune option can be used to remove any refs that no longer
157exist on the remote.
158
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700159SSH Connections
160---------------
161
162If at least one project remote URL uses an SSH connection (ssh://,
163git+ssh://, or user@host:path syntax) repo will automatically
164enable the SSH ControlMaster option when connecting to that host.
165This feature permits other projects in the same '%prog' session to
166reuse the same SSH tunnel, saving connection setup overheads.
167
168To disable this behavior on UNIX platforms, set the GIT_SSH
169environment variable to 'ssh'. For example:
170
171 export GIT_SSH=ssh
172 %prog
173
174Compatibility
175~~~~~~~~~~~~~
176
177This feature is automatically disabled on Windows, due to the lack
178of UNIX domain socket support.
179
180This feature is not compatible with url.insteadof rewrites in the
181user's ~/.gitconfig. '%prog' is currently not able to perform the
182rewrite early enough to establish the ControlMaster tunnel.
183
184If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
185later is required to fix a server side protocol bug.
186
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700187"""
188
Nico Sallembien6623b212010-05-11 12:57:01 -0700189 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000190 try:
191 self.jobs = self.manifest.default.sync_j
192 except ManifestParseError:
193 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700194
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500195 p.add_option('-f', '--force-broken',
196 dest='force_broken', action='store_true',
197 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700198 p.add_option('--force-sync',
199 dest='force_sync', action='store_true',
200 help="overwrite an existing git directory if it needs to "
201 "point to a different object directory. WARNING: this "
202 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900203 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700204 dest='local_only', action='store_true',
205 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900206 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700207 dest='network_only', action='store_true',
208 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700210 dest='detach_head', action='store_true',
211 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700213 dest='current_branch_only', action='store_true',
214 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700216 dest='quiet', action='store_true',
217 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900218 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800219 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700220 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500221 p.add_option('-m', '--manifest-name',
222 dest='manifest_name',
223 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700224 p.add_option('--no-clone-bundle',
225 dest='no_clone_bundle', action='store_true',
226 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800227 p.add_option('-u', '--manifest-server-username', action='store',
228 dest='manifest_server_username',
229 help='username to authenticate with the manifest server')
230 p.add_option('-p', '--manifest-server-password', action='store',
231 dest='manifest_server_password',
232 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800233 p.add_option('--fetch-submodules',
234 dest='fetch_submodules', action='store_true',
235 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700236 p.add_option('--no-tags',
237 dest='no_tags', action='store_true',
238 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900239 p.add_option('--optimized-fetch',
240 dest='optimized_fetch', action='store_true',
241 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900242 p.add_option('--prune', dest='prune', action='store_true',
243 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700244 if show_smart:
245 p.add_option('-s', '--smart-sync',
246 dest='smart_sync', action='store_true',
247 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200248 p.add_option('-t', '--smart-tag',
249 dest='smart_tag', action='store',
250 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700251
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700252 g = p.add_option_group('repo Version options')
253 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700254 dest='no_repo_verify', action='store_true',
255 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700256 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800257 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700258 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700259
natalie.chen451dca82015-12-09 21:24:16 +0800260 def _FetchProjectList(self, opt, projects, lock, fetched, pm, sem, err_event):
David Pursehousec1b86a22012-11-14 11:36:51 +0900261 """Main function of the fetch threads when jobs are > 1.
Doug Andersonfc06ced2011-03-16 15:49:18 -0700262
David James8d201162013-10-11 17:03:19 -0700263 Delegates most of the work to _FetchHelper.
264
265 Args:
266 opt: Program options returned from optparse. See _Options().
267 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800268 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700269 _FetchHelper docstring for details.
270 """
natalie.chen451dca82015-12-09 21:24:16 +0800271 try:
272 for project in projects:
273 success = self._FetchHelper(opt, project, lock, fetched, pm, sem, err_event)
274 if not success and not opt.force_broken:
275 break
276 finally:
277 sem.release()
David James8d201162013-10-11 17:03:19 -0700278
279 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
280 """Fetch git objects for a single project.
281
David Pursehousec1b86a22012-11-14 11:36:51 +0900282 Args:
283 opt: Program options returned from optparse. See _Options().
284 project: Project object for the project to fetch.
285 lock: Lock for accessing objects that are shared amongst multiple
286 _FetchHelper() threads.
287 fetched: set object that we will add project.gitdir to when we're done
288 (with our lock held).
289 pm: Instance of a Project object. We will call pm.update() (with our
290 lock held).
291 sem: We'll release() this semaphore when we exit so that another thread
292 can be started up.
293 err_event: We'll set this event in the case of an error (after printing
294 out info about the error).
David James8d201162013-10-11 17:03:19 -0700295
296 Returns:
297 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900298 """
299 # We'll set to true once we've locked the lock.
300 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700301
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530302 if not opt.quiet:
303 print('Fetching project %s' % project.name)
304
David Pursehousec1b86a22012-11-14 11:36:51 +0900305 # Encapsulate everything in a try/except/finally so that:
306 # - We always set err_event in the case of an exception.
307 # - We always make sure we call sem.release().
308 # - We always make sure we unlock the lock if we locked it.
309 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700310 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900311 start = time.time()
312 success = project.Sync_NetworkHalf(
313 quiet=opt.quiet,
314 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700315 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700316 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900317 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900318 optimized_fetch=opt.optimized_fetch,
319 prune=opt.prune)
David Pursehousec1b86a22012-11-14 11:36:51 +0900320 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700321
David Pursehousec1b86a22012-11-14 11:36:51 +0900322 # Lock around all the rest of the code, since printing, updating a set
323 # and Progress.update() are not thread safe.
324 lock.acquire()
325 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700326
David Pursehousec1b86a22012-11-14 11:36:51 +0900327 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800328 err_event.set()
David Pursehousec1b86a22012-11-14 11:36:51 +0900329 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
330 if opt.force_broken:
331 print('warn: --force-broken, continuing to sync',
332 file=sys.stderr)
333 else:
334 raise _FetchError()
Roy Lee18afd7f2010-05-09 04:32:08 +0800335
David Pursehousec1b86a22012-11-14 11:36:51 +0900336 fetched.add(project.gitdir)
337 pm.update()
338 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800339 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400340 except Exception as e:
341 print('error: Cannot fetch %s (%s: %s)' \
342 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900343 err_event.set()
344 raise
345 finally:
346 if did_lock:
347 lock.release()
natalie.chen451dca82015-12-09 21:24:16 +0800348 #sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800349
David James8d201162013-10-11 17:03:19 -0700350 return success
351
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700352 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700353 fetched = set()
David James89ece422014-01-09 18:51:58 -0800354 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700355 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800356
David James89ece422014-01-09 18:51:58 -0800357 objdir_project_map = dict()
358 for project in projects:
359 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700360
David James89ece422014-01-09 18:51:58 -0800361 threads = set()
362 sem = _threading.Semaphore(self.jobs)
363 err_event = _threading.Event()
364 for project_list in objdir_project_map.values():
365 # Check for any errors before running any more tasks.
366 # ...we'll let existing threads finish, though.
367 if err_event.isSet() and not opt.force_broken:
368 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700369
David James89ece422014-01-09 18:51:58 -0800370 sem.acquire()
371 kwargs = dict(opt=opt,
372 projects=project_list,
373 lock=lock,
374 fetched=fetched,
375 pm=pm,
376 sem=sem,
377 err_event=err_event)
378 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700379 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800380 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200381 # Ensure that Ctrl-C will not freeze the repo process.
382 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800383 threads.add(t)
384 t.start()
David James89ece422014-01-09 18:51:58 -0800385 else:
386 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800387
David James89ece422014-01-09 18:51:58 -0800388 for t in threads:
389 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800390
David James89ece422014-01-09 18:51:58 -0800391 # If we saw an error, exit with code 1 so that other scripts can check.
392 if err_event.isSet():
393 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
394 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700395
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700396 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700397 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700398
Julien Campergue335f5ef2013-10-16 11:02:35 +0200399 if not self.manifest.IsArchive:
400 self._GCProjects(projects)
401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700402 return fetched
403
Dave Borowitz18857212012-10-23 17:02:59 -0700404 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700405 gitdirs = {}
406 for project in projects:
407 gitdirs[project.gitdir] = project.bare_git
408
Dave Borowitze2152672012-10-31 12:24:38 -0700409 has_dash_c = git_require((1, 7, 2))
410 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700411 cpu_count = multiprocessing.cpu_count()
412 else:
413 cpu_count = 1
414 jobs = min(self.jobs, cpu_count)
415
416 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700417 for bare_git in gitdirs.values():
418 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700419 return
420
421 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
422
423 threads = set()
424 sem = _threading.Semaphore(jobs)
425 err_event = _threading.Event()
426
David James8d201162013-10-11 17:03:19 -0700427 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700428 try:
429 try:
David James8d201162013-10-11 17:03:19 -0700430 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700431 except GitError:
432 err_event.set()
433 except:
434 err_event.set()
435 raise
436 finally:
437 sem.release()
438
David James8d201162013-10-11 17:03:19 -0700439 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700440 if err_event.isSet():
441 break
442 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700443 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700444 t.daemon = True
445 threads.add(t)
446 t.start()
447
448 for t in threads:
449 t.join()
450
451 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700452 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700453 sys.exit(1)
454
Tim Kilbourn07669002013-03-08 15:02:49 -0800455 def _ReloadManifest(self, manifest_name=None):
456 if manifest_name:
457 # Override calls _Unload already
458 self.manifest.Override(manifest_name)
459 else:
460 self.manifest._Unload()
461
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700462 def UpdateProjectList(self):
463 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700464 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700465 if project.relpath:
466 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700467 file_name = 'project.list'
468 file_path = os.path.join(self.manifest.repodir, file_name)
469 old_project_paths = []
470
471 if os.path.exists(file_path):
472 fd = open(file_path, 'r')
473 try:
474 old_project_paths = fd.read().split('\n')
475 finally:
476 fd.close()
477 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700478 if not path:
479 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700480 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900481 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400482 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700483 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900484 project = Project(
485 manifest = self.manifest,
486 name = path,
487 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700488 gitdir = gitdir,
489 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900490 worktree = os.path.join(self.manifest.topdir, path),
491 relpath = path,
492 revisionExpr = 'HEAD',
493 revisionId = None,
494 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400495
David Pursehousec1b86a22012-11-14 11:36:51 +0900496 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900497 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900498 'are present' % project.relpath, file=sys.stderr)
499 print(' commit changes, then run sync again',
500 file=sys.stderr)
501 return -1
502 else:
503 print('Deleting obsolete path %s' % project.worktree,
504 file=sys.stderr)
505 shutil.rmtree(project.worktree)
506 # Try deleting parent subdirs if they are empty
507 project_dir = os.path.dirname(project.worktree)
508 while project_dir != self.manifest.topdir:
509 try:
510 os.rmdir(project_dir)
511 except OSError:
512 break
513 project_dir = os.path.dirname(project_dir)
natalie.chen0c367f52017-05-19 17:01:37 +0800514 project.RemoveOldCopyAndLinkFiles(os.path.join(self.manifest.repodir, 'projects', '%s.git' % path))
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700515
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700516 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700517 fd = open(file_path, 'w')
518 try:
519 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700520 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700521 finally:
522 fd.close()
523 return 0
524
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700525 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800526 if opt.jobs:
527 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700528 if self.jobs > 1:
529 soft_limit, _ = _rlimit_nofile()
530 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
531
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700532 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700533 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700534 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700535 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700536 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700537 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500538 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700539 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500540 sys.exit(1)
541 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700542 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500543 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900544 if opt.manifest_server_username or opt.manifest_server_password:
545 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700546 print('error: -u and -p may only be combined with -s or -t',
547 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900548 sys.exit(1)
549 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700550 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900551 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500552
553 if opt.manifest_name:
554 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700555
Chirayu Desaia892b102013-06-11 14:18:46 +0530556 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900557 smart_sync_manifest_name = "smart_sync_override.xml"
558 smart_sync_manifest_path = os.path.join(
559 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530560
Victor Boivie08c880d2011-04-19 10:32:52 +0200561 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700562 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900563 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700564 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700565 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900566
567 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900568 if not opt.quiet:
569 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900570
David Pursehouse86d973d2012-08-24 10:21:02 +0900571 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900572 username = None
573 password = None
574 if opt.manifest_server_username and opt.manifest_server_password:
575 username = opt.manifest_server_username
576 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900577 else:
578 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900579 info = netrc.netrc()
580 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900581 # .netrc file does not exist or could not be opened
582 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900583 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900584 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530585 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900586 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900587 auth = info.authenticators(parse_result.hostname)
588 if auth:
589 username, _account, password = auth
590 else:
591 print('No credentials found for %s in .netrc'
592 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700593 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700594 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900595
596 if (username and password):
597 manifest_server = manifest_server.replace('://', '://%s:%s@' %
natalie.chen3e01e3a2016-07-20 10:45:57 +0800598 (username, url_quote(password, safe='')),
David Pursehousecf76b1b2012-09-14 10:31:42 +0900599 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900600
Dan Willemsen0745bb22015-08-17 13:41:45 -0700601 transport = PersistentTransport(manifest_server)
602 if manifest_server.startswith('persistent-'):
603 manifest_server = manifest_server[len('persistent-'):]
604
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700605 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700606 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200607 if opt.smart_sync:
608 p = self.manifest.manifestProject
609 b = p.GetBranch(p.CurrentBranch)
610 branch = b.merge
611 if branch.startswith(R_HEADS):
612 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700613
Victor Boivie08c880d2011-04-19 10:32:52 +0200614 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700615 if 'SYNC_TARGET' in env:
616 target = env['SYNC_TARGET']
617 [success, manifest_str] = server.GetApprovedManifest(branch, target)
618 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200619 target = '%s-%s' % (env['TARGET_PRODUCT'],
620 env['TARGET_BUILD_VARIANT'])
621 [success, manifest_str] = server.GetApprovedManifest(branch, target)
622 else:
623 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700624 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200625 assert(opt.smart_tag)
626 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700627
628 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900629 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700630 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900631 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700632 try:
633 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700634 finally:
635 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900636 except IOError as e:
637 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900638 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700639 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700640 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100641 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700642 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900643 print('error: manifest server RPC call failed: %s' %
644 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700645 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530646 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700647 print('error: cannot connect to manifest server %s:\n%s'
648 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900649 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530650 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700651 print('error: cannot connect to manifest server %s:\n%d %s'
652 % (self.manifest.manifest_server, e.errcode, e.errmsg),
653 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700654 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900655 else: # Not smart sync or smart tag mode
656 if os.path.isfile(smart_sync_manifest_path):
657 try:
658 os.remove(smart_sync_manifest_path)
659 except OSError as e:
660 print('error: failed to remove existing smart sync override manifest: %s' %
661 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700662
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700663 rp = self.manifest.repoProject
664 rp.PreSync()
665
666 mp = self.manifest.manifestProject
667 mp.PreSync()
668
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800669 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700670 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800671
Nico Sallembien9bb18162009-12-07 15:38:01 -0800672 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700673 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700674 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900675 no_tags=opt.no_tags,
676 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800677
678 if mp.HasChanges:
679 syncbuf = SyncBuffer(mp.config)
680 mp.Sync_LocalHalf(syncbuf)
681 if not syncbuf.Finish():
682 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100683 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700684 if opt.jobs is None:
685 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700686
Simran Basib9a1b732015-08-20 12:19:28 -0700687 if self.gitc_manifest:
688 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700689 missing_ok=True)
690 gitc_projects = []
691 opened_projects = []
692 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700693 if project.relpath in self.gitc_manifest.paths and \
694 self.gitc_manifest.paths[project.relpath].old_revision:
695 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700696 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700697 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700698
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700699 if not args:
700 gitc_projects = None
701
702 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700703 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700704 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
705 if manifest_name:
706 manifest.Override(manifest_name)
707 else:
708 manifest.Override(self.manifest.manifestFile)
709 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
710 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700711 gitc_projects)
712 print('GITC client successfully synced.')
713
714 # The opened projects need to be synced as normal, therefore we
715 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700716 # TODO: make this more reliable -- if there's a project name/path overlap,
717 # this may choose the wrong project.
718 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
719 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700720 if not args:
721 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800722 all_projects = self.GetProjects(args,
723 missing_ok=True,
724 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700725
Dave Borowitz67700e92012-10-23 15:00:54 -0700726 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700727 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700728 to_fetch = []
729 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700730 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700731 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900732 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700733 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700734
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800735 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700736 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700737 if opt.network_only:
738 # bail out now; the rest touches the working tree
739 return
740
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800741 # Iteratively fetch missing and/or nested unregistered submodules
742 previously_missing_set = set()
743 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100744 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800745 all_projects = self.GetProjects(args,
746 missing_ok=True,
747 submodules_ok=opt.fetch_submodules)
748 missing = []
749 for project in all_projects:
750 if project.gitdir not in fetched:
751 missing.append(project)
752 if not missing:
753 break
754 # Stop us from non-stopped fetching actually-missing repos: If set of
755 # missing repos has not been changed from last fetch, we break.
756 missing_set = set(p.name for p in missing)
757 if previously_missing_set == missing_set:
758 break
759 previously_missing_set = missing_set
760 fetched.update(self._Fetch(missing, opt))
761
Julien Campergue335f5ef2013-10-16 11:02:35 +0200762 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700763 # bail out now, we have no working tree
764 return
765
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700766 if self.UpdateProjectList():
767 sys.exit(1)
768
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700769 syncbuf = SyncBuffer(mp.config,
770 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900771 pm = Progress('Syncing work tree', len(all_projects))
772 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700773 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800774 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700775 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700776 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700777 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700778 if not syncbuf.Finish():
779 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700781 # If there's a notice that's supposed to print at the end of the sync, print
782 # it now...
783 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700784 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700785
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700786def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800787 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700788 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700789 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800790 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700791 if project.Exists:
792 project.PostRepoUpgrade()
793
794def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
795 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700796 print('info: A new version of repo is available', file=sys.stderr)
797 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700798 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700799 syncbuf = SyncBuffer(rp.config)
800 rp.Sync_LocalHalf(syncbuf)
801 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700802 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700803 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700804 raise RepoChangedException(['--repo-upgraded'])
805 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700806 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700807 else:
808 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700809 print('repo version %s is current' % rp.work_git.describe(HEAD),
810 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700811
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812def _VerifyTag(project):
813 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
814 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700815 print('warning: GnuPG was not available during last "repo init"\n'
816 'warning: Cannot automatically authenticate repo."""',
817 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700818 return True
819
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700821 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822 except GitError:
823 cur = None
824
825 if not cur \
826 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700827 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700828 if rev.startswith(R_HEADS):
829 rev = rev[len(R_HEADS):]
830
Sarah Owenscecd1d82012-11-01 22:59:27 -0700831 print(file=sys.stderr)
832 print("warning: project '%s' branch '%s' is not signed"
833 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834 return False
835
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800836 env = os.environ.copy()
837 env['GIT_DIR'] = project.gitdir.encode()
838 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700839
840 cmd = [GIT, 'tag', '-v', cur]
841 proc = subprocess.Popen(cmd,
842 stdout = subprocess.PIPE,
843 stderr = subprocess.PIPE,
844 env = env)
845 out = proc.stdout.read()
846 proc.stdout.close()
847
848 err = proc.stderr.read()
849 proc.stderr.close()
850
851 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700852 print(file=sys.stderr)
853 print(out, file=sys.stderr)
854 print(err, file=sys.stderr)
855 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700856 return False
857 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700858
859class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700860 _ALPHA = 0.5
861
Dave Borowitz67700e92012-10-23 15:00:54 -0700862 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100863 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700864 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700865 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700866
867 def Get(self, project):
868 self._Load()
869 return self._times.get(project.name, _ONE_DAY_S)
870
871 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700872 self._Load()
873 name = project.name
874 old = self._times.get(name, t)
875 self._seen.add(name)
876 a = self._ALPHA
877 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700878
879 def _Load(self):
880 if self._times is None:
881 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100882 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700883 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100884 self._times = json.load(f)
885 finally:
886 f.close()
887 except (IOError, ValueError):
888 try:
889 os.remove(self._path)
890 except OSError:
891 pass
892 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700893
894 def Save(self):
895 if self._times is None:
896 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700897
898 to_delete = []
899 for name in self._times:
900 if name not in self._seen:
901 to_delete.append(name)
902 for name in to_delete:
903 del self._times[name]
904
Dave Borowitz67700e92012-10-23 15:00:54 -0700905 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100906 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700907 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100908 json.dump(self._times, f, indent=2)
909 finally:
910 f.close()
911 except (IOError, TypeError):
912 try:
913 os.remove(self._path)
914 except OSError:
915 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700916
917# This is a replacement for xmlrpc.client.Transport using urllib2
918# and supporting persistent-http[s]. It cannot change hosts from
919# request to request like the normal transport, the real url
920# is passed during initialization.
921class PersistentTransport(xmlrpc.client.Transport):
922 def __init__(self, orig_host):
923 self.orig_host = orig_host
924
925 def request(self, host, handler, request_body, verbose=False):
926 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
927 # Python doesn't understand cookies with the #HttpOnly_ prefix
928 # Since we're only using them for HTTP, copy the file temporarily,
929 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700930 if cookiefile:
931 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900932 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700933 try:
934 with open(cookiefile) as f:
935 for line in f:
936 if line.startswith("#HttpOnly_"):
937 line = line[len("#HttpOnly_"):]
938 tmpcookiefile.write(line)
939 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700940
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700941 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900942 try:
943 cookiejar.load()
944 except cookielib.LoadError:
945 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700946 finally:
947 tmpcookiefile.close()
948 else:
949 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700950
951 proxyhandler = urllib.request.ProxyHandler
952 if proxy:
953 proxyhandler = urllib.request.ProxyHandler({
954 "http": proxy,
955 "https": proxy })
956
957 opener = urllib.request.build_opener(
958 urllib.request.HTTPCookieProcessor(cookiejar),
959 proxyhandler)
960
961 url = urllib.parse.urljoin(self.orig_host, handler)
962 parse_results = urllib.parse.urlparse(url)
963
964 scheme = parse_results.scheme
965 if scheme == 'persistent-http':
966 scheme = 'http'
967 if scheme == 'persistent-https':
968 # If we're proxying through persistent-https, use http. The
969 # proxy itself will do the https.
970 if proxy:
971 scheme = 'http'
972 else:
973 scheme = 'https'
974
975 # Parse out any authentication information using the base class
976 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
977
978 url = urllib.parse.urlunparse((
979 scheme,
980 host,
981 parse_results.path,
982 parse_results.params,
983 parse_results.query,
984 parse_results.fragment))
985
986 request = urllib.request.Request(url, request_body)
987 if extra_headers is not None:
988 for (name, header) in extra_headers:
989 request.add_header(name, header)
990 request.add_header('Content-Type', 'text/xml')
991 try:
992 response = opener.open(request)
993 except urllib.error.HTTPError as e:
994 if e.code == 501:
995 # We may have been redirected through a login process
996 # but our POST turned into a GET. Retry.
997 response = opener.open(request)
998 else:
999 raise
1000
1001 p, u = xmlrpc.client.getparser()
1002 while 1:
1003 data = response.read(1024)
1004 if not data:
1005 break
1006 p.feed(data)
1007 p.close()
1008 return u.close()
1009
1010 def close(self):
1011 pass
1012