blob: e865e564d82af14de7028113d0b0ce79318cdb15 [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',
245 help='smart sync using manifest from a 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:
323 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
324 if opt.force_broken:
325 print('warn: --force-broken, continuing to sync',
326 file=sys.stderr)
327 else:
328 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700329
David Pursehousec1b86a22012-11-14 11:36:51 +0900330 fetched.add(project.gitdir)
331 pm.update()
332 except _FetchError:
333 err_event.set()
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400334 except Exception as e:
335 print('error: Cannot fetch %s (%s: %s)' \
336 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900337 err_event.set()
338 raise
339 finally:
340 if did_lock:
341 lock.release()
342 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800343
David James8d201162013-10-11 17:03:19 -0700344 return success
345
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700346 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700347 fetched = set()
David James89ece422014-01-09 18:51:58 -0800348 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700349 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800350
David James89ece422014-01-09 18:51:58 -0800351 objdir_project_map = dict()
352 for project in projects:
353 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700354
David James89ece422014-01-09 18:51:58 -0800355 threads = set()
356 sem = _threading.Semaphore(self.jobs)
357 err_event = _threading.Event()
358 for project_list in objdir_project_map.values():
359 # Check for any errors before running any more tasks.
360 # ...we'll let existing threads finish, though.
361 if err_event.isSet() and not opt.force_broken:
362 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700363
David James89ece422014-01-09 18:51:58 -0800364 sem.acquire()
365 kwargs = dict(opt=opt,
366 projects=project_list,
367 lock=lock,
368 fetched=fetched,
369 pm=pm,
370 sem=sem,
371 err_event=err_event)
372 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700373 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800374 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200375 # Ensure that Ctrl-C will not freeze the repo process.
376 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800377 threads.add(t)
378 t.start()
David James89ece422014-01-09 18:51:58 -0800379 else:
380 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800381
David James89ece422014-01-09 18:51:58 -0800382 for t in threads:
383 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800384
David James89ece422014-01-09 18:51:58 -0800385 # If we saw an error, exit with code 1 so that other scripts can check.
386 if err_event.isSet():
387 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
388 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700389
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700390 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700391 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700392
Julien Campergue335f5ef2013-10-16 11:02:35 +0200393 if not self.manifest.IsArchive:
394 self._GCProjects(projects)
395
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700396 return fetched
397
Dave Borowitz18857212012-10-23 17:02:59 -0700398 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700399 gitdirs = {}
400 for project in projects:
401 gitdirs[project.gitdir] = project.bare_git
402
Dave Borowitze2152672012-10-31 12:24:38 -0700403 has_dash_c = git_require((1, 7, 2))
404 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700405 cpu_count = multiprocessing.cpu_count()
406 else:
407 cpu_count = 1
408 jobs = min(self.jobs, cpu_count)
409
410 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700411 for bare_git in gitdirs.values():
412 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700413 return
414
415 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
416
417 threads = set()
418 sem = _threading.Semaphore(jobs)
419 err_event = _threading.Event()
420
David James8d201162013-10-11 17:03:19 -0700421 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700422 try:
423 try:
David James8d201162013-10-11 17:03:19 -0700424 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700425 except GitError:
426 err_event.set()
427 except:
428 err_event.set()
429 raise
430 finally:
431 sem.release()
432
David James8d201162013-10-11 17:03:19 -0700433 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700434 if err_event.isSet():
435 break
436 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700437 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700438 t.daemon = True
439 threads.add(t)
440 t.start()
441
442 for t in threads:
443 t.join()
444
445 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700446 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700447 sys.exit(1)
448
Tim Kilbourn07669002013-03-08 15:02:49 -0800449 def _ReloadManifest(self, manifest_name=None):
450 if manifest_name:
451 # Override calls _Unload already
452 self.manifest.Override(manifest_name)
453 else:
454 self.manifest._Unload()
455
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700456 def UpdateProjectList(self):
457 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700458 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700459 if project.relpath:
460 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700461 file_name = 'project.list'
462 file_path = os.path.join(self.manifest.repodir, file_name)
463 old_project_paths = []
464
465 if os.path.exists(file_path):
466 fd = open(file_path, 'r')
467 try:
468 old_project_paths = fd.read().split('\n')
469 finally:
470 fd.close()
471 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700472 if not path:
473 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700474 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900475 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400476 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700477 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900478 project = Project(
479 manifest = self.manifest,
480 name = path,
481 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700482 gitdir = gitdir,
483 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900484 worktree = os.path.join(self.manifest.topdir, path),
485 relpath = path,
486 revisionExpr = 'HEAD',
487 revisionId = None,
488 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400489
David Pursehousec1b86a22012-11-14 11:36:51 +0900490 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900491 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900492 'are present' % project.relpath, file=sys.stderr)
493 print(' commit changes, then run sync again',
494 file=sys.stderr)
495 return -1
496 else:
497 print('Deleting obsolete path %s' % project.worktree,
498 file=sys.stderr)
499 shutil.rmtree(project.worktree)
500 # Try deleting parent subdirs if they are empty
501 project_dir = os.path.dirname(project.worktree)
502 while project_dir != self.manifest.topdir:
503 try:
504 os.rmdir(project_dir)
505 except OSError:
506 break
507 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700508
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700509 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700510 fd = open(file_path, 'w')
511 try:
512 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700513 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700514 finally:
515 fd.close()
516 return 0
517
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700518 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800519 if opt.jobs:
520 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700521 if self.jobs > 1:
522 soft_limit, _ = _rlimit_nofile()
523 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
524
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700525 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700526 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700527 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700528 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700529 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700530 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500531 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700532 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500533 sys.exit(1)
534 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700535 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500536 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900537 if opt.manifest_server_username or opt.manifest_server_password:
538 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700539 print('error: -u and -p may only be combined with -s or -t',
540 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900541 sys.exit(1)
542 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700543 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900544 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500545
546 if opt.manifest_name:
547 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700548
Chirayu Desaia892b102013-06-11 14:18:46 +0530549 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900550 smart_sync_manifest_name = "smart_sync_override.xml"
551 smart_sync_manifest_path = os.path.join(
552 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530553
Victor Boivie08c880d2011-04-19 10:32:52 +0200554 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700555 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900556 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700557 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700558 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900559
560 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900561 if not opt.quiet:
562 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900563
David Pursehouse86d973d2012-08-24 10:21:02 +0900564 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900565 username = None
566 password = None
567 if opt.manifest_server_username and opt.manifest_server_password:
568 username = opt.manifest_server_username
569 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900570 else:
571 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900572 info = netrc.netrc()
573 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900574 # .netrc file does not exist or could not be opened
575 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900576 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900577 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530578 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900579 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900580 auth = info.authenticators(parse_result.hostname)
581 if auth:
582 username, _account, password = auth
583 else:
584 print('No credentials found for %s in .netrc'
585 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700586 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700587 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900588
589 if (username and password):
590 manifest_server = manifest_server.replace('://', '://%s:%s@' %
591 (username, password),
592 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900593
Dan Willemsen0745bb22015-08-17 13:41:45 -0700594 transport = PersistentTransport(manifest_server)
595 if manifest_server.startswith('persistent-'):
596 manifest_server = manifest_server[len('persistent-'):]
597
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700598 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700599 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200600 if opt.smart_sync:
601 p = self.manifest.manifestProject
602 b = p.GetBranch(p.CurrentBranch)
603 branch = b.merge
604 if branch.startswith(R_HEADS):
605 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700606
Victor Boivie08c880d2011-04-19 10:32:52 +0200607 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700608 if 'SYNC_TARGET' in env:
609 target = env['SYNC_TARGET']
610 [success, manifest_str] = server.GetApprovedManifest(branch, target)
611 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200612 target = '%s-%s' % (env['TARGET_PRODUCT'],
613 env['TARGET_BUILD_VARIANT'])
614 [success, manifest_str] = server.GetApprovedManifest(branch, target)
615 else:
616 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700617 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200618 assert(opt.smart_tag)
619 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700620
621 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900622 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700623 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900624 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700625 try:
626 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700627 finally:
628 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900629 except IOError as e:
630 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900631 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700632 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700633 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100634 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700635 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900636 print('error: manifest server RPC call failed: %s' %
637 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700638 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530639 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700640 print('error: cannot connect to manifest server %s:\n%s'
641 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900642 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530643 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700644 print('error: cannot connect to manifest server %s:\n%d %s'
645 % (self.manifest.manifest_server, e.errcode, e.errmsg),
646 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700647 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900648 else: # Not smart sync or smart tag mode
649 if os.path.isfile(smart_sync_manifest_path):
650 try:
651 os.remove(smart_sync_manifest_path)
652 except OSError as e:
653 print('error: failed to remove existing smart sync override manifest: %s' %
654 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700655
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700656 rp = self.manifest.repoProject
657 rp.PreSync()
658
659 mp = self.manifest.manifestProject
660 mp.PreSync()
661
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800662 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700663 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800664
Nico Sallembien9bb18162009-12-07 15:38:01 -0800665 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700666 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700667 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900668 no_tags=opt.no_tags,
669 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800670
671 if mp.HasChanges:
672 syncbuf = SyncBuffer(mp.config)
673 mp.Sync_LocalHalf(syncbuf)
674 if not syncbuf.Finish():
675 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100676 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700677 if opt.jobs is None:
678 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700679
Simran Basib9a1b732015-08-20 12:19:28 -0700680 if self.gitc_manifest:
681 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700682 missing_ok=True)
683 gitc_projects = []
684 opened_projects = []
685 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700686 if project.relpath in self.gitc_manifest.paths and \
687 self.gitc_manifest.paths[project.relpath].old_revision:
688 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700689 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700690 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700691
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700692 if not args:
693 gitc_projects = None
694
695 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700696 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700697 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
698 if manifest_name:
699 manifest.Override(manifest_name)
700 else:
701 manifest.Override(self.manifest.manifestFile)
702 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
703 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700704 gitc_projects)
705 print('GITC client successfully synced.')
706
707 # The opened projects need to be synced as normal, therefore we
708 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700709 # TODO: make this more reliable -- if there's a project name/path overlap,
710 # this may choose the wrong project.
711 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
712 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700713 if not args:
714 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800715 all_projects = self.GetProjects(args,
716 missing_ok=True,
717 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700718
Dave Borowitz67700e92012-10-23 15:00:54 -0700719 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700720 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700721 to_fetch = []
722 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700723 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700724 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900725 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700726 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700727
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800728 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700729 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700730 if opt.network_only:
731 # bail out now; the rest touches the working tree
732 return
733
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800734 # Iteratively fetch missing and/or nested unregistered submodules
735 previously_missing_set = set()
736 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100737 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800738 all_projects = self.GetProjects(args,
739 missing_ok=True,
740 submodules_ok=opt.fetch_submodules)
741 missing = []
742 for project in all_projects:
743 if project.gitdir not in fetched:
744 missing.append(project)
745 if not missing:
746 break
747 # Stop us from non-stopped fetching actually-missing repos: If set of
748 # missing repos has not been changed from last fetch, we break.
749 missing_set = set(p.name for p in missing)
750 if previously_missing_set == missing_set:
751 break
752 previously_missing_set = missing_set
753 fetched.update(self._Fetch(missing, opt))
754
Julien Campergue335f5ef2013-10-16 11:02:35 +0200755 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700756 # bail out now, we have no working tree
757 return
758
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700759 if self.UpdateProjectList():
760 sys.exit(1)
761
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700762 syncbuf = SyncBuffer(mp.config,
763 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900764 pm = Progress('Syncing work tree', len(all_projects))
765 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700766 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800767 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700768 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700769 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700770 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700771 if not syncbuf.Finish():
772 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700773
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700774 # If there's a notice that's supposed to print at the end of the sync, print
775 # it now...
776 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700777 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700778
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700779def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800780 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700781 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700782 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800783 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700784 if project.Exists:
785 project.PostRepoUpgrade()
786
787def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
788 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700789 print('info: A new version of repo is available', file=sys.stderr)
790 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700791 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700792 syncbuf = SyncBuffer(rp.config)
793 rp.Sync_LocalHalf(syncbuf)
794 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700795 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700796 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700797 raise RepoChangedException(['--repo-upgraded'])
798 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700799 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700800 else:
801 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700802 print('repo version %s is current' % rp.work_git.describe(HEAD),
803 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700804
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700805def _VerifyTag(project):
806 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
807 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700808 print('warning: GnuPG was not available during last "repo init"\n'
809 'warning: Cannot automatically authenticate repo."""',
810 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811 return True
812
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700813 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700814 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700815 except GitError:
816 cur = None
817
818 if not cur \
819 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700820 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821 if rev.startswith(R_HEADS):
822 rev = rev[len(R_HEADS):]
823
Sarah Owenscecd1d82012-11-01 22:59:27 -0700824 print(file=sys.stderr)
825 print("warning: project '%s' branch '%s' is not signed"
826 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700827 return False
828
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800829 env = os.environ.copy()
830 env['GIT_DIR'] = project.gitdir.encode()
831 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700832
833 cmd = [GIT, 'tag', '-v', cur]
834 proc = subprocess.Popen(cmd,
835 stdout = subprocess.PIPE,
836 stderr = subprocess.PIPE,
837 env = env)
838 out = proc.stdout.read()
839 proc.stdout.close()
840
841 err = proc.stderr.read()
842 proc.stderr.close()
843
844 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700845 print(file=sys.stderr)
846 print(out, file=sys.stderr)
847 print(err, file=sys.stderr)
848 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700849 return False
850 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700851
852class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700853 _ALPHA = 0.5
854
Dave Borowitz67700e92012-10-23 15:00:54 -0700855 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100856 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700857 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700858 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700859
860 def Get(self, project):
861 self._Load()
862 return self._times.get(project.name, _ONE_DAY_S)
863
864 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700865 self._Load()
866 name = project.name
867 old = self._times.get(name, t)
868 self._seen.add(name)
869 a = self._ALPHA
870 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700871
872 def _Load(self):
873 if self._times is None:
874 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100875 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700876 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100877 self._times = json.load(f)
878 finally:
879 f.close()
880 except (IOError, ValueError):
881 try:
882 os.remove(self._path)
883 except OSError:
884 pass
885 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700886
887 def Save(self):
888 if self._times is None:
889 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700890
891 to_delete = []
892 for name in self._times:
893 if name not in self._seen:
894 to_delete.append(name)
895 for name in to_delete:
896 del self._times[name]
897
Dave Borowitz67700e92012-10-23 15:00:54 -0700898 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100899 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700900 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100901 json.dump(self._times, f, indent=2)
902 finally:
903 f.close()
904 except (IOError, TypeError):
905 try:
906 os.remove(self._path)
907 except OSError:
908 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700909
910# This is a replacement for xmlrpc.client.Transport using urllib2
911# and supporting persistent-http[s]. It cannot change hosts from
912# request to request like the normal transport, the real url
913# is passed during initialization.
914class PersistentTransport(xmlrpc.client.Transport):
915 def __init__(self, orig_host):
916 self.orig_host = orig_host
917
918 def request(self, host, handler, request_body, verbose=False):
919 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
920 # Python doesn't understand cookies with the #HttpOnly_ prefix
921 # Since we're only using them for HTTP, copy the file temporarily,
922 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700923 if cookiefile:
924 tmpcookiefile = tempfile.NamedTemporaryFile()
David Pursehouse4c5f74e2015-10-02 11:10:10 +0900925 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700926 try:
927 with open(cookiefile) as f:
928 for line in f:
929 if line.startswith("#HttpOnly_"):
930 line = line[len("#HttpOnly_"):]
931 tmpcookiefile.write(line)
932 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700933
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700934 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +0900935 try:
936 cookiejar.load()
937 except cookielib.LoadError:
938 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700939 finally:
940 tmpcookiefile.close()
941 else:
942 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700943
944 proxyhandler = urllib.request.ProxyHandler
945 if proxy:
946 proxyhandler = urllib.request.ProxyHandler({
947 "http": proxy,
948 "https": proxy })
949
950 opener = urllib.request.build_opener(
951 urllib.request.HTTPCookieProcessor(cookiejar),
952 proxyhandler)
953
954 url = urllib.parse.urljoin(self.orig_host, handler)
955 parse_results = urllib.parse.urlparse(url)
956
957 scheme = parse_results.scheme
958 if scheme == 'persistent-http':
959 scheme = 'http'
960 if scheme == 'persistent-https':
961 # If we're proxying through persistent-https, use http. The
962 # proxy itself will do the https.
963 if proxy:
964 scheme = 'http'
965 else:
966 scheme = 'https'
967
968 # Parse out any authentication information using the base class
969 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
970
971 url = urllib.parse.urlunparse((
972 scheme,
973 host,
974 parse_results.path,
975 parse_results.params,
976 parse_results.query,
977 parse_results.fragment))
978
979 request = urllib.request.Request(url, request_body)
980 if extra_headers is not None:
981 for (name, header) in extra_headers:
982 request.add_header(name, header)
983 request.add_header('Content-Type', 'text/xml')
984 try:
985 response = opener.open(request)
986 except urllib.error.HTTPError as e:
987 if e.code == 501:
988 # We may have been redirected through a login process
989 # but our POST turned into a GET. Retry.
990 response = opener.open(request)
991 else:
992 raise
993
994 p, u = xmlrpc.client.getparser()
995 while 1:
996 data = response.read(1024)
997 if not data:
998 break
999 p.feed(data)
1000 p.close()
1001 return u.close()
1002
1003 def close(self):
1004 pass
1005