blob: efd396163d2d99082d8cce0723b912fd1bd9913c [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Mike Frysinger979d5bd2020-02-09 02:28:34 -050018
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
David Pursehouse86d973d2012-08-24 10:21:02 +090020import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070021from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
23import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import subprocess
26import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070027import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070028import time
David Pursehouse59bbb582013-05-17 10:49:33 +090029
30from pyversion import is_python3
31if is_python3():
Dan Willemsen0745bb22015-08-17 13:41:45 -070032 import http.cookiejar as cookielib
33 import urllib.error
Chirayu Desai217ea7d2013-03-01 19:14:38 +053034 import urllib.parse
Dan Willemsen0745bb22015-08-17 13:41:45 -070035 import urllib.request
David Pursehouse59bbb582013-05-17 10:49:33 +090036 import xmlrpc.client
37else:
Dan Willemsen0745bb22015-08-17 13:41:45 -070038 import cookielib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053039 import imp
Dan Willemsen0745bb22015-08-17 13:41:45 -070040 import urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053041 import urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090042 import xmlrpclib
Chirayu Desai217ea7d2013-03-01 19:14:38 +053043 urllib = imp.new_module('urllib')
Dan Willemsen0745bb22015-08-17 13:41:45 -070044 urllib.error = urllib2
Chirayu Desaidb2ad9d2013-06-11 13:42:25 +053045 urllib.parse = urlparse
Dan Willemsen0745bb22015-08-17 13:41:45 -070046 urllib.request = urllib2
Chirayu Desai217ea7d2013-03-01 19:14:38 +053047 xmlrpc = imp.new_module('xmlrpc')
48 xmlrpc.client = xmlrpclib
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070049
Roy Lee18afd7f2010-05-09 04:32:08 +080050try:
51 import threading as _threading
52except ImportError:
53 import dummy_threading as _threading
54
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070055try:
56 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090057
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070058 def _rlimit_nofile():
59 return resource.getrlimit(resource.RLIMIT_NOFILE)
60except ImportError:
61 def _rlimit_nofile():
62 return (256, 256)
63
Dave Borowitz18857212012-10-23 17:02:59 -070064try:
65 import multiprocessing
66except ImportError:
67 multiprocessing = None
68
David Rileye0684ad2017-04-05 00:02:59 -070069import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070070from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090071from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090072from git_refs import R_HEADS, HEAD
Simran Basibdb52712015-08-10 13:23:23 -070073import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070074from project import Project
75from project import RemoteSpec
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080076from command import Command, MirrorSafeCommand
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +000077from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070078import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070079from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070080from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080081from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070082from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070083
Dave Borowitz67700e92012-10-23 15:00:54 -070084_ONE_DAY_S = 24 * 60 * 60
85
David Pursehouse819827a2020-02-12 15:20:19 +090086
Doug Andersonfc06ced2011-03-16 15:49:18 -070087class _FetchError(Exception):
88 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
89 pass
90
David Pursehouse819827a2020-02-12 15:20:19 +090091
Xin Li745be2e2019-06-03 11:24:30 -070092class _CheckoutError(Exception):
93 """Internal error thrown in _CheckoutOne() when we don't want stack trace."""
94
David Pursehouse819827a2020-02-12 15:20:19 +090095
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080096class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080097 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070098 common = True
99 helpSummary = "Update working tree to the latest revision"
100 helpUsage = """
101%prog [<project>...]
102"""
103 helpDescription = """
104The '%prog' command synchronizes local project directories
105with the remote repositories specified in the manifest. If a local
106project does not yet exist, it will clone a new local directory from
107the remote repository and set up tracking branches as specified in
108the manifest. If the local project already exists, '%prog'
109will update the remote branches and rebase any new local changes
110on top of the new remote changes.
111
112'%prog' will synchronize all projects listed at the command
113line. Projects can be specified either by name, or by a relative
114or absolute path to the project's local directory. If no projects
115are specified, '%prog' will synchronize all projects listed in
116the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700117
118The -d/--detach option can be used to switch specified projects
119back to the manifest revision. This option is especially helpful
120if the project is currently on a topic branch, but the manifest
121revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700122
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700123The -s/--smart-sync option can be used to sync to a known good
124build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200125manifest. The -t/--smart-tag option is similar and allows you to
126specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700127
David Pursehousecf76b1b2012-09-14 10:31:42 +0900128The -u/--manifest-server-username and -p/--manifest-server-password
129options can be used to specify a username and password to authenticate
130with the manifest server when using the -s or -t option.
131
132If -u and -p are not specified when using the -s or -t option, '%prog'
133will attempt to read authentication credentials for the manifest server
134from the user's .netrc file.
135
136'%prog' will not use authentication credentials from -u/-p or .netrc
137if the manifest server specified in the manifest file already includes
138credentials.
139
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400140By default, all projects will be synced. The --fail-fast option can be used
141to halt syncing as soon as possible when the the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500142
Kevin Degiabaa7f32014-11-12 11:27:45 -0700143The --force-sync option can be used to overwrite existing git
144directories if they have previously been linked to a different
145object direcotry. WARNING: This may cause data to be lost since
146refs may be removed when overwriting.
147
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500148The --force-remove-dirty option can be used to remove previously used
149projects with uncommitted changes. WARNING: This may cause data to be
150lost since uncommitted changes may be removed with projects that no longer
151exist in the manifest.
152
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700153The --no-clone-bundle option disables any attempt to use
154$URL/clone.bundle to bootstrap a new Git repository from a
155resumeable bundle file on a content delivery network. This
156may be necessary if there are problems with the local Python
157HTTP client or proxy configuration, but the Git binary works.
158
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800159The --fetch-submodules option enables fetching Git submodules
160of a project from server.
161
David Pursehousef2fad612015-01-29 14:36:28 +0900162The -c/--current-branch option can be used to only fetch objects that
163are on the branch specified by a project's revision.
164
David Pursehouseb1553542014-09-04 21:28:09 +0900165The --optimized-fetch option can be used to only fetch projects that
166are fixed to a sha1 revision if the sha1 revision does not already
167exist locally.
168
David Pursehouse74cfd272015-10-14 10:50:15 +0900169The --prune option can be used to remove any refs that no longer
170exist on the remote.
171
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400172# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700173
174If at least one project remote URL uses an SSH connection (ssh://,
175git+ssh://, or user@host:path syntax) repo will automatically
176enable the SSH ControlMaster option when connecting to that host.
177This feature permits other projects in the same '%prog' session to
178reuse the same SSH tunnel, saving connection setup overheads.
179
180To disable this behavior on UNIX platforms, set the GIT_SSH
181environment variable to 'ssh'. For example:
182
183 export GIT_SSH=ssh
184 %prog
185
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400186# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700187
188This feature is automatically disabled on Windows, due to the lack
189of UNIX domain socket support.
190
191This feature is not compatible with url.insteadof rewrites in the
192user's ~/.gitconfig. '%prog' is currently not able to perform the
193rewrite early enough to establish the ControlMaster tunnel.
194
195If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
196later is required to fix a server side protocol bug.
197
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700198"""
199
Nico Sallembien6623b212010-05-11 12:57:01 -0700200 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000201 try:
202 self.jobs = self.manifest.default.sync_j
203 except ManifestParseError:
204 self.jobs = 1
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700205
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500206 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200207 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400208 help='obsolete option (to be deleted in the future)')
209 p.add_option('--fail-fast',
210 dest='fail_fast', action='store_true',
211 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700212 p.add_option('--force-sync',
213 dest='force_sync', action='store_true',
214 help="overwrite an existing git directory if it needs to "
215 "point to a different object directory. WARNING: this "
216 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500217 p.add_option('--force-remove-dirty',
218 dest='force_remove_dirty', action='store_true',
219 help="force remove projects with uncommitted modifications if "
220 "projects no longer exist in the manifest. "
221 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900222 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700223 dest='local_only', action='store_true',
224 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900225 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100226 dest='mp_update', action='store_false', default='true',
227 help='use the existing manifest checkout as-is. '
228 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900229 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700230 dest='network_only', action='store_true',
231 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900232 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700233 dest='detach_head', action='store_true',
234 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900235 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700236 dest='current_branch_only', action='store_true',
237 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500238 p.add_option('-v', '--verbose',
239 dest='output_mode', action='store_true',
240 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900241 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500242 dest='output_mode', action='store_false',
243 help='only show errors')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900244 p.add_option('-j', '--jobs',
Roy Lee18afd7f2010-05-09 04:32:08 +0800245 dest='jobs', action='store', type='int',
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700246 help="projects to fetch simultaneously (default %d)" % self.jobs)
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500247 p.add_option('-m', '--manifest-name',
248 dest='manifest_name',
249 help='temporary manifest to use for this sync', metavar='NAME.xml')
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700250 p.add_option('--no-clone-bundle',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500251 dest='clone_bundle', default=True, action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700252 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800253 p.add_option('-u', '--manifest-server-username', action='store',
254 dest='manifest_server_username',
255 help='username to authenticate with the manifest server')
256 p.add_option('-p', '--manifest-server-password', action='store',
257 dest='manifest_server_password',
258 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800259 p.add_option('--fetch-submodules',
260 dest='fetch_submodules', action='store_true',
261 help='fetch submodules from server')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700262 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500263 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700264 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900265 p.add_option('--optimized-fetch',
266 dest='optimized_fetch', action='store_true',
267 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600268 p.add_option('--retry-fetches',
269 default=0, action='store', type='int',
270 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900271 p.add_option('--prune', dest='prune', action='store_true',
272 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700273 if show_smart:
274 p.add_option('-s', '--smart-sync',
275 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900276 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200277 p.add_option('-t', '--smart-tag',
278 dest='smart_tag', action='store',
279 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700280
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700281 g = p.add_option_group('repo Version options')
282 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500283 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700284 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700285 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800286 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700287 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700288
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500289 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700290 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800291
David James8d201162013-10-11 17:03:19 -0700292 Delegates most of the work to _FetchHelper.
293
294 Args:
295 opt: Program options returned from optparse. See _Options().
296 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500297 sem: We'll release() this semaphore when we exit so that another thread
298 can be started up.
David James89ece422014-01-09 18:51:58 -0800299 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700300 _FetchHelper docstring for details.
301 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500302 try:
303 for project in projects:
304 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400305 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500306 break
307 finally:
308 sem.release()
David James8d201162013-10-11 17:03:19 -0700309
Xin Li745be2e2019-06-03 11:24:30 -0700310 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
311 clone_filter):
David James8d201162013-10-11 17:03:19 -0700312 """Fetch git objects for a single project.
313
David Pursehousec1b86a22012-11-14 11:36:51 +0900314 Args:
315 opt: Program options returned from optparse. See _Options().
316 project: Project object for the project to fetch.
317 lock: Lock for accessing objects that are shared amongst multiple
318 _FetchHelper() threads.
319 fetched: set object that we will add project.gitdir to when we're done
320 (with our lock held).
321 pm: Instance of a Project object. We will call pm.update() (with our
322 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900323 err_event: We'll set this event in the case of an error (after printing
324 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700325 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700326
327 Returns:
328 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900329 """
330 # We'll set to true once we've locked the lock.
331 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700332
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 # Encapsulate everything in a try/except/finally so that:
334 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900335 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700336 start = time.time()
337 success = False
David Pursehousec1b86a22012-11-14 11:36:51 +0900338 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700339 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900340 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900341 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500342 verbose=opt.verbose,
David Pursehouseabdf7502020-02-12 14:58:39 +0900343 current_branch_only=opt.current_branch_only,
344 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500345 clone_bundle=opt.clone_bundle,
346 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900347 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600348 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900349 prune=opt.prune,
350 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900351 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700352
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 # Lock around all the rest of the code, since printing, updating a set
354 # and Progress.update() are not thread safe.
355 lock.acquire()
356 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700357
David Pursehousec1b86a22012-11-14 11:36:51 +0900358 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800359 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700360 print('error: Cannot fetch %s from %s'
361 % (project.name, project.remote.url),
362 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400363 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900364 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700365
David Pursehousec1b86a22012-11-14 11:36:51 +0900366 fetched.add(project.gitdir)
Mike Frysinger3538dd22019-08-26 15:32:06 -0400367 pm.update(msg=project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900368 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800369 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400370 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900371 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900372 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900373 err_event.set()
374 raise
375 finally:
376 if did_lock:
377 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700378 finish = time.time()
379 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
380 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800381
David James8d201162013-10-11 17:03:19 -0700382 return success
383
Mike Frysinger5a033082019-09-23 19:21:20 -0400384 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700385 fetched = set()
David James89ece422014-01-09 18:51:58 -0800386 lock = _threading.Lock()
Tim Schumacher913327f2017-06-05 15:01:41 +0200387 pm = Progress('Fetching projects', len(projects),
Tim Schumacher7be072e2017-06-28 18:29:23 +0200388 always_print_percentage=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800389
David James89ece422014-01-09 18:51:58 -0800390 objdir_project_map = dict()
391 for project in projects:
392 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700393
David James89ece422014-01-09 18:51:58 -0800394 threads = set()
395 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800396 for project_list in objdir_project_map.values():
397 # Check for any errors before running any more tasks.
398 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400399 if err_event.isSet() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800400 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700401
David James89ece422014-01-09 18:51:58 -0800402 sem.acquire()
403 kwargs = dict(opt=opt,
404 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500405 sem=sem,
David James89ece422014-01-09 18:51:58 -0800406 lock=lock,
407 fetched=fetched,
408 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700409 err_event=err_event,
410 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800411 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900412 t = _threading.Thread(target=self._FetchProjectList,
413 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200414 # Ensure that Ctrl-C will not freeze the repo process.
415 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800416 threads.add(t)
417 t.start()
David James89ece422014-01-09 18:51:58 -0800418 else:
419 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800420
David James89ece422014-01-09 18:51:58 -0800421 for t in threads:
422 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800423
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700424 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700425 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700426
Julien Campergue335f5ef2013-10-16 11:02:35 +0200427 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400428 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200429
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700430 return fetched
431
Xin Li745be2e2019-06-03 11:24:30 -0700432 def _CheckoutWorker(self, opt, sem, project, *args, **kwargs):
433 """Main function of the fetch threads.
434
435 Delegates most of the work to _CheckoutOne.
436
437 Args:
438 opt: Program options returned from optparse. See _Options().
439 projects: Projects to fetch.
440 sem: We'll release() this semaphore when we exit so that another thread
441 can be started up.
442 *args, **kwargs: Remaining arguments to pass to _CheckoutOne. See the
443 _CheckoutOne docstring for details.
444 """
445 try:
Mike Frysingera34186e2019-08-07 18:07:31 -0400446 return self._CheckoutOne(opt, project, *args, **kwargs)
Xin Li745be2e2019-06-03 11:24:30 -0700447 finally:
448 sem.release()
449
Vadim Bendeburydff91942019-11-06 11:05:00 -0800450 def _CheckoutOne(self, opt, project, lock, pm, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700451 """Checkout work tree for one project
452
453 Args:
454 opt: Program options returned from optparse. See _Options().
455 project: Project object for the project to checkout.
456 lock: Lock for accessing objects that are shared amongst multiple
457 _CheckoutWorker() threads.
458 pm: Instance of a Project object. We will call pm.update() (with our
459 lock held).
460 err_event: We'll set this event in the case of an error (after printing
461 out info about the error).
Vadim Bendeburydff91942019-11-06 11:05:00 -0800462 err_results: A list of strings, paths to git repos where checkout
463 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700464
465 Returns:
466 Whether the fetch was successful.
467 """
468 # We'll set to true once we've locked the lock.
469 did_lock = False
470
Xin Li745be2e2019-06-03 11:24:30 -0700471 # Encapsulate everything in a try/except/finally so that:
472 # - We always set err_event in the case of an exception.
473 # - We always make sure we unlock the lock if we locked it.
474 start = time.time()
475 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
476 detach_head=opt.detach_head)
477 success = False
478 try:
479 try:
480 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
Xin Li745be2e2019-06-03 11:24:30 -0700481
482 # Lock around all the rest of the code, since printing, updating a set
483 # and Progress.update() are not thread safe.
484 lock.acquire()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400485 success = syncbuf.Finish()
Xin Li745be2e2019-06-03 11:24:30 -0700486 did_lock = True
487
488 if not success:
489 err_event.set()
490 print('error: Cannot checkout %s' % (project.name),
491 file=sys.stderr)
492 raise _CheckoutError()
493
Mike Frysinger3538dd22019-08-26 15:32:06 -0400494 pm.update(msg=project.name)
Xin Li745be2e2019-06-03 11:24:30 -0700495 except _CheckoutError:
496 pass
497 except Exception as e:
498 print('error: Cannot checkout %s: %s: %s' %
499 (project.name, type(e).__name__, str(e)),
500 file=sys.stderr)
501 err_event.set()
502 raise
503 finally:
504 if did_lock:
Vadim Bendeburydff91942019-11-06 11:05:00 -0800505 if not success:
506 err_results.append(project.relpath)
Xin Li745be2e2019-06-03 11:24:30 -0700507 lock.release()
508 finish = time.time()
509 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
510 start, finish, success)
511
512 return success
513
Mike Frysinger5a033082019-09-23 19:21:20 -0400514 def _Checkout(self, all_projects, opt, err_event, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700515 """Checkout projects listed in all_projects
516
517 Args:
518 all_projects: List of all projects that should be checked out.
519 opt: Program options returned from optparse. See _Options().
Mike Frysinger5a033082019-09-23 19:21:20 -0400520 err_event: We'll set this event in the case of an error (after printing
521 out info about the error).
522 err_results: A list of strings, paths to git repos where checkout
523 failed.
Xin Li745be2e2019-06-03 11:24:30 -0700524 """
525
526 # Perform checkouts in multiple threads when we are using partial clone.
527 # Without partial clone, all needed git objects are already downloaded,
528 # in this situation it's better to use only one process because the checkout
529 # would be mostly disk I/O; with partial clone, the objects are only
530 # downloaded when demanded (at checkout time), which is similar to the
531 # Sync_NetworkHalf case and parallelism would be helpful.
532 if self.manifest.CloneFilter:
533 syncjobs = self.jobs
534 else:
535 syncjobs = 1
536
537 lock = _threading.Lock()
Mike Frysinger3538dd22019-08-26 15:32:06 -0400538 pm = Progress('Checking out projects', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700539
540 threads = set()
541 sem = _threading.Semaphore(syncjobs)
Xin Li745be2e2019-06-03 11:24:30 -0700542
543 for project in all_projects:
544 # Check for any errors before running any more tasks.
545 # ...we'll let existing threads finish, though.
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400546 if err_event.isSet() and opt.fail_fast:
Xin Li745be2e2019-06-03 11:24:30 -0700547 break
548
549 sem.acquire()
550 if project.worktree:
551 kwargs = dict(opt=opt,
552 sem=sem,
553 project=project,
554 lock=lock,
555 pm=pm,
Vadim Bendeburydff91942019-11-06 11:05:00 -0800556 err_event=err_event,
557 err_results=err_results)
Xin Li745be2e2019-06-03 11:24:30 -0700558 if syncjobs > 1:
559 t = _threading.Thread(target=self._CheckoutWorker,
560 kwargs=kwargs)
561 # Ensure that Ctrl-C will not freeze the repo process.
562 t.daemon = True
563 threads.add(t)
564 t.start()
565 else:
566 self._CheckoutWorker(**kwargs)
567
568 for t in threads:
569 t.join()
570
571 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700572
Mike Frysinger5a033082019-09-23 19:21:20 -0400573 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700574 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700575 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500576 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500577 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900578 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500579 print('%s: Shared project %s found, disabling pruning.' %
580 (project.relpath, project.name))
581 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500582 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500583 else:
584 # This isn't perfect, but it's the best we can do with old git.
585 print('%s: WARNING: shared projects are unreliable when using old '
586 'versions of git; please upgrade to git-2.7.0+.'
587 % (project.relpath,),
588 file=sys.stderr)
589 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700590 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700591
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500592 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700593 cpu_count = multiprocessing.cpu_count()
594 else:
595 cpu_count = 1
596 jobs = min(self.jobs, cpu_count)
597
598 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700599 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700600 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700601 return
602
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400603 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700604
605 threads = set()
606 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700607
David James8d201162013-10-11 17:03:19 -0700608 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700609 try:
610 try:
David James8d201162013-10-11 17:03:19 -0700611 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700612 except GitError:
613 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900614 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700615 err_event.set()
616 raise
617 finally:
618 sem.release()
619
Gabe Black2ff30292014-10-09 17:54:35 -0700620 for bare_git in gc_gitdirs.values():
Mike Frysinger5a033082019-09-23 19:21:20 -0400621 if err_event.isSet() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700622 break
623 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700624 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700625 t.daemon = True
626 threads.add(t)
627 t.start()
628
629 for t in threads:
630 t.join()
631
Tim Kilbourn07669002013-03-08 15:02:49 -0800632 def _ReloadManifest(self, manifest_name=None):
633 if manifest_name:
634 # Override calls _Unload already
635 self.manifest.Override(manifest_name)
636 else:
637 self.manifest._Unload()
638
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500639 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700640 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700641 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700642 if project.relpath:
643 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700644 file_name = 'project.list'
645 file_path = os.path.join(self.manifest.repodir, file_name)
646 old_project_paths = []
647
648 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500649 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700650 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800651 # In reversed order, so subfolders are deleted before parent folder.
652 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700653 if not path:
654 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700655 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900656 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700657 gitdir = os.path.join(self.manifest.topdir, path, '.git')
658 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900659 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900660 manifest=self.manifest,
661 name=path,
662 remote=RemoteSpec('origin'),
663 gitdir=gitdir,
664 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500665 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900666 worktree=os.path.join(self.manifest.topdir, path),
667 relpath=path,
668 revisionExpr='HEAD',
669 revisionId=None,
670 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500671 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900672 quiet=opt.quiet,
673 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400674 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700675
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700676 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500677 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700678 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700679 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700680 return 0
681
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400682 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
683 if not self.manifest.manifest_server:
684 print('error: cannot smart sync: no manifest server defined in '
685 'manifest', file=sys.stderr)
686 sys.exit(1)
687
688 manifest_server = self.manifest.manifest_server
689 if not opt.quiet:
690 print('Using manifest server %s' % manifest_server)
691
David Pursehouseeeff3532020-02-12 11:24:10 +0900692 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400693 username = None
694 password = None
695 if opt.manifest_server_username and opt.manifest_server_password:
696 username = opt.manifest_server_username
697 password = opt.manifest_server_password
698 else:
699 try:
700 info = netrc.netrc()
701 except IOError:
702 # .netrc file does not exist or could not be opened
703 pass
704 else:
705 try:
706 parse_result = urllib.parse.urlparse(manifest_server)
707 if parse_result.hostname:
708 auth = info.authenticators(parse_result.hostname)
709 if auth:
710 username, _account, password = auth
711 else:
712 print('No credentials found for %s in .netrc'
713 % parse_result.hostname, file=sys.stderr)
714 except netrc.NetrcParseError as e:
715 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
716
717 if (username and password):
718 manifest_server = manifest_server.replace('://', '://%s:%s@' %
719 (username, password),
720 1)
721
722 transport = PersistentTransport(manifest_server)
723 if manifest_server.startswith('persistent-'):
724 manifest_server = manifest_server[len('persistent-'):]
725
726 try:
727 server = xmlrpc.client.Server(manifest_server, transport=transport)
728 if opt.smart_sync:
729 p = self.manifest.manifestProject
730 b = p.GetBranch(p.CurrentBranch)
731 branch = b.merge
732 if branch.startswith(R_HEADS):
733 branch = branch[len(R_HEADS):]
734
Mike Frysinger56ce3462019-12-04 19:30:48 -0500735 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500736 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400737 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500738 elif ('TARGET_PRODUCT' in os.environ and
739 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500740 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
741 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400742 [success, manifest_str] = server.GetApprovedManifest(branch, target)
743 else:
744 [success, manifest_str] = server.GetApprovedManifest(branch)
745 else:
746 assert(opt.smart_tag)
747 [success, manifest_str] = server.GetManifest(opt.smart_tag)
748
749 if success:
750 manifest_name = os.path.basename(smart_sync_manifest_path)
751 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500752 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400753 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400754 except IOError as e:
755 print('error: cannot write manifest to %s:\n%s'
756 % (smart_sync_manifest_path, e),
757 file=sys.stderr)
758 sys.exit(1)
759 self._ReloadManifest(manifest_name)
760 else:
761 print('error: manifest server RPC call failed: %s' %
762 manifest_str, file=sys.stderr)
763 sys.exit(1)
764 except (socket.error, IOError, xmlrpc.client.Fault) as e:
765 print('error: cannot connect to manifest server %s:\n%s'
766 % (self.manifest.manifest_server, e), file=sys.stderr)
767 sys.exit(1)
768 except xmlrpc.client.ProtocolError as e:
769 print('error: cannot connect to manifest server %s:\n%d %s'
770 % (self.manifest.manifest_server, e.errcode, e.errmsg),
771 file=sys.stderr)
772 sys.exit(1)
773
774 return manifest_name
775
Mike Frysingerfb527e32019-08-27 02:34:32 -0400776 def _UpdateManifestProject(self, opt, mp, manifest_name):
777 """Fetch & update the local manifest project."""
778 if not opt.local_only:
779 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500780 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400781 current_branch_only=opt.current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500782 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400783 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600784 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400785 submodules=self.manifest.HasSubmodules,
786 clone_filter=self.manifest.CloneFilter)
787 finish = time.time()
788 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
789 start, finish, success)
790
791 if mp.HasChanges:
792 syncbuf = SyncBuffer(mp.config)
793 start = time.time()
794 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
795 clean = syncbuf.Finish()
796 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
797 start, time.time(), clean)
798 if not clean:
799 sys.exit(1)
800 self._ReloadManifest(opt.manifest_name)
801 if opt.jobs is None:
802 self.jobs = self.manifest.default.sync_j
803
Mike Frysingerae6cb082019-08-27 01:10:59 -0400804 def ValidateOptions(self, opt, args):
805 if opt.force_broken:
806 print('warning: -f/--force-broken is now the default behavior, and the '
807 'options are deprecated', file=sys.stderr)
808 if opt.network_only and opt.detach_head:
809 self.OptionParser.error('cannot combine -n and -d')
810 if opt.network_only and opt.local_only:
811 self.OptionParser.error('cannot combine -n and -l')
812 if opt.manifest_name and opt.smart_sync:
813 self.OptionParser.error('cannot combine -m and -s')
814 if opt.manifest_name and opt.smart_tag:
815 self.OptionParser.error('cannot combine -m and -t')
816 if opt.manifest_server_username or opt.manifest_server_password:
817 if not (opt.smart_sync or opt.smart_tag):
818 self.OptionParser.error('-u and -p may only be combined with -s or -t')
819 if None in [opt.manifest_server_username, opt.manifest_server_password]:
820 self.OptionParser.error('both -u and -p must be given')
821
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800823 if opt.jobs:
824 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700825 if self.jobs > 1:
826 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400827 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700828
Mike Frysinger521d01b2020-02-17 01:51:49 -0500829 opt.quiet = opt.output_mode is False
830 opt.verbose = opt.output_mode is True
831
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500832 if opt.manifest_name:
833 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700834
Chirayu Desaia892b102013-06-11 14:18:46 +0530835 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900836 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900837 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530838
Victor Boivie08c880d2011-04-19 10:32:52 +0200839 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400840 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
841 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900842 if os.path.isfile(smart_sync_manifest_path):
843 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800844 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900845 except OSError as e:
846 print('error: failed to remove existing smart sync override manifest: %s' %
847 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700848
Mike Frysinger5a033082019-09-23 19:21:20 -0400849 err_event = _threading.Event()
850
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700851 rp = self.manifest.repoProject
852 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500853 cb = rp.CurrentBranch
854 if cb:
855 base = rp.GetBranch(cb).merge
856 if not base or not base.startswith('refs/heads/'):
857 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400858 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500859 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700860
861 mp = self.manifest.manifestProject
862 mp.PreSync()
863
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800864 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700865 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800866
Fredrik de Grootcc960972019-11-22 09:04:31 +0100867 if not opt.mp_update:
868 print('Skipping update of local manifest project.')
869 else:
870 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700871
Simran Basib9a1b732015-08-20 12:19:28 -0700872 if self.gitc_manifest:
873 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700874 missing_ok=True)
875 gitc_projects = []
876 opened_projects = []
877 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700878 if project.relpath in self.gitc_manifest.paths and \
879 self.gitc_manifest.paths[project.relpath].old_revision:
880 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700881 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700882 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700883
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700884 if not args:
885 gitc_projects = None
886
887 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700888 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700889 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
890 if manifest_name:
891 manifest.Override(manifest_name)
892 else:
893 manifest.Override(self.manifest.manifestFile)
894 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
895 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700896 gitc_projects)
897 print('GITC client successfully synced.')
898
899 # The opened projects need to be synced as normal, therefore we
900 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700901 # TODO: make this more reliable -- if there's a project name/path overlap,
902 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900903 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
904 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700905 if not args:
906 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800907 all_projects = self.GetProjects(args,
908 missing_ok=True,
909 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700910
Mike Frysinger5a033082019-09-23 19:21:20 -0400911 err_network_sync = False
912 err_update_projects = False
913 err_checkout = False
914
Dave Borowitz67700e92012-10-23 15:00:54 -0700915 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700916 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700917 to_fetch = []
918 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700919 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700920 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900921 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700922 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700923
Mike Frysinger5a033082019-09-23 19:21:20 -0400924 fetched = self._Fetch(to_fetch, opt, err_event)
925
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500926 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700927 if opt.network_only:
928 # bail out now; the rest touches the working tree
Mike Frysinger5a033082019-09-23 19:21:20 -0400929 if err_event.isSet():
930 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
931 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700932 return
933
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800934 # Iteratively fetch missing and/or nested unregistered submodules
935 previously_missing_set = set()
936 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100937 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800938 all_projects = self.GetProjects(args,
939 missing_ok=True,
940 submodules_ok=opt.fetch_submodules)
941 missing = []
942 for project in all_projects:
943 if project.gitdir not in fetched:
944 missing.append(project)
945 if not missing:
946 break
947 # Stop us from non-stopped fetching actually-missing repos: If set of
948 # missing repos has not been changed from last fetch, we break.
949 missing_set = set(p.name for p in missing)
950 if previously_missing_set == missing_set:
951 break
952 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400953 fetched.update(self._Fetch(missing, opt, err_event))
954
955 # If we saw an error, exit with code 1 so that other scripts can check.
956 if err_event.isSet():
957 err_network_sync = True
958 if opt.fail_fast:
959 print('\nerror: Exited sync due to fetch errors.\n'
960 'Local checkouts *not* updated. Resolve network issues & '
961 'retry.\n'
962 '`repo sync -l` will update some local checkouts.',
963 file=sys.stderr)
964 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800965
Julien Campergue335f5ef2013-10-16 11:02:35 +0200966 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700967 # bail out now, we have no working tree
968 return
969
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500970 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400971 err_event.set()
972 err_update_projects = True
973 if opt.fail_fast:
974 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
975 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700976
Mike Frysinger5a033082019-09-23 19:21:20 -0400977 err_results = []
978 self._Checkout(all_projects, opt, err_event, err_results)
979 if err_event.isSet():
980 err_checkout = True
981 # NB: We don't exit here because this is the last step.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700983 # If there's a notice that's supposed to print at the end of the sync, print
984 # it now...
985 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700986 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700987
Mike Frysinger5a033082019-09-23 19:21:20 -0400988 # If we saw an error, exit with code 1 so that other scripts can check.
989 if err_event.isSet():
990 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
991 if err_network_sync:
992 print('error: Downloading network changes failed.', file=sys.stderr)
993 if err_update_projects:
994 print('error: Updating local project lists failed.', file=sys.stderr)
995 if err_checkout:
996 print('error: Checking out local projects failed.', file=sys.stderr)
997 if err_results:
998 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
999 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1000 file=sys.stderr)
1001 sys.exit(1)
1002
Mike Frysingere19d9e12020-02-12 11:23:32 -05001003 if not opt.quiet:
1004 print('repo sync has finished successfully.')
1005
David Pursehouse819827a2020-02-12 15:20:19 +09001006
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001007def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001008 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001009 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001010 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001011 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001012 if project.Exists:
1013 project.PostRepoUpgrade()
1014
David Pursehouse819827a2020-02-12 15:20:19 +09001015
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001016def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001017 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001018 print('info: A new version of repo is available', file=sys.stderr)
1019 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001020 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001021 syncbuf = SyncBuffer(rp.config)
1022 rp.Sync_LocalHalf(syncbuf)
1023 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001024 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001025 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001026 raise RepoChangedException(['--repo-upgraded'])
1027 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001028 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001029 else:
1030 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001031 print('repo version %s is current' % rp.work_git.describe(HEAD),
1032 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001033
David Pursehouse819827a2020-02-12 15:20:19 +09001034
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035def _VerifyTag(project):
1036 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1037 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001038 print('warning: GnuPG was not available during last "repo init"\n'
1039 'warning: Cannot automatically authenticate repo."""',
1040 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001041 return True
1042
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001044 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045 except GitError:
1046 cur = None
1047
1048 if not cur \
1049 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001050 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001051 if rev.startswith(R_HEADS):
1052 rev = rev[len(R_HEADS):]
1053
Sarah Owenscecd1d82012-11-01 22:59:27 -07001054 print(file=sys.stderr)
1055 print("warning: project '%s' branch '%s' is not signed"
1056 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057 return False
1058
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001059 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001060 env['GIT_DIR'] = project.gitdir
1061 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001062
1063 cmd = [GIT, 'tag', '-v', cur]
1064 proc = subprocess.Popen(cmd,
David Pursehousee5913ae2020-02-12 13:56:59 +09001065 stdout=subprocess.PIPE,
1066 stderr=subprocess.PIPE,
1067 env=env)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068 out = proc.stdout.read()
1069 proc.stdout.close()
1070
1071 err = proc.stderr.read()
1072 proc.stderr.close()
1073
1074 if proc.wait() != 0:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001075 print(file=sys.stderr)
1076 print(out, file=sys.stderr)
1077 print(err, file=sys.stderr)
1078 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079 return False
1080 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001081
David Rileye0684ad2017-04-05 00:02:59 -07001082
Dave Borowitz67700e92012-10-23 15:00:54 -07001083class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001084 _ALPHA = 0.5
1085
Dave Borowitz67700e92012-10-23 15:00:54 -07001086 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001087 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001088 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001089 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001090
1091 def Get(self, project):
1092 self._Load()
1093 return self._times.get(project.name, _ONE_DAY_S)
1094
1095 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001096 self._Load()
1097 name = project.name
1098 old = self._times.get(name, t)
1099 self._seen.add(name)
1100 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001101 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001102
1103 def _Load(self):
1104 if self._times is None:
1105 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001106 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001107 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001108 except (IOError, ValueError):
1109 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001110 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001111 except OSError:
1112 pass
1113 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001114
1115 def Save(self):
1116 if self._times is None:
1117 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001118
1119 to_delete = []
1120 for name in self._times:
1121 if name not in self._seen:
1122 to_delete.append(name)
1123 for name in to_delete:
1124 del self._times[name]
1125
Dave Borowitz67700e92012-10-23 15:00:54 -07001126 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001127 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001128 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001129 except (IOError, TypeError):
1130 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001131 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001132 except OSError:
1133 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001134
1135# This is a replacement for xmlrpc.client.Transport using urllib2
1136# and supporting persistent-http[s]. It cannot change hosts from
1137# request to request like the normal transport, the real url
1138# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001139
1140
Dan Willemsen0745bb22015-08-17 13:41:45 -07001141class PersistentTransport(xmlrpc.client.Transport):
1142 def __init__(self, orig_host):
1143 self.orig_host = orig_host
1144
1145 def request(self, host, handler, request_body, verbose=False):
1146 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1147 # Python doesn't understand cookies with the #HttpOnly_ prefix
1148 # Since we're only using them for HTTP, copy the file temporarily,
1149 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001150 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001151 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001152 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001153 try:
1154 with open(cookiefile) as f:
1155 for line in f:
1156 if line.startswith("#HttpOnly_"):
1157 line = line[len("#HttpOnly_"):]
1158 tmpcookiefile.write(line)
1159 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001160
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001161 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001162 try:
1163 cookiejar.load()
1164 except cookielib.LoadError:
1165 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001166 finally:
1167 tmpcookiefile.close()
1168 else:
1169 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001170
1171 proxyhandler = urllib.request.ProxyHandler
1172 if proxy:
1173 proxyhandler = urllib.request.ProxyHandler({
1174 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001175 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001176
1177 opener = urllib.request.build_opener(
1178 urllib.request.HTTPCookieProcessor(cookiejar),
1179 proxyhandler)
1180
1181 url = urllib.parse.urljoin(self.orig_host, handler)
1182 parse_results = urllib.parse.urlparse(url)
1183
1184 scheme = parse_results.scheme
1185 if scheme == 'persistent-http':
1186 scheme = 'http'
1187 if scheme == 'persistent-https':
1188 # If we're proxying through persistent-https, use http. The
1189 # proxy itself will do the https.
1190 if proxy:
1191 scheme = 'http'
1192 else:
1193 scheme = 'https'
1194
1195 # Parse out any authentication information using the base class
1196 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1197
1198 url = urllib.parse.urlunparse((
1199 scheme,
1200 host,
1201 parse_results.path,
1202 parse_results.params,
1203 parse_results.query,
1204 parse_results.fragment))
1205
1206 request = urllib.request.Request(url, request_body)
1207 if extra_headers is not None:
1208 for (name, header) in extra_headers:
1209 request.add_header(name, header)
1210 request.add_header('Content-Type', 'text/xml')
1211 try:
1212 response = opener.open(request)
1213 except urllib.error.HTTPError as e:
1214 if e.code == 501:
1215 # We may have been redirected through a login process
1216 # but our POST turned into a GET. Retry.
1217 response = opener.open(request)
1218 else:
1219 raise
1220
1221 p, u = xmlrpc.client.getparser()
1222 while 1:
1223 data = response.read(1024)
1224 if not data:
1225 break
1226 p.feed(data)
1227 p.close()
1228 return u.close()
1229
1230 def close(self):
1231 pass