blob: ecf2ffc0e352953e259f423fe09e9cb53acaf345 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001#
2# Copyright (C) 2008 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
Sarah Owenscecd1d82012-11-01 22:59:27 -070016from __future__ import print_function
Anthony King85b24ac2014-05-06 15:57:48 +010017import json
David Pursehouse86d973d2012-08-24 10:21:02 +090018import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070019from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import os
21import re
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070022import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import subprocess
25import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
David Pursehouse59bbb582013-05-17 10:49:33 +090028
29from pyversion import is_python3
30if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070031 import http.cookiejar as cookielib
32 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053033 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070034 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090035 import xmlrpc.client
36else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070037 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053038 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070039 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053040 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090041 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053042 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070043 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053044 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070045 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053046 xmlrpc = imp.new_module('xmlrpc')
47 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070048
Roy Lee18afd7f2010-05-09 04:32:08 +080049try:
50 import threading as _threading
51except ImportError:
52 import dummy_threading as _threading
53
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070054try:
55 import resource
56 def _rlimit_nofile():
57 return resource.getrlimit(resource.RLIMIT_NOFILE)
58except ImportError:
59 def _rlimit_nofile():
60 return (256, 256)
61
Dave Borowitz18857212012-10-23 17:02:59 -070062try:
63 import multiprocessing
64except ImportError:
65 multiprocessing = None
66
Dave Borowitze2152672012-10-31 12:24:38 -070067from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090068from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090069from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070070import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070071from project import Project
72from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080073from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000074from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070075from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070076from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080077from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070078from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070079
Dave Borowitz67700e92012-10-23 15:00:54 -070080_ONE_DAY_S = 24 * 60 * 60
81
Doug Andersonfc06ced2011-03-16 15:49:18 -070082class _FetchError(Exception):
83 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
84 pass
85
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080086class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080087 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070088 common = True
89 helpSummary = "Update working tree to the latest revision"
90 helpUsage = """
91%prog [<project>...]
92"""
93 helpDescription = """
94The '%prog' command synchronizes local project directories
95with the remote repositories specified in the manifest. If a local
96project does not yet exist, it will clone a new local directory from
97the remote repository and set up tracking branches as specified in
98the manifest. If the local project already exists, '%prog'
99will update the remote branches and rebase any new local changes
100on top of the new remote changes.
101
102'%prog' will synchronize all projects listed at the command
103line. Projects can be specified either by name, or by a relative
104or absolute path to the project's local directory. If no projects
105are specified, '%prog' will synchronize all projects listed in
106the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700107
108The -d/--detach option can be used to switch specified projects
109back to the manifest revision. This option is especially helpful
110if the project is currently on a topic branch, but the manifest
111revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700112
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700113The -s/--smart-sync option can be used to sync to a known good
114build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200115manifest. The -t/--smart-tag option is similar and allows you to
116specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700117
David Pursehousecf76b1b2012-09-14 10:31:42 +0900118The -u/--manifest-server-username and -p/--manifest-server-password
119options can be used to specify a username and password to authenticate
120with the manifest server when using the -s or -t option.
121
122If -u and -p are not specified when using the -s or -t option, '%prog'
123will attempt to read authentication credentials for the manifest server
124from the user's .netrc file.
125
126'%prog' will not use authentication credentials from -u/-p or .netrc
127if the manifest server specified in the manifest file already includes
128credentials.
129
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500130The -f/--force-broken option can be used to proceed with syncing
131other projects if a project sync fails.
132
Kevin Degiabaa7f32014-11-12 11:27:45 -0700133The --force-sync option can be used to overwrite existing git
134directories if they have previously been linked to a different
135object direcotry. WARNING: This may cause data to be lost since
136refs may be removed when overwriting.
137
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700138The --no-clone-bundle option disables any attempt to use
139$URL/clone.bundle to bootstrap a new Git repository from a
140resumeable bundle file on a content delivery network. This
141may be necessary if there are problems with the local Python
142HTTP client or proxy configuration, but the Git binary works.
143
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800144The --fetch-submodules option enables fetching Git submodules
145of a project from server.
146
David Pursehousef2fad612015-01-29 14:36:28 +0900147The -c/--current-branch option can be used to only fetch objects that
148are on the branch specified by a project's revision.
149
David Pursehouseb1553542014-09-04 21:28:09 +0900150The --optimized-fetch option can be used to only fetch projects that
151are fixed to a sha1 revision if the sha1 revision does not already
152exist locally.
153
David Pursehouse74cfd272015-10-14 10:50:15 +0900154The --prune option can be used to remove any refs that no longer
155exist on the remote.
156
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700157SSH Connections
158---------------
159
160If at least one project remote URL uses an SSH connection (ssh://,
161git+ssh://, or user@host:path syntax) repo will automatically
162enable the SSH ControlMaster option when connecting to that host.
163This feature permits other projects in the same '%prog' session to
164reuse the same SSH tunnel, saving connection setup overheads.
165
166To disable this behavior on UNIX platforms, set the GIT_SSH
167environment variable to 'ssh'. For example:
168
169 export GIT_SSH=ssh
170 %prog
171
172Compatibility
173~~~~~~~~~~~~~
174
175This feature is automatically disabled on Windows, due to the lack
176of UNIX domain socket support.
177
178This feature is not compatible with url.insteadof rewrites in the
179user's ~/.gitconfig. '%prog' is currently not able to perform the
180rewrite early enough to establish the ControlMaster tunnel.
181
182If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
183later is required to fix a server side protocol bug.
184
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700185"""
186
Nico Sallembien6623b212010-05-11 12:57:01 -0700187 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000188 try:
189 self.jobs = self.manifest.default.sync_j
190 except ManifestParseError:
191 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700192
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500193 p.add_option('-f', '--force-broken',
194 dest='force_broken', action='store_true',
195 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700196 p.add_option('--force-sync',
197 dest='force_sync', action='store_true',
198 help="overwrite an existing git directory if it needs to "
199 "point to a different object directory. WARNING: this "
200 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900201 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700202 dest='local_only', action='store_true',
203 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700205 dest='network_only', action='store_true',
206 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900207 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700208 dest='detach_head', action='store_true',
209 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700211 dest='current_branch_only', action='store_true',
212 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700214 dest='quiet', action='store_true',
215 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900216 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800217 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700218 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500219 p.add_option('-m', '--manifest-name',
220 dest='manifest_name',
221 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700222 p.add_option('--no-clone-bundle',
223 dest='no_clone_bundle', action='store_true',
224 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800225 p.add_option('-u', '--manifest-server-username', action='store',
226 dest='manifest_server_username',
227 help='username to authenticate with the manifest server')
228 p.add_option('-p', '--manifest-server-password', action='store',
229 dest='manifest_server_password',
230 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800231 p.add_option('--fetch-submodules',
232 dest='fetch_submodules', action='store_true',
233 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700234 p.add_option('--no-tags',
235 dest='no_tags', action='store_true',
236 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900237 p.add_option('--optimized-fetch',
238 dest='optimized_fetch', action='store_true',
239 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900240 p.add_option('--prune', dest='prune', action='store_true',
241 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700242 if show_smart:
243 p.add_option('-s', '--smart-sync',
244 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900245 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200246 p.add_option('-t', '--smart-tag',
247 dest='smart_tag', action='store',
248 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700249
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700250 g = p.add_option_group('repo Version options')
251 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252 dest='no_repo_verify', action='store_true',
253 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700254 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800255 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700256 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700257
David James89ece422014-01-09 18:51:58 -0800258 def _FetchProjectList(self, opt, projects, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900259 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800260
David James8d201162013-10-11 17:03:19 -0700261 Delegates most of the work to _FetchHelper.
262
263 Args:
264 opt: Program options returned from optparse. See _Options().
265 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800266 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700267 _FetchHelper docstring for details.
268 """
269 for project in projects:
David James89ece422014-01-09 18:51:58 -0800270 success = self._FetchHelper(opt, project, *args, **kwargs)
David James8d201162013-10-11 17:03:19 -0700271 if not success and not opt.force_broken:
272 break
273
274 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
275 """Fetch git objects for a single project.
276
David Pursehousec1b86a22012-11-14 11:36:51 +0900277 Args:
278 opt: Program options returned from optparse. See _Options().
279 project: Project object for the project to fetch.
280 lock: Lock for accessing objects that are shared amongst multiple
281 _FetchHelper() threads.
282 fetched: set object that we will add project.gitdir to when we're done
283 (with our lock held).
284 pm: Instance of a Project object. We will call pm.update() (with our
285 lock held).
286 sem: We'll release() this semaphore when we exit so that another thread
287 can be started up.
288 err_event: We'll set this event in the case of an error (after printing
289 out info about the error).
David James8d201162013-10-11 17:03:19 -0700290
291 Returns:
292 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900293 """
294 # We'll set to true once we've locked the lock.
295 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700296
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530297 if not opt.quiet:
298 print('Fetching project %s' % project.name)
299
David Pursehousec1b86a22012-11-14 11:36:51 +0900300 # Encapsulate everything in a try/except/finally so that:
301 # - We always set err_event in the case of an exception.
302 # - We always make sure we call sem.release().
303 # - We always make sure we unlock the lock if we locked it.
304 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700305 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900306 start = time.time()
307 success = project.Sync_NetworkHalf(
308 quiet=opt.quiet,
309 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700310 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700311 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900312 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
David Pursehouse74cfd272015-10-14 10:50:15 +0900313 optimized_fetch=opt.optimized_fetch,
314 prune=opt.prune)
David Pursehousec1b86a22012-11-14 11:36:51 +0900315 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700316
David Pursehousec1b86a22012-11-14 11:36:51 +0900317 # Lock around all the rest of the code, since printing, updating a set
318 # and Progress.update() are not thread safe.
319 lock.acquire()
320 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700321
David Pursehousec1b86a22012-11-14 11:36:51 +0900322 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800323 err_event.set()
David Pursehousec1b86a22012-11-14 11:36:51 +0900324 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
325 if opt.force_broken:
326 print('warn: --force-broken, continuing to sync',
327 file=sys.stderr)
328 else:
329 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700330
David Pursehousec1b86a22012-11-14 11:36:51 +0900331 fetched.add(project.gitdir)
332 pm.update()
333 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800334 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400335 except Exception as e:
336 print('error: Cannot fetch %s (%s: %s)' \
337 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 err_event.set()
339 raise
340 finally:
341 if did_lock:
342 lock.release()
343 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800344
David James8d201162013-10-11 17:03:19 -0700345 return success
346
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700347 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700348 fetched = set()
David James89ece422014-01-09 18:51:58 -0800349 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700350 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800351
David James89ece422014-01-09 18:51:58 -0800352 objdir_project_map = dict()
353 for project in projects:
354 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700355
David James89ece422014-01-09 18:51:58 -0800356 threads = set()
357 sem = _threading.Semaphore(self.jobs)
358 err_event = _threading.Event()
359 for project_list in objdir_project_map.values():
360 # Check for any errors before running any more tasks.
361 # ...we'll let existing threads finish, though.
362 if err_event.isSet() and not opt.force_broken:
363 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700364
David James89ece422014-01-09 18:51:58 -0800365 sem.acquire()
366 kwargs = dict(opt=opt,
367 projects=project_list,
368 lock=lock,
369 fetched=fetched,
370 pm=pm,
371 sem=sem,
372 err_event=err_event)
373 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700374 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800375 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200376 # Ensure that Ctrl-C will not freeze the repo process.
377 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800378 threads.add(t)
379 t.start()
David James89ece422014-01-09 18:51:58 -0800380 else:
381 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800382
David James89ece422014-01-09 18:51:58 -0800383 for t in threads:
384 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800385
David James89ece422014-01-09 18:51:58 -0800386 # If we saw an error, exit with code 1 so that other scripts can check.
387 if err_event.isSet():
388 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
389 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700390
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700391 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700392 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700393
Julien Campergue335f5ef2013-10-16 11:02:35 +0200394 if not self.manifest.IsArchive:
395 self._GCProjects(projects)
396
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700397 return fetched
398
Dave Borowitz18857212012-10-23 17:02:59 -0700399 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700400 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700401 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700402 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
403 print('Shared project %s found, disabling pruning.' % project.name)
404 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
405 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700406
Dave Borowitze2152672012-10-31 12:24:38 -0700407 has_dash_c = git_require((1, 7, 2))
408 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700409 cpu_count = multiprocessing.cpu_count()
410 else:
411 cpu_count = 1
412 jobs = min(self.jobs, cpu_count)
413
414 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700415 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700416 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700417 return
418
419 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
420
421 threads = set()
422 sem = _threading.Semaphore(jobs)
423 err_event = _threading.Event()
424
David James8d201162013-10-11 17:03:19 -0700425 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700426 try:
427 try:
David James8d201162013-10-11 17:03:19 -0700428 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700429 except GitError:
430 err_event.set()
431 except:
432 err_event.set()
433 raise
434 finally:
435 sem.release()
436
Gabe Black2ff30292014-10-09 17:54:35 -0700437 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700438 if err_event.isSet():
439 break
440 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700441 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700442 t.daemon = True
443 threads.add(t)
444 t.start()
445
446 for t in threads:
447 t.join()
448
449 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700450 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700451 sys.exit(1)
452
Tim Kilbourn07669002013-03-08 15:02:49 -0800453 def _ReloadManifest(self, manifest_name=None):
454 if manifest_name:
455 # Override calls _Unload already
456 self.manifest.Override(manifest_name)
457 else:
458 self.manifest._Unload()
459
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700460 def UpdateProjectList(self):
461 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700462 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700463 if project.relpath:
464 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700465 file_name = 'project.list'
466 file_path = os.path.join(self.manifest.repodir, file_name)
467 old_project_paths = []
468
469 if os.path.exists(file_path):
470 fd = open(file_path, 'r')
471 try:
472 old_project_paths = fd.read().split('\n')
473 finally:
474 fd.close()
475 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700476 if not path:
477 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700478 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900479 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400480 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700481 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900482 project = Project(
483 manifest = self.manifest,
484 name = path,
485 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700486 gitdir = gitdir,
487 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900488 worktree = os.path.join(self.manifest.topdir, path),
489 relpath = path,
490 revisionExpr = 'HEAD',
491 revisionId = None,
492 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400493
David Pursehousec1b86a22012-11-14 11:36:51 +0900494 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900495 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900496 'are present' % project.relpath, file=sys.stderr)
497 print(' commit changes, then run sync again',
498 file=sys.stderr)
499 return -1
500 else:
501 print('Deleting obsolete path %s' % project.worktree,
502 file=sys.stderr)
503 shutil.rmtree(project.worktree)
504 # Try deleting parent subdirs if they are empty
505 project_dir = os.path.dirname(project.worktree)
506 while project_dir != self.manifest.topdir:
507 try:
508 os.rmdir(project_dir)
509 except OSError:
510 break
511 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700512
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700513 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700514 fd = open(file_path, 'w')
515 try:
516 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700517 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700518 finally:
519 fd.close()
520 return 0
521
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700522 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800523 if opt.jobs:
524 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700525 if self.jobs > 1:
526 soft_limit, _ = _rlimit_nofile()
527 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
528
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700529 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700530 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700531 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700532 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700533 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700534 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500535 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700536 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500537 sys.exit(1)
538 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700539 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500540 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900541 if opt.manifest_server_username or opt.manifest_server_password:
542 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700543 print('error: -u and -p may only be combined with -s or -t',
544 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900545 sys.exit(1)
546 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700547 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900548 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500549
550 if opt.manifest_name:
551 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700552
Chirayu Desaia892b102013-06-11 14:18:46 +0530553 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900554 smart_sync_manifest_name = "smart_sync_override.xml"
555 smart_sync_manifest_path = os.path.join(
556 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530557
Victor Boivie08c880d2011-04-19 10:32:52 +0200558 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700559 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900560 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700561 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700562 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900563
564 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900565 if not opt.quiet:
566 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900567
David Pursehouse86d973d2012-08-24 10:21:02 +0900568 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900569 username = None
570 password = None
571 if opt.manifest_server_username and opt.manifest_server_password:
572 username = opt.manifest_server_username
573 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900574 else:
575 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900576 info = netrc.netrc()
577 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900578 # .netrc file does not exist or could not be opened
579 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900580 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900581 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530582 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900583 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900584 auth = info.authenticators(parse_result.hostname)
585 if auth:
586 username, _account, password = auth
587 else:
588 print('No credentials found for %s in .netrc'
589 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700590 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700591 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900592
593 if (username and password):
594 manifest_server = manifest_server.replace('://', '://%s:%s@' %
595 (username, password),
596 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900597
Dan Willemsen0745bb22015-08-17 13:41:45 -0700598 transport = PersistentTransport(manifest_server)
599 if manifest_server.startswith('persistent-'):
600 manifest_server = manifest_server[len('persistent-'):]
601
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700602 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700603 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200604 if opt.smart_sync:
605 p = self.manifest.manifestProject
606 b = p.GetBranch(p.CurrentBranch)
607 branch = b.merge
608 if branch.startswith(R_HEADS):
609 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700610
Victor Boivie08c880d2011-04-19 10:32:52 +0200611 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700612 if 'SYNC_TARGET' in env:
613 target = env['SYNC_TARGET']
614 [success, manifest_str] = server.GetApprovedManifest(branch, target)
615 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200616 target = '%s-%s' % (env['TARGET_PRODUCT'],
617 env['TARGET_BUILD_VARIANT'])
618 [success, manifest_str] = server.GetApprovedManifest(branch, target)
619 else:
620 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700621 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200622 assert(opt.smart_tag)
623 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700624
625 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900626 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700627 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900628 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700629 try:
630 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700631 finally:
632 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900633 except IOError as e:
634 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900635 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700636 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700637 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100638 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700639 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900640 print('error: manifest server RPC call failed: %s' %
641 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700642 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530643 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700644 print('error: cannot connect to manifest server %s:\n%s'
645 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900646 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530647 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700648 print('error: cannot connect to manifest server %s:\n%d %s'
649 % (self.manifest.manifest_server, e.errcode, e.errmsg),
650 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700651 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900652 else: # Not smart sync or smart tag mode
653 if os.path.isfile(smart_sync_manifest_path):
654 try:
655 os.remove(smart_sync_manifest_path)
656 except OSError as e:
657 print('error: failed to remove existing smart sync override manifest: %s' %
658 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700659
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700660 rp = self.manifest.repoProject
661 rp.PreSync()
662
663 mp = self.manifest.manifestProject
664 mp.PreSync()
665
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800666 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700667 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800668
Nico Sallembien9bb18162009-12-07 15:38:01 -0800669 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700670 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700671 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900672 no_tags=opt.no_tags,
673 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800674
675 if mp.HasChanges:
676 syncbuf = SyncBuffer(mp.config)
677 mp.Sync_LocalHalf(syncbuf)
678 if not syncbuf.Finish():
679 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100680 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700681 if opt.jobs is None:
682 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700683
Simran Basib9a1b732015-08-20 12:19:28 -0700684 if self.gitc_manifest:
685 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700686 missing_ok=True)
687 gitc_projects = []
688 opened_projects = []
689 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700690 if project.relpath in self.gitc_manifest.paths and \
691 self.gitc_manifest.paths[project.relpath].old_revision:
692 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700693 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700694 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700695
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700696 if not args:
697 gitc_projects = None
698
699 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700700 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700701 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
702 if manifest_name:
703 manifest.Override(manifest_name)
704 else:
705 manifest.Override(self.manifest.manifestFile)
706 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
707 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700708 gitc_projects)
709 print('GITC client successfully synced.')
710
711 # The opened projects need to be synced as normal, therefore we
712 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700713 # TODO: make this more reliable -- if there's a project name/path overlap,
714 # this may choose the wrong project.
715 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
716 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700717 if not args:
718 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800719 all_projects = self.GetProjects(args,
720 missing_ok=True,
721 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700722
Dave Borowitz67700e92012-10-23 15:00:54 -0700723 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700724 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700725 to_fetch = []
726 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700727 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700728 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900729 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700730 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700731
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800732 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700733 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700734 if opt.network_only:
735 # bail out now; the rest touches the working tree
736 return
737
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800738 # Iteratively fetch missing and/or nested unregistered submodules
739 previously_missing_set = set()
740 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100741 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800742 all_projects = self.GetProjects(args,
743 missing_ok=True,
744 submodules_ok=opt.fetch_submodules)
745 missing = []
746 for project in all_projects:
747 if project.gitdir not in fetched:
748 missing.append(project)
749 if not missing:
750 break
751 # Stop us from non-stopped fetching actually-missing repos: If set of
752 # missing repos has not been changed from last fetch, we break.
753 missing_set = set(p.name for p in missing)
754 if previously_missing_set == missing_set:
755 break
756 previously_missing_set = missing_set
757 fetched.update(self._Fetch(missing, opt))
758
Julien Campergue335f5ef2013-10-16 11:02:35 +0200759 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700760 # bail out now, we have no working tree
761 return
762
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700763 if self.UpdateProjectList():
764 sys.exit(1)
765
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700766 syncbuf = SyncBuffer(mp.config,
767 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900768 pm = Progress('Syncing work tree', len(all_projects))
769 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700770 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800771 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700772 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700773 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700774 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700775 if not syncbuf.Finish():
776 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700777
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700778 # If there's a notice that's supposed to print at the end of the sync, print
779 # it now...
780 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700781 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700782
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700783def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800784 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700785 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700786 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800787 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700788 if project.Exists:
789 project.PostRepoUpgrade()
790
791def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
792 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700793 print('info: A new version of repo is available', file=sys.stderr)
794 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700795 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700796 syncbuf = SyncBuffer(rp.config)
797 rp.Sync_LocalHalf(syncbuf)
798 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700799 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700800 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700801 raise RepoChangedException(['--repo-upgraded'])
802 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700803 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700804 else:
805 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700806 print('repo version %s is current' % rp.work_git.describe(HEAD),
807 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700808
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700809def _VerifyTag(project):
810 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
811 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700812 print('warning: GnuPG was not available during last "repo init"\n'
813 'warning: Cannot automatically authenticate repo."""',
814 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700815 return True
816
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700818 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819 except GitError:
820 cur = None
821
822 if not cur \
823 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700824 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700825 if rev.startswith(R_HEADS):
826 rev = rev[len(R_HEADS):]
827
Sarah Owenscecd1d82012-11-01 22:59:27 -0700828 print(file=sys.stderr)
829 print("warning: project '%s' branch '%s' is not signed"
830 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700831 return False
832
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800833 env = os.environ.copy()
834 env['GIT_DIR'] = project.gitdir.encode()
835 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836
837 cmd = [GIT, 'tag', '-v', cur]
838 proc = subprocess.Popen(cmd,
839 stdout = subprocess.PIPE,
840 stderr = subprocess.PIPE,
841 env = env)
842 out = proc.stdout.read()
843 proc.stdout.close()
844
845 err = proc.stderr.read()
846 proc.stderr.close()
847
848 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700849 print(file=sys.stderr)
850 print(out, file=sys.stderr)
851 print(err, file=sys.stderr)
852 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700853 return False
854 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700855
856class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700857 _ALPHA = 0.5
858
Dave Borowitz67700e92012-10-23 15:00:54 -0700859 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100860 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700861 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700862 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700863
864 def Get(self, project):
865 self._Load()
866 return self._times.get(project.name, _ONE_DAY_S)
867
868 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700869 self._Load()
870 name = project.name
871 old = self._times.get(name, t)
872 self._seen.add(name)
873 a = self._ALPHA
874 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700875
876 def _Load(self):
877 if self._times is None:
878 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100879 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700880 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100881 self._times = json.load(f)
882 finally:
883 f.close()
884 except (IOError, ValueError):
885 try:
886 os.remove(self._path)
887 except OSError:
888 pass
889 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700890
891 def Save(self):
892 if self._times is None:
893 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700894
895 to_delete = []
896 for name in self._times:
897 if name not in self._seen:
898 to_delete.append(name)
899 for name in to_delete:
900 del self._times[name]
901
Dave Borowitz67700e92012-10-23 15:00:54 -0700902 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100903 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700904 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100905 json.dump(self._times, f, indent=2)
906 finally:
907 f.close()
908 except (IOError, TypeError):
909 try:
910 os.remove(self._path)
911 except OSError:
912 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700913
914# This is a replacement for xmlrpc.client.Transport using urllib2
915# and supporting persistent-http[s]. It cannot change hosts from
916# request to request like the normal transport, the real url
917# is passed during initialization.
918class PersistentTransport(xmlrpc.client.Transport):
919 def __init__(self, orig_host):
920 self.orig_host = orig_host
921
922 def request(self, host, handler, request_body, verbose=False):
923 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
924 # Python doesn't understand cookies with the #HttpOnly_ prefix
925 # Since we're only using them for HTTP, copy the file temporarily,
926 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700927 if cookiefile:
928 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900929 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700930 try:
931 with open(cookiefile) as f:
932 for line in f:
933 if line.startswith("#HttpOnly_"):
934 line = line[len("#HttpOnly_"):]
935 tmpcookiefile.write(line)
936 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700937
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700938 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900939 try:
940 cookiejar.load()
941 except cookielib.LoadError:
942 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700943 finally:
944 tmpcookiefile.close()
945 else:
946 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700947
948 proxyhandler = urllib.request.ProxyHandler
949 if proxy:
950 proxyhandler = urllib.request.ProxyHandler({
951 "http": proxy,
952 "https": proxy })
953
954 opener = urllib.request.build_opener(
955 urllib.request.HTTPCookieProcessor(cookiejar),
956 proxyhandler)
957
958 url = urllib.parse.urljoin(self.orig_host, handler)
959 parse_results = urllib.parse.urlparse(url)
960
961 scheme = parse_results.scheme
962 if scheme == 'persistent-http':
963 scheme = 'http'
964 if scheme == 'persistent-https':
965 # If we're proxying through persistent-https, use http. The
966 # proxy itself will do the https.
967 if proxy:
968 scheme = 'http'
969 else:
970 scheme = 'https'
971
972 # Parse out any authentication information using the base class
973 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
974
975 url = urllib.parse.urlunparse((
976 scheme,
977 host,
978 parse_results.path,
979 parse_results.params,
980 parse_results.query,
981 parse_results.fragment))
982
983 request = urllib.request.Request(url, request_body)
984 if extra_headers is not None:
985 for (name, header) in extra_headers:
986 request.add_header(name, header)
987 request.add_header('Content-Type', 'text/xml')
988 try:
989 response = opener.open(request)
990 except urllib.error.HTTPError as e:
991 if e.code == 501:
992 # We may have been redirected through a login process
993 # but our POST turned into a GET. Retry.
994 response = opener.open(request)
995 else:
996 raise
997
998 p, u = xmlrpc.client.getparser()
999 while 1:
1000 data = response.read(1024)
1001 if not data:
1002 break
1003 p.feed(data)
1004 p.close()
1005 return u.close()
1006
1007 def close(self):
1008 pass
1009