blob: 7bbbe44d1e129d170b45e3b2aa1b24bf785299b8 [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)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700514
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700515 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700516 fd = open(file_path, 'w')
517 try:
518 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700519 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700520 finally:
521 fd.close()
522 return 0
523
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700524 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800525 if opt.jobs:
526 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700527 if self.jobs > 1:
528 soft_limit, _ = _rlimit_nofile()
529 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
530
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700531 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700532 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700533 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700534 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700535 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700536 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500537 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700538 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500539 sys.exit(1)
540 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700541 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500542 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900543 if opt.manifest_server_username or opt.manifest_server_password:
544 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700545 print('error: -u and -p may only be combined with -s or -t',
546 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900547 sys.exit(1)
548 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700549 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900550 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500551
552 if opt.manifest_name:
553 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700554
Chirayu Desaia892b102013-06-11 14:18:46 +0530555 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900556 smart_sync_manifest_name = "smart_sync_override.xml"
557 smart_sync_manifest_path = os.path.join(
558 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530559
Victor Boivie08c880d2011-04-19 10:32:52 +0200560 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700561 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900562 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700563 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700564 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900565
566 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900567 if not opt.quiet:
568 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900569
David Pursehouse86d973d2012-08-24 10:21:02 +0900570 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900571 username = None
572 password = None
573 if opt.manifest_server_username and opt.manifest_server_password:
574 username = opt.manifest_server_username
575 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900576 else:
577 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900578 info = netrc.netrc()
579 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900580 # .netrc file does not exist or could not be opened
581 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900582 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900583 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530584 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900585 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900586 auth = info.authenticators(parse_result.hostname)
587 if auth:
588 username, _account, password = auth
589 else:
590 print('No credentials found for %s in .netrc'
591 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700592 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700593 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900594
595 if (username and password):
596 manifest_server = manifest_server.replace('://', '://%s:%s@' %
natalie.chen3e01e3a2016-07-20 10:45:57 +0800597 (username, url_quote(password, safe='')),
David Pursehousecf76b1b2012-09-14 10:31:42 +0900598 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900599
Dan Willemsen0745bb22015-08-17 13:41:45 -0700600 transport = PersistentTransport(manifest_server)
601 if manifest_server.startswith('persistent-'):
602 manifest_server = manifest_server[len('persistent-'):]
603
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700604 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700605 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200606 if opt.smart_sync:
607 p = self.manifest.manifestProject
608 b = p.GetBranch(p.CurrentBranch)
609 branch = b.merge
610 if branch.startswith(R_HEADS):
611 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700612
Victor Boivie08c880d2011-04-19 10:32:52 +0200613 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700614 if 'SYNC_TARGET' in env:
615 target = env['SYNC_TARGET']
616 [success, manifest_str] = server.GetApprovedManifest(branch, target)
617 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200618 target = '%s-%s' % (env['TARGET_PRODUCT'],
619 env['TARGET_BUILD_VARIANT'])
620 [success, manifest_str] = server.GetApprovedManifest(branch, target)
621 else:
622 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700623 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200624 assert(opt.smart_tag)
625 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700626
627 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900628 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700629 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900630 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700631 try:
632 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700633 finally:
634 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900635 except IOError as e:
636 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900637 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700638 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700639 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100640 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700641 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900642 print('error: manifest server RPC call failed: %s' %
643 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700644 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530645 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700646 print('error: cannot connect to manifest server %s:\n%s'
647 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900648 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530649 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700650 print('error: cannot connect to manifest server %s:\n%d %s'
651 % (self.manifest.manifest_server, e.errcode, e.errmsg),
652 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700653 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900654 else: # Not smart sync or smart tag mode
655 if os.path.isfile(smart_sync_manifest_path):
656 try:
657 os.remove(smart_sync_manifest_path)
658 except OSError as e:
659 print('error: failed to remove existing smart sync override manifest: %s' %
660 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700661
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700662 rp = self.manifest.repoProject
663 rp.PreSync()
664
665 mp = self.manifest.manifestProject
666 mp.PreSync()
667
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800668 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700669 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800670
Nico Sallembien9bb18162009-12-07 15:38:01 -0800671 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700672 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700673 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900674 no_tags=opt.no_tags,
675 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800676
677 if mp.HasChanges:
678 syncbuf = SyncBuffer(mp.config)
679 mp.Sync_LocalHalf(syncbuf)
680 if not syncbuf.Finish():
681 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100682 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700683 if opt.jobs is None:
684 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700685
Simran Basib9a1b732015-08-20 12:19:28 -0700686 if self.gitc_manifest:
687 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700688 missing_ok=True)
689 gitc_projects = []
690 opened_projects = []
691 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700692 if project.relpath in self.gitc_manifest.paths and \
693 self.gitc_manifest.paths[project.relpath].old_revision:
694 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700695 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700696 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700697
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700698 if not args:
699 gitc_projects = None
700
701 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700702 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700703 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
704 if manifest_name:
705 manifest.Override(manifest_name)
706 else:
707 manifest.Override(self.manifest.manifestFile)
708 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
709 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700710 gitc_projects)
711 print('GITC client successfully synced.')
712
713 # The opened projects need to be synced as normal, therefore we
714 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700715 # TODO: make this more reliable -- if there's a project name/path overlap,
716 # this may choose the wrong project.
717 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
718 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700719 if not args:
720 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800721 all_projects = self.GetProjects(args,
722 missing_ok=True,
723 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700724
Dave Borowitz67700e92012-10-23 15:00:54 -0700725 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700726 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700727 to_fetch = []
728 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700729 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700730 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900731 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700732 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700733
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800734 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700735 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700736 if opt.network_only:
737 # bail out now; the rest touches the working tree
738 return
739
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800740 # Iteratively fetch missing and/or nested unregistered submodules
741 previously_missing_set = set()
742 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100743 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800744 all_projects = self.GetProjects(args,
745 missing_ok=True,
746 submodules_ok=opt.fetch_submodules)
747 missing = []
748 for project in all_projects:
749 if project.gitdir not in fetched:
750 missing.append(project)
751 if not missing:
752 break
753 # Stop us from non-stopped fetching actually-missing repos: If set of
754 # missing repos has not been changed from last fetch, we break.
755 missing_set = set(p.name for p in missing)
756 if previously_missing_set == missing_set:
757 break
758 previously_missing_set = missing_set
759 fetched.update(self._Fetch(missing, opt))
760
Julien Campergue335f5ef2013-10-16 11:02:35 +0200761 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700762 # bail out now, we have no working tree
763 return
764
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700765 if self.UpdateProjectList():
766 sys.exit(1)
767
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700768 syncbuf = SyncBuffer(mp.config,
769 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900770 pm = Progress('Syncing work tree', len(all_projects))
771 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700772 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800773 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700774 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700775 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700776 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700777 if not syncbuf.Finish():
778 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700779
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700780 # If there's a notice that's supposed to print at the end of the sync, print
781 # it now...
782 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700783 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700784
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700785def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800786 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700787 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700788 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800789 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700790 if project.Exists:
791 project.PostRepoUpgrade()
792
793def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
794 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700795 print('info: A new version of repo is available', file=sys.stderr)
796 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700797 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700798 syncbuf = SyncBuffer(rp.config)
799 rp.Sync_LocalHalf(syncbuf)
800 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700801 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700802 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700803 raise RepoChangedException(['--repo-upgraded'])
804 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700805 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700806 else:
807 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700808 print('repo version %s is current' % rp.work_git.describe(HEAD),
809 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700810
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811def _VerifyTag(project):
812 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
813 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700814 print('warning: GnuPG was not available during last "repo init"\n'
815 'warning: Cannot automatically authenticate repo."""',
816 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817 return True
818
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700820 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821 except GitError:
822 cur = None
823
824 if not cur \
825 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700826 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700827 if rev.startswith(R_HEADS):
828 rev = rev[len(R_HEADS):]
829
Sarah Owenscecd1d82012-11-01 22:59:27 -0700830 print(file=sys.stderr)
831 print("warning: project '%s' branch '%s' is not signed"
832 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700833 return False
834
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800835 env = os.environ.copy()
836 env['GIT_DIR'] = project.gitdir.encode()
837 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700838
839 cmd = [GIT, 'tag', '-v', cur]
840 proc = subprocess.Popen(cmd,
841 stdout = subprocess.PIPE,
842 stderr = subprocess.PIPE,
843 env = env)
844 out = proc.stdout.read()
845 proc.stdout.close()
846
847 err = proc.stderr.read()
848 proc.stderr.close()
849
850 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700851 print(file=sys.stderr)
852 print(out, file=sys.stderr)
853 print(err, file=sys.stderr)
854 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700855 return False
856 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700857
858class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700859 _ALPHA = 0.5
860
Dave Borowitz67700e92012-10-23 15:00:54 -0700861 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100862 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700863 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700864 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700865
866 def Get(self, project):
867 self._Load()
868 return self._times.get(project.name, _ONE_DAY_S)
869
870 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700871 self._Load()
872 name = project.name
873 old = self._times.get(name, t)
874 self._seen.add(name)
875 a = self._ALPHA
876 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700877
878 def _Load(self):
879 if self._times is None:
880 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100881 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700882 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100883 self._times = json.load(f)
884 finally:
885 f.close()
886 except (IOError, ValueError):
887 try:
888 os.remove(self._path)
889 except OSError:
890 pass
891 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700892
893 def Save(self):
894 if self._times is None:
895 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700896
897 to_delete = []
898 for name in self._times:
899 if name not in self._seen:
900 to_delete.append(name)
901 for name in to_delete:
902 del self._times[name]
903
Dave Borowitz67700e92012-10-23 15:00:54 -0700904 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100905 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700906 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100907 json.dump(self._times, f, indent=2)
908 finally:
909 f.close()
910 except (IOError, TypeError):
911 try:
912 os.remove(self._path)
913 except OSError:
914 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700915
916# This is a replacement for xmlrpc.client.Transport using urllib2
917# and supporting persistent-http[s]. It cannot change hosts from
918# request to request like the normal transport, the real url
919# is passed during initialization.
920class PersistentTransport(xmlrpc.client.Transport):
921 def __init__(self, orig_host):
922 self.orig_host = orig_host
923
924 def request(self, host, handler, request_body, verbose=False):
925 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
926 # Python doesn't understand cookies with the #HttpOnly_ prefix
927 # Since we're only using them for HTTP, copy the file temporarily,
928 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700929 if cookiefile:
930 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900931 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700932 try:
933 with open(cookiefile) as f:
934 for line in f:
935 if line.startswith("#HttpOnly_"):
936 line = line[len("#HttpOnly_"):]
937 tmpcookiefile.write(line)
938 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700939
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700940 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900941 try:
942 cookiejar.load()
943 except cookielib.LoadError:
944 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700945 finally:
946 tmpcookiefile.close()
947 else:
948 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700949
950 proxyhandler = urllib.request.ProxyHandler
951 if proxy:
952 proxyhandler = urllib.request.ProxyHandler({
953 "http": proxy,
954 "https": proxy })
955
956 opener = urllib.request.build_opener(
957 urllib.request.HTTPCookieProcessor(cookiejar),
958 proxyhandler)
959
960 url = urllib.parse.urljoin(self.orig_host, handler)
961 parse_results = urllib.parse.urlparse(url)
962
963 scheme = parse_results.scheme
964 if scheme == 'persistent-http':
965 scheme = 'http'
966 if scheme == 'persistent-https':
967 # If we're proxying through persistent-https, use http. The
968 # proxy itself will do the https.
969 if proxy:
970 scheme = 'http'
971 else:
972 scheme = 'https'
973
974 # Parse out any authentication information using the base class
975 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
976
977 url = urllib.parse.urlunparse((
978 scheme,
979 host,
980 parse_results.path,
981 parse_results.params,
982 parse_results.query,
983 parse_results.fragment))
984
985 request = urllib.request.Request(url, request_body)
986 if extra_headers is not None:
987 for (name, header) in extra_headers:
988 request.add_header(name, header)
989 request.add_header('Content-Type', 'text/xml')
990 try:
991 response = opener.open(request)
992 except urllib.error.HTTPError as e:
993 if e.code == 501:
994 # We may have been redirected through a login process
995 # but our POST turned into a GET. Retry.
996 response = opener.open(request)
997 else:
998 raise
999
1000 p, u = xmlrpc.client.getparser()
1001 while 1:
1002 data = response.read(1024)
1003 if not data:
1004 break
1005 p.feed(data)
1006 p.close()
1007 return u.close()
1008
1009 def close(self):
1010 pass
1011