blob: d4432ce8fa8c67b29d3fe06665c7e183a3419fdb [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
David Rileye0684ad2017-04-05 00:02:59 -070067import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070068from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090069from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090070from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070071import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070072from project import Project
73from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080074from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000075from error import RepoChangedException, GitError, ManifestParseError
Shawn O. Pearce350cde42009-04-16 11:21:18 -070076from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070077from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080078from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070079from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070080
Dave Borowitz67700e92012-10-23 15:00:54 -070081_ONE_DAY_S = 24 * 60 * 60
82
Doug Andersonfc06ced2011-03-16 15:49:18 -070083class _FetchError(Exception):
84 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
85 pass
86
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080087class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080088 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070089 common = True
90 helpSummary = "Update working tree to the latest revision"
91 helpUsage = """
92%prog [<project>...]
93"""
94 helpDescription = """
95The '%prog' command synchronizes local project directories
96with the remote repositories specified in the manifest. If a local
97project does not yet exist, it will clone a new local directory from
98the remote repository and set up tracking branches as specified in
99the manifest. If the local project already exists, '%prog'
100will update the remote branches and rebase any new local changes
101on top of the new remote changes.
102
103'%prog' will synchronize all projects listed at the command
104line. Projects can be specified either by name, or by a relative
105or absolute path to the project's local directory. If no projects
106are specified, '%prog' will synchronize all projects listed in
107the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700108
109The -d/--detach option can be used to switch specified projects
110back to the manifest revision. This option is especially helpful
111if the project is currently on a topic branch, but the manifest
112revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700113
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700114The -s/--smart-sync option can be used to sync to a known good
115build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200116manifest. The -t/--smart-tag option is similar and allows you to
117specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700118
David Pursehousecf76b1b2012-09-14 10:31:42 +0900119The -u/--manifest-server-username and -p/--manifest-server-password
120options can be used to specify a username and password to authenticate
121with the manifest server when using the -s or -t option.
122
123If -u and -p are not specified when using the -s or -t option, '%prog'
124will attempt to read authentication credentials for the manifest server
125from the user's .netrc file.
126
127'%prog' will not use authentication credentials from -u/-p or .netrc
128if the manifest server specified in the manifest file already includes
129credentials.
130
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500131The -f/--force-broken option can be used to proceed with syncing
132other projects if a project sync fails.
133
Kevin Degiabaa7f32014-11-12 11:27:45 -0700134The --force-sync option can be used to overwrite existing git
135directories if they have previously been linked to a different
136object direcotry. WARNING: This may cause data to be lost since
137refs may be removed when overwriting.
138
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700139The --no-clone-bundle option disables any attempt to use
140$URL/clone.bundle to bootstrap a new Git repository from a
141resumeable bundle file on a content delivery network. This
142may be necessary if there are problems with the local Python
143HTTP client or proxy configuration, but the Git binary works.
144
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800145The --fetch-submodules option enables fetching Git submodules
146of a project from server.
147
David Pursehousef2fad612015-01-29 14:36:28 +0900148The -c/--current-branch option can be used to only fetch objects that
149are on the branch specified by a project's revision.
150
David Pursehouseb1553542014-09-04 21:28:09 +0900151The --optimized-fetch option can be used to only fetch projects that
152are fixed to a sha1 revision if the sha1 revision does not already
153exist locally.
154
David Pursehouse74cfd272015-10-14 10:50:15 +0900155The --prune option can be used to remove any refs that no longer
156exist on the remote.
157
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700158SSH Connections
159---------------
160
161If at least one project remote URL uses an SSH connection (ssh://,
162git+ssh://, or user@host:path syntax) repo will automatically
163enable the SSH ControlMaster option when connecting to that host.
164This feature permits other projects in the same '%prog' session to
165reuse the same SSH tunnel, saving connection setup overheads.
166
167To disable this behavior on UNIX platforms, set the GIT_SSH
168environment variable to 'ssh'. For example:
169
170 export GIT_SSH=ssh
171 %prog
172
173Compatibility
174~~~~~~~~~~~~~
175
176This feature is automatically disabled on Windows, due to the lack
177of UNIX domain socket support.
178
179This feature is not compatible with url.insteadof rewrites in the
180user's ~/.gitconfig. '%prog' is currently not able to perform the
181rewrite early enough to establish the ControlMaster tunnel.
182
183If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
184later is required to fix a server side protocol bug.
185
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700186"""
187
Nico Sallembien6623b212010-05-11 12:57:01 -0700188 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000189 try:
190 self.jobs = self.manifest.default.sync_j
191 except ManifestParseError:
192 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700193
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500194 p.add_option('-f', '--force-broken',
195 dest='force_broken', action='store_true',
196 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700197 p.add_option('--force-sync',
198 dest='force_sync', action='store_true',
199 help="overwrite an existing git directory if it needs to "
200 "point to a different object directory. WARNING: this "
201 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900202 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700203 dest='local_only', action='store_true',
204 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900205 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700206 dest='network_only', action='store_true',
207 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900208 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700209 dest='detach_head', action='store_true',
210 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700212 dest='current_branch_only', action='store_true',
213 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700215 dest='quiet', action='store_true',
216 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800218 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700219 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500220 p.add_option('-m', '--manifest-name',
221 dest='manifest_name',
222 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700223 p.add_option('--no-clone-bundle',
224 dest='no_clone_bundle', action='store_true',
225 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800226 p.add_option('-u', '--manifest-server-username', action='store',
227 dest='manifest_server_username',
228 help='username to authenticate with the manifest server')
229 p.add_option('-p', '--manifest-server-password', action='store',
230 dest='manifest_server_password',
231 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800232 p.add_option('--fetch-submodules',
233 dest='fetch_submodules', action='store_true',
234 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700235 p.add_option('--no-tags',
236 dest='no_tags', action='store_true',
237 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900238 p.add_option('--optimized-fetch',
239 dest='optimized_fetch', action='store_true',
240 help='only fetch projects fixed to sha1 if revision does not exist locally')
David Pursehouse74cfd272015-10-14 10:50:15 +0900241 p.add_option('--prune', dest='prune', action='store_true',
242 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700243 if show_smart:
244 p.add_option('-s', '--smart-sync',
245 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900246 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200247 p.add_option('-t', '--smart-tag',
248 dest='smart_tag', action='store',
249 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700250
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700251 g = p.add_option_group('repo Version options')
252 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700253 dest='no_repo_verify', action='store_true',
254 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700255 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800256 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700257 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700258
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500259 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900260 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800261
David James8d201162013-10-11 17:03:19 -0700262 Delegates most of the work to _FetchHelper.
263
264 Args:
265 opt: Program options returned from optparse. See _Options().
266 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500267 sem: We'll release() this semaphore when we exit so that another thread
268 can be started up.
David James89ece422014-01-09 18:51:58 -0800269 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700270 _FetchHelper docstring for details.
271 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500272 try:
273 for project in projects:
274 success = self._FetchHelper(opt, project, *args, **kwargs)
275 if not success and not opt.force_broken:
276 break
277 finally:
278 sem.release()
David James8d201162013-10-11 17:03:19 -0700279
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500280 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event):
David James8d201162013-10-11 17:03:19 -0700281 """Fetch git objects for a single project.
282
David Pursehousec1b86a22012-11-14 11:36:51 +0900283 Args:
284 opt: Program options returned from optparse. See _Options().
285 project: Project object for the project to fetch.
286 lock: Lock for accessing objects that are shared amongst multiple
287 _FetchHelper() threads.
288 fetched: set object that we will add project.gitdir to when we're done
289 (with our lock held).
290 pm: Instance of a Project object. We will call pm.update() (with our
291 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900292 err_event: We'll set this event in the case of an error (after printing
293 out info about the error).
David James8d201162013-10-11 17:03:19 -0700294
295 Returns:
296 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900297 """
298 # We'll set to true once we've locked the lock.
299 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700300
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530301 if not opt.quiet:
302 print('Fetching project %s' % project.name)
303
David Pursehousec1b86a22012-11-14 11:36:51 +0900304 # Encapsulate everything in a try/except/finally so that:
305 # - We always set err_event in the case of an exception.
306 # - We always make sure we call sem.release().
307 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700308 start = time.time()
309 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900310 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700311 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900312 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()
Marc Herbertffb4b892017-04-04 22:03:53 -0700329 print('error: Cannot fetch %s from %s'
330 % (project.name, project.remote.url),
331 file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900332 if opt.force_broken:
333 print('warn: --force-broken, continuing to sync',
334 file=sys.stderr)
335 else:
336 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700337
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 fetched.add(project.gitdir)
339 pm.update()
340 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800341 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400342 except Exception as e:
343 print('error: Cannot fetch %s (%s: %s)' \
344 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900345 err_event.set()
346 raise
347 finally:
348 if did_lock:
349 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700350 finish = time.time()
351 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
352 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800353
David James8d201162013-10-11 17:03:19 -0700354 return success
355
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700356 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700357 fetched = set()
David James89ece422014-01-09 18:51:58 -0800358 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200359 pm = Progress('Fetching projects', len(projects),
360 print_newline=not(opt.quiet))
Roy Lee18afd7f2010-05-09 04:32:08 +0800361
David James89ece422014-01-09 18:51:58 -0800362 objdir_project_map = dict()
363 for project in projects:
364 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700365
David James89ece422014-01-09 18:51:58 -0800366 threads = set()
367 sem = _threading.Semaphore(self.jobs)
368 err_event = _threading.Event()
369 for project_list in objdir_project_map.values():
370 # Check for any errors before running any more tasks.
371 # ...we'll let existing threads finish, though.
372 if err_event.isSet() and not opt.force_broken:
373 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700374
David James89ece422014-01-09 18:51:58 -0800375 sem.acquire()
376 kwargs = dict(opt=opt,
377 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500378 sem=sem,
David James89ece422014-01-09 18:51:58 -0800379 lock=lock,
380 fetched=fetched,
381 pm=pm,
David James89ece422014-01-09 18:51:58 -0800382 err_event=err_event)
383 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700384 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800385 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200386 # Ensure that Ctrl-C will not freeze the repo process.
387 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800388 threads.add(t)
389 t.start()
David James89ece422014-01-09 18:51:58 -0800390 else:
391 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800392
David James89ece422014-01-09 18:51:58 -0800393 for t in threads:
394 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800395
David James89ece422014-01-09 18:51:58 -0800396 # If we saw an error, exit with code 1 so that other scripts can check.
Nicolas Cornu8419ab22017-06-16 12:09:06 +0200397 if err_event.isSet() and not opt.force_broken:
David James89ece422014-01-09 18:51:58 -0800398 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
399 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700400
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700401 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700402 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700403
Julien Campergue335f5ef2013-10-16 11:02:35 +0200404 if not self.manifest.IsArchive:
405 self._GCProjects(projects)
406
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700407 return fetched
408
Dave Borowitz18857212012-10-23 17:02:59 -0700409 def _GCProjects(self, projects):
Gabe Black2ff30292014-10-09 17:54:35 -0700410 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700411 for project in projects:
Gabe Black2ff30292014-10-09 17:54:35 -0700412 if len(project.manifest.GetProjectsWithName(project.name)) > 1:
413 print('Shared project %s found, disabling pruning.' % project.name)
414 project.bare_git.config('--replace-all', 'gc.pruneExpire', 'never')
415 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700416
Dave Borowitze2152672012-10-31 12:24:38 -0700417 has_dash_c = git_require((1, 7, 2))
418 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700419 cpu_count = multiprocessing.cpu_count()
420 else:
421 cpu_count = 1
422 jobs = min(self.jobs, cpu_count)
423
424 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700425 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700426 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700427 return
428
429 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
430
431 threads = set()
432 sem = _threading.Semaphore(jobs)
433 err_event = _threading.Event()
434
David James8d201162013-10-11 17:03:19 -0700435 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700436 try:
437 try:
David James8d201162013-10-11 17:03:19 -0700438 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700439 except GitError:
440 err_event.set()
441 except:
442 err_event.set()
443 raise
444 finally:
445 sem.release()
446
Gabe Black2ff30292014-10-09 17:54:35 -0700447 for bare_git in gc_gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700448 if err_event.isSet():
449 break
450 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700451 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700452 t.daemon = True
453 threads.add(t)
454 t.start()
455
456 for t in threads:
457 t.join()
458
459 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700460 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700461 sys.exit(1)
462
Tim Kilbourn07669002013-03-08 15:02:49 -0800463 def _ReloadManifest(self, manifest_name=None):
464 if manifest_name:
465 # Override calls _Unload already
466 self.manifest.Override(manifest_name)
467 else:
468 self.manifest._Unload()
469
Dan Willemsen43507912016-09-01 16:26:02 -0700470 def _DeleteProject(self, path):
471 print('Deleting obsolete path %s' % path, file=sys.stderr)
472
473 # Delete the .git directory first, so we're less likely to have a partially
474 # working git repository around. There shouldn't be any git projects here,
475 # so rmtree works.
476 try:
477 shutil.rmtree(os.path.join(path, '.git'))
478 except OSError:
479 print('Failed to remove %s' % os.path.join(path, '.git'), file=sys.stderr)
480 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
481 print(' remove manually, then run sync again', file=sys.stderr)
482 return -1
483
484 # Delete everything under the worktree, except for directories that contain
485 # another git project
486 dirs_to_remove = []
487 failed = False
488 for root, dirs, files in os.walk(path):
489 for f in files:
490 try:
491 os.remove(os.path.join(root, f))
492 except OSError:
493 print('Failed to remove %s' % os.path.join(root, f), file=sys.stderr)
494 failed = True
495 dirs[:] = [d for d in dirs
496 if not os.path.lexists(os.path.join(root, d, '.git'))]
497 dirs_to_remove += [os.path.join(root, d) for d in dirs
498 if os.path.join(root, d) not in dirs_to_remove]
499 for d in reversed(dirs_to_remove):
Dan Willemseneceeb1b2016-09-25 18:24:27 -0700500 if os.path.islink(d):
501 try:
502 os.remove(d)
503 except OSError:
504 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
505 failed = True
506 elif len(os.listdir(d)) == 0:
Dan Willemsen43507912016-09-01 16:26:02 -0700507 try:
508 os.rmdir(d)
509 except OSError:
510 print('Failed to remove %s' % os.path.join(root, d), file=sys.stderr)
511 failed = True
512 continue
513 if failed:
514 print('error: Failed to delete obsolete path %s' % path, file=sys.stderr)
515 print(' remove manually, then run sync again', file=sys.stderr)
516 return -1
517
518 # Try deleting parent dirs if they are empty
519 project_dir = path
520 while project_dir != self.manifest.topdir:
521 if len(os.listdir(project_dir)) == 0:
522 os.rmdir(project_dir)
523 else:
524 break
525 project_dir = os.path.dirname(project_dir)
526
527 return 0
528
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700529 def UpdateProjectList(self):
530 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700531 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700532 if project.relpath:
533 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700534 file_name = 'project.list'
535 file_path = os.path.join(self.manifest.repodir, file_name)
536 old_project_paths = []
537
538 if os.path.exists(file_path):
539 fd = open(file_path, 'r')
540 try:
541 old_project_paths = fd.read().split('\n')
542 finally:
543 fd.close()
544 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700545 if not path:
546 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700547 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900548 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700549 gitdir = os.path.join(self.manifest.topdir, path, '.git')
550 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900551 project = Project(
552 manifest = self.manifest,
553 name = path,
554 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700555 gitdir = gitdir,
556 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900557 worktree = os.path.join(self.manifest.topdir, path),
558 relpath = path,
559 revisionExpr = 'HEAD',
560 revisionId = None,
561 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400562
David Pursehousec1b86a22012-11-14 11:36:51 +0900563 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900564 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900565 'are present' % project.relpath, file=sys.stderr)
566 print(' commit changes, then run sync again',
567 file=sys.stderr)
568 return -1
Dan Willemsen43507912016-09-01 16:26:02 -0700569 elif self._DeleteProject(project.worktree):
570 return -1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700571
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700572 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700573 fd = open(file_path, 'w')
574 try:
575 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700576 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700577 finally:
578 fd.close()
579 return 0
580
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700581 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800582 if opt.jobs:
583 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700584 if self.jobs > 1:
585 soft_limit, _ = _rlimit_nofile()
586 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
587
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700588 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700589 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700590 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700591 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700592 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700593 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500594 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700595 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500596 sys.exit(1)
597 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700598 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500599 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900600 if opt.manifest_server_username or opt.manifest_server_password:
601 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700602 print('error: -u and -p may only be combined with -s or -t',
603 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900604 sys.exit(1)
605 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700606 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900607 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500608
609 if opt.manifest_name:
610 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700611
Chirayu Desaia892b102013-06-11 14:18:46 +0530612 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900613 smart_sync_manifest_name = "smart_sync_override.xml"
614 smart_sync_manifest_path = os.path.join(
615 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530616
Victor Boivie08c880d2011-04-19 10:32:52 +0200617 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700618 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900619 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700620 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700621 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900622
623 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900624 if not opt.quiet:
625 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900626
David Pursehouse86d973d2012-08-24 10:21:02 +0900627 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900628 username = None
629 password = None
630 if opt.manifest_server_username and opt.manifest_server_password:
631 username = opt.manifest_server_username
632 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900633 else:
634 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900635 info = netrc.netrc()
636 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900637 # .netrc file does not exist or could not be opened
638 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900639 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900640 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530641 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900642 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900643 auth = info.authenticators(parse_result.hostname)
644 if auth:
645 username, _account, password = auth
646 else:
647 print('No credentials found for %s in .netrc'
648 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700649 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700650 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900651
652 if (username and password):
653 manifest_server = manifest_server.replace('://', '://%s:%s@' %
654 (username, password),
655 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900656
Dan Willemsen0745bb22015-08-17 13:41:45 -0700657 transport = PersistentTransport(manifest_server)
658 if manifest_server.startswith('persistent-'):
659 manifest_server = manifest_server[len('persistent-'):]
660
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700661 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700662 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200663 if opt.smart_sync:
664 p = self.manifest.manifestProject
665 b = p.GetBranch(p.CurrentBranch)
666 branch = b.merge
667 if branch.startswith(R_HEADS):
668 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700669
Victor Boivie08c880d2011-04-19 10:32:52 +0200670 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700671 if 'SYNC_TARGET' in env:
672 target = env['SYNC_TARGET']
673 [success, manifest_str] = server.GetApprovedManifest(branch, target)
674 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200675 target = '%s-%s' % (env['TARGET_PRODUCT'],
676 env['TARGET_BUILD_VARIANT'])
677 [success, manifest_str] = server.GetApprovedManifest(branch, target)
678 else:
679 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700680 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200681 assert(opt.smart_tag)
682 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700683
684 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900685 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700686 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900687 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700688 try:
689 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700690 finally:
691 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900692 except IOError as e:
693 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900694 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700695 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700696 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100697 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700698 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900699 print('error: manifest server RPC call failed: %s' %
700 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700701 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530702 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700703 print('error: cannot connect to manifest server %s:\n%s'
704 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900705 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530706 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700707 print('error: cannot connect to manifest server %s:\n%d %s'
708 % (self.manifest.manifest_server, e.errcode, e.errmsg),
709 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700710 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900711 else: # Not smart sync or smart tag mode
712 if os.path.isfile(smart_sync_manifest_path):
713 try:
714 os.remove(smart_sync_manifest_path)
715 except OSError as e:
716 print('error: failed to remove existing smart sync override manifest: %s' %
717 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700718
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700719 rp = self.manifest.repoProject
720 rp.PreSync()
721
722 mp = self.manifest.manifestProject
723 mp.PreSync()
724
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800725 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700726 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800727
Nico Sallembien9bb18162009-12-07 15:38:01 -0800728 if not opt.local_only:
David Rileye0684ad2017-04-05 00:02:59 -0700729 start = time.time()
730 success = mp.Sync_NetworkHalf(quiet=opt.quiet,
731 current_branch_only=opt.current_branch_only,
732 no_tags=opt.no_tags,
733 optimized_fetch=opt.optimized_fetch,
734 submodules=self.manifest.HasSubmodules)
735 finish = time.time()
736 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
737 start, finish, success)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800738
739 if mp.HasChanges:
740 syncbuf = SyncBuffer(mp.config)
David Rileye0684ad2017-04-05 00:02:59 -0700741 start = time.time()
Martin Kellye4e94d22017-03-21 16:05:12 -0700742 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
David Rileye0684ad2017-04-05 00:02:59 -0700743 clean = syncbuf.Finish()
744 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
745 start, time.time(), clean)
746 if not clean:
Nico Sallembien9bb18162009-12-07 15:38:01 -0800747 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100748 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700749 if opt.jobs is None:
750 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700751
Simran Basib9a1b732015-08-20 12:19:28 -0700752 if self.gitc_manifest:
753 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700754 missing_ok=True)
755 gitc_projects = []
756 opened_projects = []
757 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700758 if project.relpath in self.gitc_manifest.paths and \
759 self.gitc_manifest.paths[project.relpath].old_revision:
760 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700761 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700762 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700763
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700764 if not args:
765 gitc_projects = None
766
767 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700768 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700769 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
770 if manifest_name:
771 manifest.Override(manifest_name)
772 else:
773 manifest.Override(self.manifest.manifestFile)
774 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
775 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700776 gitc_projects)
777 print('GITC client successfully synced.')
778
779 # The opened projects need to be synced as normal, therefore we
780 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700781 # TODO: make this more reliable -- if there's a project name/path overlap,
782 # this may choose the wrong project.
783 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
784 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700785 if not args:
786 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800787 all_projects = self.GetProjects(args,
788 missing_ok=True,
789 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790
Dave Borowitz67700e92012-10-23 15:00:54 -0700791 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700792 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700793 to_fetch = []
794 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700795 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700796 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900797 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700798 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700799
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800800 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700801 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700802 if opt.network_only:
803 # bail out now; the rest touches the working tree
804 return
805
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800806 # Iteratively fetch missing and/or nested unregistered submodules
807 previously_missing_set = set()
808 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100809 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800810 all_projects = self.GetProjects(args,
811 missing_ok=True,
812 submodules_ok=opt.fetch_submodules)
813 missing = []
814 for project in all_projects:
815 if project.gitdir not in fetched:
816 missing.append(project)
817 if not missing:
818 break
819 # Stop us from non-stopped fetching actually-missing repos: If set of
820 # missing repos has not been changed from last fetch, we break.
821 missing_set = set(p.name for p in missing)
822 if previously_missing_set == missing_set:
823 break
824 previously_missing_set = missing_set
825 fetched.update(self._Fetch(missing, opt))
826
Julien Campergue335f5ef2013-10-16 11:02:35 +0200827 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700828 # bail out now, we have no working tree
829 return
830
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700831 if self.UpdateProjectList():
832 sys.exit(1)
833
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700834 syncbuf = SyncBuffer(mp.config,
835 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900836 pm = Progress('Syncing work tree', len(all_projects))
837 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700838 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800839 if project.worktree:
David Rileye0684ad2017-04-05 00:02:59 -0700840 start = time.time()
Kevin Degiabaa7f32014-11-12 11:27:45 -0700841 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
David Rileye0684ad2017-04-05 00:02:59 -0700842 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
843 start, time.time(), syncbuf.Recently())
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700844 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700845 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700846 if not syncbuf.Finish():
847 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700848
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700849 # If there's a notice that's supposed to print at the end of the sync, print
850 # it now...
851 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700852 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700853
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700854def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800855 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700856 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700857 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800858 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700859 if project.Exists:
860 project.PostRepoUpgrade()
861
862def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
863 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700864 print('info: A new version of repo is available', file=sys.stderr)
865 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700866 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700867 syncbuf = SyncBuffer(rp.config)
868 rp.Sync_LocalHalf(syncbuf)
869 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700870 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700871 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700872 raise RepoChangedException(['--repo-upgraded'])
873 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700874 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700875 else:
876 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700877 print('repo version %s is current' % rp.work_git.describe(HEAD),
878 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700879
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880def _VerifyTag(project):
881 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
882 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700883 print('warning: GnuPG was not available during last "repo init"\n'
884 'warning: Cannot automatically authenticate repo."""',
885 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700886 return True
887
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700889 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700890 except GitError:
891 cur = None
892
893 if not cur \
894 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700895 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700896 if rev.startswith(R_HEADS):
897 rev = rev[len(R_HEADS):]
898
Sarah Owenscecd1d82012-11-01 22:59:27 -0700899 print(file=sys.stderr)
900 print("warning: project '%s' branch '%s' is not signed"
901 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700902 return False
903
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800904 env = os.environ.copy()
905 env['GIT_DIR'] = project.gitdir.encode()
906 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700907
908 cmd = [GIT, 'tag', '-v', cur]
909 proc = subprocess.Popen(cmd,
910 stdout = subprocess.PIPE,
911 stderr = subprocess.PIPE,
912 env = env)
913 out = proc.stdout.read()
914 proc.stdout.close()
915
916 err = proc.stderr.read()
917 proc.stderr.close()
918
919 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700920 print(file=sys.stderr)
921 print(out, file=sys.stderr)
922 print(err, file=sys.stderr)
923 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700924 return False
925 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700926
David Rileye0684ad2017-04-05 00:02:59 -0700927
Dave Borowitz67700e92012-10-23 15:00:54 -0700928class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700929 _ALPHA = 0.5
930
Dave Borowitz67700e92012-10-23 15:00:54 -0700931 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100932 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700933 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700934 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700935
936 def Get(self, project):
937 self._Load()
938 return self._times.get(project.name, _ONE_DAY_S)
939
940 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700941 self._Load()
942 name = project.name
943 old = self._times.get(name, t)
944 self._seen.add(name)
945 a = self._ALPHA
946 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700947
948 def _Load(self):
949 if self._times is None:
950 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100951 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700952 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100953 self._times = json.load(f)
954 finally:
955 f.close()
956 except (IOError, ValueError):
957 try:
958 os.remove(self._path)
959 except OSError:
960 pass
961 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700962
963 def Save(self):
964 if self._times is None:
965 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700966
967 to_delete = []
968 for name in self._times:
969 if name not in self._seen:
970 to_delete.append(name)
971 for name in to_delete:
972 del self._times[name]
973
Dave Borowitz67700e92012-10-23 15:00:54 -0700974 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100975 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700976 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100977 json.dump(self._times, f, indent=2)
978 finally:
979 f.close()
980 except (IOError, TypeError):
981 try:
982 os.remove(self._path)
983 except OSError:
984 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700985
986# This is a replacement for xmlrpc.client.Transport using urllib2
987# and supporting persistent-http[s]. It cannot change hosts from
988# request to request like the normal transport, the real url
989# is passed during initialization.
990class PersistentTransport(xmlrpc.client.Transport):
991 def __init__(self, orig_host):
992 self.orig_host = orig_host
993
994 def request(self, host, handler, request_body, verbose=False):
995 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
996 # Python doesn't understand cookies with the #HttpOnly_ prefix
997 # Since we're only using them for HTTP, copy the file temporarily,
998 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700999 if cookiefile:
1000 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001001 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001002 try:
1003 with open(cookiefile) as f:
1004 for line in f:
1005 if line.startswith("#HttpOnly_"):
1006 line = line[len("#HttpOnly_"):]
1007 tmpcookiefile.write(line)
1008 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001009
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001010 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001011 try:
1012 cookiejar.load()
1013 except cookielib.LoadError:
1014 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001015 finally:
1016 tmpcookiefile.close()
1017 else:
1018 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001019
1020 proxyhandler = urllib.request.ProxyHandler
1021 if proxy:
1022 proxyhandler = urllib.request.ProxyHandler({
1023 "http": proxy,
1024 "https": proxy })
1025
1026 opener = urllib.request.build_opener(
1027 urllib.request.HTTPCookieProcessor(cookiejar),
1028 proxyhandler)
1029
1030 url = urllib.parse.urljoin(self.orig_host, handler)
1031 parse_results = urllib.parse.urlparse(url)
1032
1033 scheme = parse_results.scheme
1034 if scheme == 'persistent-http':
1035 scheme = 'http'
1036 if scheme == 'persistent-https':
1037 # If we're proxying through persistent-https, use http. The
1038 # proxy itself will do the https.
1039 if proxy:
1040 scheme = 'http'
1041 else:
1042 scheme = 'https'
1043
1044 # Parse out any authentication information using the base class
1045 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1046
1047 url = urllib.parse.urlunparse((
1048 scheme,
1049 host,
1050 parse_results.path,
1051 parse_results.params,
1052 parse_results.query,
1053 parse_results.fragment))
1054
1055 request = urllib.request.Request(url, request_body)
1056 if extra_headers is not None:
1057 for (name, header) in extra_headers:
1058 request.add_header(name, header)
1059 request.add_header('Content-Type', 'text/xml')
1060 try:
1061 response = opener.open(request)
1062 except urllib.error.HTTPError as e:
1063 if e.code == 501:
1064 # We may have been redirected through a login process
1065 # but our POST turned into a GET. Retry.
1066 response = opener.open(request)
1067 else:
1068 raise
1069
1070 p, u = xmlrpc.client.getparser()
1071 while 1:
1072 data = response.read(1024)
1073 if not data:
1074 break
1075 p.feed(data)
1076 p.close()
1077 return u.close()
1078
1079 def close(self):
1080 pass
1081