blob: a99d7e74a9a679bbf6662c10ec666b5e82ebcebb [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
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700154SSH Connections
155---------------
156
157If at least one project remote URL uses an SSH connection (ssh://,
158git+ssh://, or user@host:path syntax) repo will automatically
159enable the SSH ControlMaster option when connecting to that host.
160This feature permits other projects in the same '%prog' session to
161reuse the same SSH tunnel, saving connection setup overheads.
162
163To disable this behavior on UNIX platforms, set the GIT_SSH
164environment variable to 'ssh'. For example:
165
166 export GIT_SSH=ssh
167 %prog
168
169Compatibility
170~~~~~~~~~~~~~
171
172This feature is automatically disabled on Windows, due to the lack
173of UNIX domain socket support.
174
175This feature is not compatible with url.insteadof rewrites in the
176user's ~/.gitconfig. '%prog' is currently not able to perform the
177rewrite early enough to establish the ControlMaster tunnel.
178
179If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
180later is required to fix a server side protocol bug.
181
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700182"""
183
Nico Sallembien6623b212010-05-11 12:57:01 -0700184 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000185 try:
186 self.jobs = self.manifest.default.sync_j
187 except ManifestParseError:
188 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700189
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500190 p.add_option('-f', '--force-broken',
191 dest='force_broken', action='store_true',
192 help="continue sync even if a project fails to sync")
Kevin Degiabaa7f32014-11-12 11:27:45 -0700193 p.add_option('--force-sync',
194 dest='force_sync', action='store_true',
195 help="overwrite an existing git directory if it needs to "
196 "point to a different object directory. WARNING: this "
197 "may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900198 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700199 dest='local_only', action='store_true',
200 help="only update working tree, don't fetch")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900201 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700202 dest='network_only', action='store_true',
203 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700205 dest='detach_head', action='store_true',
206 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900207 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700208 dest='current_branch_only', action='store_true',
209 help='fetch only current branch from server')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900210 p.add_option('-q', '--quiet',
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700211 dest='quiet', action='store_true',
212 help='be more quiet')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900213 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800214 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700215 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500216 p.add_option('-m', '--manifest-name',
217 dest='manifest_name',
218 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700219 p.add_option('--no-clone-bundle',
220 dest='no_clone_bundle', action='store_true',
221 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800222 p.add_option('-u', '--manifest-server-username', action='store',
223 dest='manifest_server_username',
224 help='username to authenticate with the manifest server')
225 p.add_option('-p', '--manifest-server-password', action='store',
226 dest='manifest_server_password',
227 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800228 p.add_option('--fetch-submodules',
229 dest='fetch_submodules', action='store_true',
230 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700231 p.add_option('--no-tags',
232 dest='no_tags', action='store_true',
233 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900234 p.add_option('--optimized-fetch',
235 dest='optimized_fetch', action='store_true',
236 help='only fetch projects fixed to sha1 if revision does not exist locally')
Nico Sallembien6623b212010-05-11 12:57:01 -0700237 if show_smart:
238 p.add_option('-s', '--smart-sync',
239 dest='smart_sync', action='store_true',
240 help='smart sync using manifest from a known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200241 p.add_option('-t', '--smart-tag',
242 dest='smart_tag', action='store',
243 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700244
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700245 g = p.add_option_group('repo Version options')
246 g.add_option('--no-repo-verify',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700247 dest='no_repo_verify', action='store_true',
248 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700249 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800250 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700251 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252
David James89ece422014-01-09 18:51:58 -0800253 def _FetchProjectList(self, opt, projects, *args, **kwargs):
David Pursehousec1b86a22012-11-14 11:36:51 +0900254 """Main function of the fetch threads when jobs are > 1.
Roy Lee18afd7f2010-05-09 04:32:08 +0800255
David James8d201162013-10-11 17:03:19 -0700256 Delegates most of the work to _FetchHelper.
257
258 Args:
259 opt: Program options returned from optparse. See _Options().
260 projects: Projects to fetch.
David James89ece422014-01-09 18:51:58 -0800261 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700262 _FetchHelper docstring for details.
263 """
264 for project in projects:
David James89ece422014-01-09 18:51:58 -0800265 success = self._FetchHelper(opt, project, *args, **kwargs)
David James8d201162013-10-11 17:03:19 -0700266 if not success and not opt.force_broken:
267 break
268
269 def _FetchHelper(self, opt, project, lock, fetched, pm, sem, err_event):
270 """Fetch git objects for a single project.
271
David Pursehousec1b86a22012-11-14 11:36:51 +0900272 Args:
273 opt: Program options returned from optparse. See _Options().
274 project: Project object for the project to fetch.
275 lock: Lock for accessing objects that are shared amongst multiple
276 _FetchHelper() threads.
277 fetched: set object that we will add project.gitdir to when we're done
278 (with our lock held).
279 pm: Instance of a Project object. We will call pm.update() (with our
280 lock held).
281 sem: We'll release() this semaphore when we exit so that another thread
282 can be started up.
283 err_event: We'll set this event in the case of an error (after printing
284 out info about the error).
David James8d201162013-10-11 17:03:19 -0700285
286 Returns:
287 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900288 """
289 # We'll set to true once we've locked the lock.
290 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700291
Chirayu Desaifef4ae72013-04-12 14:54:32 +0530292 if not opt.quiet:
293 print('Fetching project %s' % project.name)
294
David Pursehousec1b86a22012-11-14 11:36:51 +0900295 # Encapsulate everything in a try/except/finally so that:
296 # - We always set err_event in the case of an exception.
297 # - We always make sure we call sem.release().
298 # - We always make sure we unlock the lock if we locked it.
299 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700300 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900301 start = time.time()
302 success = project.Sync_NetworkHalf(
303 quiet=opt.quiet,
304 current_branch_only=opt.current_branch_only,
Kevin Degiabaa7f32014-11-12 11:27:45 -0700305 force_sync=opt.force_sync,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700306 clone_bundle=not opt.no_clone_bundle,
David Pursehouseb1553542014-09-04 21:28:09 +0900307 no_tags=opt.no_tags, archive=self.manifest.IsArchive,
308 optimized_fetch=opt.optimized_fetch)
David Pursehousec1b86a22012-11-14 11:36:51 +0900309 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700310
David Pursehousec1b86a22012-11-14 11:36:51 +0900311 # Lock around all the rest of the code, since printing, updating a set
312 # and Progress.update() are not thread safe.
313 lock.acquire()
314 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700315
David Pursehousec1b86a22012-11-14 11:36:51 +0900316 if not success:
317 print('error: Cannot fetch %s' % project.name, file=sys.stderr)
318 if opt.force_broken:
319 print('warn: --force-broken, continuing to sync',
320 file=sys.stderr)
321 else:
322 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700323
David Pursehousec1b86a22012-11-14 11:36:51 +0900324 fetched.add(project.gitdir)
325 pm.update()
326 except _FetchError:
327 err_event.set()
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400328 except Exception as e:
329 print('error: Cannot fetch %s (%s: %s)' \
330 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900331 err_event.set()
332 raise
333 finally:
334 if did_lock:
335 lock.release()
336 sem.release()
Roy Lee18afd7f2010-05-09 04:32:08 +0800337
David James8d201162013-10-11 17:03:19 -0700338 return success
339
Shawn O. Pearce16614f82010-10-29 12:05:43 -0700340 def _Fetch(self, projects, opt):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700341 fetched = set()
David James89ece422014-01-09 18:51:58 -0800342 lock = _threading.Lock()
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700343 pm = Progress('Fetching projects', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800344
David James89ece422014-01-09 18:51:58 -0800345 objdir_project_map = dict()
346 for project in projects:
347 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700348
David James89ece422014-01-09 18:51:58 -0800349 threads = set()
350 sem = _threading.Semaphore(self.jobs)
351 err_event = _threading.Event()
352 for project_list in objdir_project_map.values():
353 # Check for any errors before running any more tasks.
354 # ...we'll let existing threads finish, though.
355 if err_event.isSet() and not opt.force_broken:
356 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700357
David James89ece422014-01-09 18:51:58 -0800358 sem.acquire()
359 kwargs = dict(opt=opt,
360 projects=project_list,
361 lock=lock,
362 fetched=fetched,
363 pm=pm,
364 sem=sem,
365 err_event=err_event)
366 if self.jobs > 1:
David James8d201162013-10-11 17:03:19 -0700367 t = _threading.Thread(target = self._FetchProjectList,
David James89ece422014-01-09 18:51:58 -0800368 kwargs = kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200369 # Ensure that Ctrl-C will not freeze the repo process.
370 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800371 threads.add(t)
372 t.start()
David James89ece422014-01-09 18:51:58 -0800373 else:
374 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800375
David James89ece422014-01-09 18:51:58 -0800376 for t in threads:
377 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800378
David James89ece422014-01-09 18:51:58 -0800379 # If we saw an error, exit with code 1 so that other scripts can check.
380 if err_event.isSet():
381 print('\nerror: Exited sync due to fetch errors', file=sys.stderr)
382 sys.exit(1)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700383
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700384 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700385 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700386
Julien Campergue335f5ef2013-10-16 11:02:35 +0200387 if not self.manifest.IsArchive:
388 self._GCProjects(projects)
389
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700390 return fetched
391
Dave Borowitz18857212012-10-23 17:02:59 -0700392 def _GCProjects(self, projects):
David James8d201162013-10-11 17:03:19 -0700393 gitdirs = {}
394 for project in projects:
395 gitdirs[project.gitdir] = project.bare_git
396
Dave Borowitze2152672012-10-31 12:24:38 -0700397 has_dash_c = git_require((1, 7, 2))
398 if multiprocessing and has_dash_c:
Dave Borowitz18857212012-10-23 17:02:59 -0700399 cpu_count = multiprocessing.cpu_count()
400 else:
401 cpu_count = 1
402 jobs = min(self.jobs, cpu_count)
403
404 if jobs < 2:
David James8d201162013-10-11 17:03:19 -0700405 for bare_git in gitdirs.values():
406 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700407 return
408
409 config = {'pack.threads': cpu_count / jobs if cpu_count > jobs else 1}
410
411 threads = set()
412 sem = _threading.Semaphore(jobs)
413 err_event = _threading.Event()
414
David James8d201162013-10-11 17:03:19 -0700415 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700416 try:
417 try:
David James8d201162013-10-11 17:03:19 -0700418 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700419 except GitError:
420 err_event.set()
421 except:
422 err_event.set()
423 raise
424 finally:
425 sem.release()
426
David James8d201162013-10-11 17:03:19 -0700427 for bare_git in gitdirs.values():
Dave Borowitz18857212012-10-23 17:02:59 -0700428 if err_event.isSet():
429 break
430 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700431 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700432 t.daemon = True
433 threads.add(t)
434 t.start()
435
436 for t in threads:
437 t.join()
438
439 if err_event.isSet():
Sarah Owenscecd1d82012-11-01 22:59:27 -0700440 print('\nerror: Exited sync due to gc errors', file=sys.stderr)
Dave Borowitz18857212012-10-23 17:02:59 -0700441 sys.exit(1)
442
Tim Kilbourn07669002013-03-08 15:02:49 -0800443 def _ReloadManifest(self, manifest_name=None):
444 if manifest_name:
445 # Override calls _Unload already
446 self.manifest.Override(manifest_name)
447 else:
448 self.manifest._Unload()
449
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700450 def UpdateProjectList(self):
451 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700452 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700453 if project.relpath:
454 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700455 file_name = 'project.list'
456 file_path = os.path.join(self.manifest.repodir, file_name)
457 old_project_paths = []
458
459 if os.path.exists(file_path):
460 fd = open(file_path, 'r')
461 try:
462 old_project_paths = fd.read().split('\n')
463 finally:
464 fd.close()
465 for path in old_project_paths:
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700466 if not path:
467 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700468 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900469 # If the path has already been deleted, we don't need to do it
Anthonyf3fdf822009-09-26 13:38:52 -0400470 if os.path.exists(self.manifest.topdir + '/' + path):
David James8d201162013-10-11 17:03:19 -0700471 gitdir = os.path.join(self.manifest.topdir, path, '.git')
David Pursehousec1b86a22012-11-14 11:36:51 +0900472 project = Project(
473 manifest = self.manifest,
474 name = path,
475 remote = RemoteSpec('origin'),
David James8d201162013-10-11 17:03:19 -0700476 gitdir = gitdir,
477 objdir = gitdir,
David Pursehousec1b86a22012-11-14 11:36:51 +0900478 worktree = os.path.join(self.manifest.topdir, path),
479 relpath = path,
480 revisionExpr = 'HEAD',
481 revisionId = None,
482 groups = None)
Anthonyf3fdf822009-09-26 13:38:52 -0400483
David Pursehousec1b86a22012-11-14 11:36:51 +0900484 if project.IsDirty():
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900485 print('error: Cannot remove project "%s": uncommitted changes '
David Pursehousec1b86a22012-11-14 11:36:51 +0900486 'are present' % project.relpath, file=sys.stderr)
487 print(' commit changes, then run sync again',
488 file=sys.stderr)
489 return -1
490 else:
491 print('Deleting obsolete path %s' % project.worktree,
492 file=sys.stderr)
493 shutil.rmtree(project.worktree)
494 # Try deleting parent subdirs if they are empty
495 project_dir = os.path.dirname(project.worktree)
496 while project_dir != self.manifest.topdir:
497 try:
498 os.rmdir(project_dir)
499 except OSError:
500 break
501 project_dir = os.path.dirname(project_dir)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700502
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700503 new_project_paths.sort()
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700504 fd = open(file_path, 'w')
505 try:
506 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700507 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700508 finally:
509 fd.close()
510 return 0
511
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700512 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800513 if opt.jobs:
514 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700515 if self.jobs > 1:
516 soft_limit, _ = _rlimit_nofile()
517 self.jobs = min(self.jobs, (soft_limit - 5) / 3)
518
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700519 if opt.network_only and opt.detach_head:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700520 print('error: cannot combine -n and -d', file=sys.stderr)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700521 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700522 if opt.network_only and opt.local_only:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700523 print('error: cannot combine -n and -l', file=sys.stderr)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700524 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500525 if opt.manifest_name and opt.smart_sync:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700526 print('error: cannot combine -m and -s', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500527 sys.exit(1)
528 if opt.manifest_name and opt.smart_tag:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700529 print('error: cannot combine -m and -t', file=sys.stderr)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500530 sys.exit(1)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900531 if opt.manifest_server_username or opt.manifest_server_password:
532 if not (opt.smart_sync or opt.smart_tag):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700533 print('error: -u and -p may only be combined with -s or -t',
534 file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900535 sys.exit(1)
536 if None in [opt.manifest_server_username, opt.manifest_server_password]:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700537 print('error: both -u and -p must be given', file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900538 sys.exit(1)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500539
540 if opt.manifest_name:
541 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700542
Chirayu Desaia892b102013-06-11 14:18:46 +0530543 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900544 smart_sync_manifest_name = "smart_sync_override.xml"
545 smart_sync_manifest_path = os.path.join(
546 self.manifest.manifestProject.worktree, smart_sync_manifest_name)
Chirayu Desaia892b102013-06-11 14:18:46 +0530547
Victor Boivie08c880d2011-04-19 10:32:52 +0200548 if opt.smart_sync or opt.smart_tag:
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700549 if not self.manifest.manifest_server:
David Pursehouse2f9e7e42013-03-05 17:26:46 +0900550 print('error: cannot smart sync: no manifest server defined in '
Sarah Owenscecd1d82012-11-01 22:59:27 -0700551 'manifest', file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700552 sys.exit(1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900553
554 manifest_server = self.manifest.manifest_server
David Pursehousefb99c712013-09-25 11:09:34 +0900555 if not opt.quiet:
556 print('Using manifest server %s' % manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900557
David Pursehouse86d973d2012-08-24 10:21:02 +0900558 if not '@' in manifest_server:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900559 username = None
560 password = None
561 if opt.manifest_server_username and opt.manifest_server_password:
562 username = opt.manifest_server_username
563 password = opt.manifest_server_password
David Pursehouse86d973d2012-08-24 10:21:02 +0900564 else:
565 try:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900566 info = netrc.netrc()
567 except IOError:
David Pursehouseba7bc732015-08-20 16:55:42 +0900568 # .netrc file does not exist or could not be opened
569 pass
David Pursehouse86d973d2012-08-24 10:21:02 +0900570 else:
David Pursehousecf76b1b2012-09-14 10:31:42 +0900571 try:
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +0530572 parse_result = urllib.parse.urlparse(manifest_server)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900573 if parse_result.hostname:
David Pursehouseba7bc732015-08-20 16:55:42 +0900574 auth = info.authenticators(parse_result.hostname)
575 if auth:
576 username, _account, password = auth
577 else:
578 print('No credentials found for %s in .netrc'
579 % parse_result.hostname, file=sys.stderr)
Sarah Owensa5be53f2012-09-09 15:37:57 -0700580 except netrc.NetrcParseError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700581 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
David Pursehousecf76b1b2012-09-14 10:31:42 +0900582
583 if (username and password):
584 manifest_server = manifest_server.replace('://', '://%s:%s@' %
585 (username, password),
586 1)
David Pursehouse86d973d2012-08-24 10:21:02 +0900587
Dan Willemsen0745bb22015-08-17 13:41:45 -0700588 transport = PersistentTransport(manifest_server)
589 if manifest_server.startswith('persistent-'):
590 manifest_server = manifest_server[len('persistent-'):]
591
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700592 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -0700593 server = xmlrpc.client.Server(manifest_server, transport=transport)
Victor Boivie08c880d2011-04-19 10:32:52 +0200594 if opt.smart_sync:
595 p = self.manifest.manifestProject
596 b = p.GetBranch(p.CurrentBranch)
597 branch = b.merge
598 if branch.startswith(R_HEADS):
599 branch = branch[len(R_HEADS):]
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700600
Victor Boivie08c880d2011-04-19 10:32:52 +0200601 env = os.environ.copy()
Jeff Davidson5cf16602014-10-02 10:13:38 -0700602 if 'SYNC_TARGET' in env:
603 target = env['SYNC_TARGET']
604 [success, manifest_str] = server.GetApprovedManifest(branch, target)
605 elif 'TARGET_PRODUCT' in env and 'TARGET_BUILD_VARIANT' in env:
Victor Boivie08c880d2011-04-19 10:32:52 +0200606 target = '%s-%s' % (env['TARGET_PRODUCT'],
607 env['TARGET_BUILD_VARIANT'])
608 [success, manifest_str] = server.GetApprovedManifest(branch, target)
609 else:
610 [success, manifest_str] = server.GetApprovedManifest(branch)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700611 else:
Victor Boivie08c880d2011-04-19 10:32:52 +0200612 assert(opt.smart_tag)
613 [success, manifest_str] = server.GetManifest(opt.smart_tag)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700614
615 if success:
David Pursehouse59b41742015-05-07 14:36:09 +0900616 manifest_name = smart_sync_manifest_name
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700617 try:
David Pursehouse59b41742015-05-07 14:36:09 +0900618 f = open(smart_sync_manifest_path, 'w')
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700619 try:
620 f.write(manifest_str)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700621 finally:
622 f.close()
David Pursehouse727cc3e2015-05-07 14:16:49 +0900623 except IOError as e:
624 print('error: cannot write manifest to %s:\n%s'
David Pursehouse59b41742015-05-07 14:36:09 +0900625 % (smart_sync_manifest_path, e),
Sarah Owenscecd1d82012-11-01 22:59:27 -0700626 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700627 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100628 self._ReloadManifest(manifest_name)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700629 else:
David Pursehouse351fe2c2013-09-25 17:54:26 +0900630 print('error: manifest server RPC call failed: %s' %
631 manifest_str, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700632 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530633 except (socket.error, IOError, xmlrpc.client.Fault) as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700634 print('error: cannot connect to manifest server %s:\n%s'
635 % (self.manifest.manifest_server, e), file=sys.stderr)
David Pursehousebd489c42012-08-23 10:21:26 +0900636 sys.exit(1)
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530637 except xmlrpc.client.ProtocolError as e:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700638 print('error: cannot connect to manifest server %s:\n%d %s'
639 % (self.manifest.manifest_server, e.errcode, e.errmsg),
640 file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700641 sys.exit(1)
David Pursehouse59b41742015-05-07 14:36:09 +0900642 else: # Not smart sync or smart tag mode
643 if os.path.isfile(smart_sync_manifest_path):
644 try:
645 os.remove(smart_sync_manifest_path)
646 except OSError as e:
647 print('error: failed to remove existing smart sync override manifest: %s' %
648 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700649
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700650 rp = self.manifest.repoProject
651 rp.PreSync()
652
653 mp = self.manifest.manifestProject
654 mp.PreSync()
655
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800656 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700657 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800658
Nico Sallembien9bb18162009-12-07 15:38:01 -0800659 if not opt.local_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700660 mp.Sync_NetworkHalf(quiet=opt.quiet,
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700661 current_branch_only=opt.current_branch_only,
David Pursehouseb1553542014-09-04 21:28:09 +0900662 no_tags=opt.no_tags,
663 optimized_fetch=opt.optimized_fetch)
Nico Sallembien9bb18162009-12-07 15:38:01 -0800664
665 if mp.HasChanges:
666 syncbuf = SyncBuffer(mp.config)
667 mp.Sync_LocalHalf(syncbuf)
668 if not syncbuf.Finish():
669 sys.exit(1)
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100670 self._ReloadManifest(manifest_name)
Shawn O. Pearcec4657962011-09-26 09:08:01 -0700671 if opt.jobs is None:
672 self.jobs = self.manifest.default.sync_j
Simran Basib9a1b732015-08-20 12:19:28 -0700673
Simran Basib9a1b732015-08-20 12:19:28 -0700674 if self.gitc_manifest:
675 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700676 missing_ok=True)
677 gitc_projects = []
678 opened_projects = []
679 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700680 if project.relpath in self.gitc_manifest.paths and \
681 self.gitc_manifest.paths[project.relpath].old_revision:
682 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700683 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700684 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700685
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700686 if not args:
687 gitc_projects = None
688
689 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700690 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700691 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
692 if manifest_name:
693 manifest.Override(manifest_name)
694 else:
695 manifest.Override(self.manifest.manifestFile)
696 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
697 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700698 gitc_projects)
699 print('GITC client successfully synced.')
700
701 # The opened projects need to be synced as normal, therefore we
702 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700703 # TODO: make this more reliable -- if there's a project name/path overlap,
704 # this may choose the wrong project.
705 args = [os.path.relpath(self.manifest.paths[p].worktree, os.getcwd())
706 for p in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700707 if not args:
708 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800709 all_projects = self.GetProjects(args,
710 missing_ok=True,
711 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700712
Dave Borowitz67700e92012-10-23 15:00:54 -0700713 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700714 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700715 to_fetch = []
716 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700717 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700718 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900719 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700720 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700721
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800722 fetched = self._Fetch(to_fetch, opt)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700723 _PostRepoFetch(rp, opt.no_repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700724 if opt.network_only:
725 # bail out now; the rest touches the working tree
726 return
727
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800728 # Iteratively fetch missing and/or nested unregistered submodules
729 previously_missing_set = set()
730 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100731 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800732 all_projects = self.GetProjects(args,
733 missing_ok=True,
734 submodules_ok=opt.fetch_submodules)
735 missing = []
736 for project in all_projects:
737 if project.gitdir not in fetched:
738 missing.append(project)
739 if not missing:
740 break
741 # Stop us from non-stopped fetching actually-missing repos: If set of
742 # missing repos has not been changed from last fetch, we break.
743 missing_set = set(p.name for p in missing)
744 if previously_missing_set == missing_set:
745 break
746 previously_missing_set = missing_set
747 fetched.update(self._Fetch(missing, opt))
748
Julien Campergue335f5ef2013-10-16 11:02:35 +0200749 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700750 # bail out now, we have no working tree
751 return
752
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700753 if self.UpdateProjectList():
754 sys.exit(1)
755
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700756 syncbuf = SyncBuffer(mp.config,
757 detach_head = opt.detach_head)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900758 pm = Progress('Syncing work tree', len(all_projects))
759 for project in all_projects:
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700760 pm.update()
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800761 if project.worktree:
Kevin Degiabaa7f32014-11-12 11:27:45 -0700762 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700763 pm.end()
Sarah Owenscecd1d82012-11-01 22:59:27 -0700764 print(file=sys.stderr)
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700765 if not syncbuf.Finish():
766 sys.exit(1)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700767
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700768 # If there's a notice that's supposed to print at the end of the sync, print
769 # it now...
770 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700771 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700772
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700773def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800774 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700775 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700776 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800777 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700778 if project.Exists:
779 project.PostRepoUpgrade()
780
781def _PostRepoFetch(rp, no_repo_verify=False, verbose=False):
782 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700783 print('info: A new version of repo is available', file=sys.stderr)
784 print(file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700785 if no_repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700786 syncbuf = SyncBuffer(rp.config)
787 rp.Sync_LocalHalf(syncbuf)
788 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700789 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700790 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700791 raise RepoChangedException(['--repo-upgraded'])
792 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700793 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700794 else:
795 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700796 print('repo version %s is current' % rp.work_git.describe(HEAD),
797 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700798
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700799def _VerifyTag(project):
800 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
801 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -0700802 print('warning: GnuPG was not available during last "repo init"\n'
803 'warning: Cannot automatically authenticate repo."""',
804 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700805 return True
806
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700807 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700808 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700809 except GitError:
810 cur = None
811
812 if not cur \
813 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700814 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700815 if rev.startswith(R_HEADS):
816 rev = rev[len(R_HEADS):]
817
Sarah Owenscecd1d82012-11-01 22:59:27 -0700818 print(file=sys.stderr)
819 print("warning: project '%s' branch '%s' is not signed"
820 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821 return False
822
Shawn O. Pearcef18cb762010-12-07 11:41:05 -0800823 env = os.environ.copy()
824 env['GIT_DIR'] = project.gitdir.encode()
825 env['GNUPGHOME'] = gpg_dir.encode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700826
827 cmd = [GIT, 'tag', '-v', cur]
828 proc = subprocess.Popen(cmd,
829 stdout = subprocess.PIPE,
830 stderr = subprocess.PIPE,
831 env = env)
832 out = proc.stdout.read()
833 proc.stdout.close()
834
835 err = proc.stderr.read()
836 proc.stderr.close()
837
838 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700839 print(file=sys.stderr)
840 print(out, file=sys.stderr)
841 print(err, file=sys.stderr)
842 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700843 return False
844 return True
Dave Borowitz67700e92012-10-23 15:00:54 -0700845
846class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -0700847 _ALPHA = 0.5
848
Dave Borowitz67700e92012-10-23 15:00:54 -0700849 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +0100850 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -0700851 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -0700852 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -0700853
854 def Get(self, project):
855 self._Load()
856 return self._times.get(project.name, _ONE_DAY_S)
857
858 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -0700859 self._Load()
860 name = project.name
861 old = self._times.get(name, t)
862 self._seen.add(name)
863 a = self._ALPHA
864 self._times[name] = (a*t) + ((1-a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -0700865
866 def _Load(self):
867 if self._times is None:
868 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100869 f = open(self._path)
Dave Borowitz67700e92012-10-23 15:00:54 -0700870 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100871 self._times = json.load(f)
872 finally:
873 f.close()
874 except (IOError, ValueError):
875 try:
876 os.remove(self._path)
877 except OSError:
878 pass
879 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -0700880
881 def Save(self):
882 if self._times is None:
883 return
Dave Borowitzd9478582012-10-23 16:35:39 -0700884
885 to_delete = []
886 for name in self._times:
887 if name not in self._seen:
888 to_delete.append(name)
889 for name in to_delete:
890 del self._times[name]
891
Dave Borowitz67700e92012-10-23 15:00:54 -0700892 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100893 f = open(self._path, 'w')
Dave Borowitz67700e92012-10-23 15:00:54 -0700894 try:
Anthony King85b24ac2014-05-06 15:57:48 +0100895 json.dump(self._times, f, indent=2)
896 finally:
897 f.close()
898 except (IOError, TypeError):
899 try:
900 os.remove(self._path)
901 except OSError:
902 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -0700903
904# This is a replacement for xmlrpc.client.Transport using urllib2
905# and supporting persistent-http[s]. It cannot change hosts from
906# request to request like the normal transport, the real url
907# is passed during initialization.
908class PersistentTransport(xmlrpc.client.Transport):
909 def __init__(self, orig_host):
910 self.orig_host = orig_host
911
912 def request(self, host, handler, request_body, verbose=False):
913 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
914 # Python doesn't understand cookies with the #HttpOnly_ prefix
915 # Since we're only using them for HTTP, copy the file temporarily,
916 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700917 if cookiefile:
918 tmpcookiefile = tempfile.NamedTemporaryFile()
919 try:
920 with open(cookiefile) as f:
921 for line in f:
922 if line.startswith("#HttpOnly_"):
923 line = line[len("#HttpOnly_"):]
924 tmpcookiefile.write(line)
925 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700926
Dan Willemsen3010e5b2015-08-20 10:09:20 -0700927 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
928 cookiejar.load()
929 finally:
930 tmpcookiefile.close()
931 else:
932 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -0700933
934 proxyhandler = urllib.request.ProxyHandler
935 if proxy:
936 proxyhandler = urllib.request.ProxyHandler({
937 "http": proxy,
938 "https": proxy })
939
940 opener = urllib.request.build_opener(
941 urllib.request.HTTPCookieProcessor(cookiejar),
942 proxyhandler)
943
944 url = urllib.parse.urljoin(self.orig_host, handler)
945 parse_results = urllib.parse.urlparse(url)
946
947 scheme = parse_results.scheme
948 if scheme == 'persistent-http':
949 scheme = 'http'
950 if scheme == 'persistent-https':
951 # If we're proxying through persistent-https, use http. The
952 # proxy itself will do the https.
953 if proxy:
954 scheme = 'http'
955 else:
956 scheme = 'https'
957
958 # Parse out any authentication information using the base class
959 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
960
961 url = urllib.parse.urlunparse((
962 scheme,
963 host,
964 parse_results.path,
965 parse_results.params,
966 parse_results.query,
967 parse_results.fragment))
968
969 request = urllib.request.Request(url, request_body)
970 if extra_headers is not None:
971 for (name, header) in extra_headers:
972 request.add_header(name, header)
973 request.add_header('Content-Type', 'text/xml')
974 try:
975 response = opener.open(request)
976 except urllib.error.HTTPError as e:
977 if e.code == 501:
978 # We may have been redirected through a login process
979 # but our POST turned into a GET. Retry.
980 response = opener.open(request)
981 else:
982 raise
983
984 p, u = xmlrpc.client.getparser()
985 while 1:
986 data = response.read(1024)
987 if not data:
988 break
989 p.feed(data)
990 p.close()
991 return u.close()
992
993 def close(self):
994 pass
995