blob: 0db96b54aabf89597d085a341d7adfba80d8389c [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Mike Frysingerebf04a42021-02-23 20:48:04 -050015import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040016import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050017import io
Anthony King85b24ac2014-05-06 15:57:48 +010018import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050019import multiprocessing
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
Mike Frysingeracf63b22019-06-13 02:24:21 -040029import urllib.error
30import urllib.parse
31import urllib.request
32import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070033
Roy Lee18afd7f2010-05-09 04:32:08 +080034try:
35 import threading as _threading
36except ImportError:
37 import dummy_threading as _threading
38
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070039try:
40 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090041
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070042 def _rlimit_nofile():
43 return resource.getrlimit(resource.RLIMIT_NOFILE)
44except ImportError:
45 def _rlimit_nofile():
46 return (256, 256)
47
Dave Borowitz18857212012-10-23 17:02:59 -070048try:
49 import multiprocessing
50except ImportError:
51 multiprocessing = None
52
David Rileye0684ad2017-04-05 00:02:59 -070053import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070054from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090055from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090056from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080057import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070058import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070059from project import Project
60from project import RemoteSpec
Mike Frysingerebf04a42021-02-23 20:48:04 -050061from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080062from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070063import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070064from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070065from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080066from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070067from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068
Dave Borowitz67700e92012-10-23 15:00:54 -070069_ONE_DAY_S = 24 * 60 * 60
70
David Pursehouse819827a2020-02-12 15:20:19 +090071
Doug Andersonfc06ced2011-03-16 15:49:18 -070072class _FetchError(Exception):
73 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
Doug Andersonfc06ced2011-03-16 15:49:18 -070074
David Pursehouse819827a2020-02-12 15:20:19 +090075
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080076class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080077 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070078 common = True
79 helpSummary = "Update working tree to the latest revision"
80 helpUsage = """
81%prog [<project>...]
82"""
83 helpDescription = """
84The '%prog' command synchronizes local project directories
85with the remote repositories specified in the manifest. If a local
86project does not yet exist, it will clone a new local directory from
87the remote repository and set up tracking branches as specified in
88the manifest. If the local project already exists, '%prog'
89will update the remote branches and rebase any new local changes
90on top of the new remote changes.
91
92'%prog' will synchronize all projects listed at the command
93line. Projects can be specified either by name, or by a relative
94or absolute path to the project's local directory. If no projects
95are specified, '%prog' will synchronize all projects listed in
96the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070097
98The -d/--detach option can be used to switch specified projects
99back to the manifest revision. This option is especially helpful
100if the project is currently on a topic branch, but the manifest
101revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700102
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700103The -s/--smart-sync option can be used to sync to a known good
104build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200105manifest. The -t/--smart-tag option is similar and allows you to
106specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700107
David Pursehousecf76b1b2012-09-14 10:31:42 +0900108The -u/--manifest-server-username and -p/--manifest-server-password
109options can be used to specify a username and password to authenticate
110with the manifest server when using the -s or -t option.
111
112If -u and -p are not specified when using the -s or -t option, '%prog'
113will attempt to read authentication credentials for the manifest server
114from the user's .netrc file.
115
116'%prog' will not use authentication credentials from -u/-p or .netrc
117if the manifest server specified in the manifest file already includes
118credentials.
119
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400120By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400121to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500122
Kevin Degiabaa7f32014-11-12 11:27:45 -0700123The --force-sync option can be used to overwrite existing git
124directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900125object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700126refs may be removed when overwriting.
127
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500128The --force-remove-dirty option can be used to remove previously used
129projects with uncommitted changes. WARNING: This may cause data to be
130lost since uncommitted changes may be removed with projects that no longer
131exist in the manifest.
132
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700133The --no-clone-bundle option disables any attempt to use
134$URL/clone.bundle to bootstrap a new Git repository from a
135resumeable bundle file on a content delivery network. This
136may be necessary if there are problems with the local Python
137HTTP client or proxy configuration, but the Git binary works.
138
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800139The --fetch-submodules option enables fetching Git submodules
140of a project from server.
141
David Pursehousef2fad612015-01-29 14:36:28 +0900142The -c/--current-branch option can be used to only fetch objects that
143are on the branch specified by a project's revision.
144
David Pursehouseb1553542014-09-04 21:28:09 +0900145The --optimized-fetch option can be used to only fetch projects that
146are fixed to a sha1 revision if the sha1 revision does not already
147exist locally.
148
David Pursehouse74cfd272015-10-14 10:50:15 +0900149The --prune option can be used to remove any refs that no longer
150exist on the remote.
151
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400152# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700153
154If at least one project remote URL uses an SSH connection (ssh://,
155git+ssh://, or user@host:path syntax) repo will automatically
156enable the SSH ControlMaster option when connecting to that host.
157This feature permits other projects in the same '%prog' session to
158reuse the same SSH tunnel, saving connection setup overheads.
159
160To disable this behavior on UNIX platforms, set the GIT_SSH
161environment variable to 'ssh'. For example:
162
163 export GIT_SSH=ssh
164 %prog
165
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400166# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700167
168This feature is automatically disabled on Windows, due to the lack
169of UNIX domain socket support.
170
171This feature is not compatible with url.insteadof rewrites in the
172user's ~/.gitconfig. '%prog' is currently not able to perform the
173rewrite early enough to establish the ControlMaster tunnel.
174
175If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
176later is required to fix a server side protocol bug.
177
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700178"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500179 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700180
Nico Sallembien6623b212010-05-11 12:57:01 -0700181 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000182 try:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500183 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000184 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500185 pass
186 super()._Options(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500188 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200189 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400190 help='obsolete option (to be deleted in the future)')
191 p.add_option('--fail-fast',
192 dest='fail_fast', action='store_true',
193 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700194 p.add_option('--force-sync',
195 dest='force_sync', action='store_true',
196 help="overwrite an existing git directory if it needs to "
197 "point to a different object directory. WARNING: this "
198 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500199 p.add_option('--force-remove-dirty',
200 dest='force_remove_dirty', action='store_true',
201 help="force remove projects with uncommitted modifications if "
202 "projects no longer exist in the manifest. "
203 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700205 dest='local_only', action='store_true',
206 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900207 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100208 dest='mp_update', action='store_false', default='true',
209 help='use the existing manifest checkout as-is. '
210 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700212 dest='network_only', action='store_true',
213 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700215 dest='detach_head', action='store_true',
216 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700218 dest='current_branch_only', action='store_true',
219 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500220 p.add_option('-v', '--verbose',
221 dest='output_mode', action='store_true',
222 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500224 dest='output_mode', action='store_false',
225 help='only show errors')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500226 p.add_option('-m', '--manifest-name',
227 dest='manifest_name',
228 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700229 p.add_option('--clone-bundle', action='store_true',
230 help='enable use of /clone.bundle on HTTP/HTTPS')
231 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700232 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800233 p.add_option('-u', '--manifest-server-username', action='store',
234 dest='manifest_server_username',
235 help='username to authenticate with the manifest server')
236 p.add_option('-p', '--manifest-server-password', action='store',
237 dest='manifest_server_password',
238 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800239 p.add_option('--fetch-submodules',
240 dest='fetch_submodules', action='store_true',
241 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800242 p.add_option('--use-superproject', action='store_true',
243 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500245 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900247 p.add_option('--optimized-fetch',
248 dest='optimized_fetch', action='store_true',
249 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600250 p.add_option('--retry-fetches',
251 default=0, action='store', type='int',
252 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900253 p.add_option('--prune', dest='prune', action='store_true',
254 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700255 if show_smart:
256 p.add_option('-s', '--smart-sync',
257 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900258 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200259 p.add_option('-t', '--smart-tag',
260 dest='smart_tag', action='store',
261 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700262
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g = p.add_option_group('repo Version options')
264 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500265 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800268 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700269 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800271 def _GetBranch(self):
272 """Returns the branch name for getting the approved manifest."""
273 p = self.manifest.manifestProject
274 b = p.GetBranch(p.CurrentBranch)
275 branch = b.merge
276 if branch.startswith(R_HEADS):
277 branch = branch[len(R_HEADS):]
278 return branch
279
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800280 def _UpdateProjectsRevisionId(self, opt, args):
281 """Update revisionId of every project with the SHA from superproject.
282
283 This function updates each project's revisionId with SHA from superproject.
284 It writes the updated manifest into a file and reloads the manifest from it.
285
286 Args:
287 opt: Program options returned from optparse. See _Options().
288 args: Arguments to pass to GetProjects. See the GetProjects
289 docstring for details.
290
291 Returns:
292 Returns path to the overriding manifest file.
293 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800294 superproject = git_superproject.Superproject(self.manifest,
295 self.repodir)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800296 all_projects = self.GetProjects(args,
297 missing_ok=True,
298 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800299 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800300 if not manifest_path:
301 print('error: Update of revsionId from superproject has failed',
302 file=sys.stderr)
303 sys.exit(1)
304 self._ReloadManifest(manifest_path)
305 return manifest_path
306
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500307 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700308 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800309
David James8d201162013-10-11 17:03:19 -0700310 Delegates most of the work to _FetchHelper.
311
312 Args:
313 opt: Program options returned from optparse. See _Options().
314 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500315 sem: We'll release() this semaphore when we exit so that another thread
316 can be started up.
David James89ece422014-01-09 18:51:58 -0800317 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700318 _FetchHelper docstring for details.
319 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500320 try:
321 for project in projects:
322 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400323 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500324 break
325 finally:
326 sem.release()
David James8d201162013-10-11 17:03:19 -0700327
Xin Li745be2e2019-06-03 11:24:30 -0700328 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
329 clone_filter):
David James8d201162013-10-11 17:03:19 -0700330 """Fetch git objects for a single project.
331
David Pursehousec1b86a22012-11-14 11:36:51 +0900332 Args:
333 opt: Program options returned from optparse. See _Options().
334 project: Project object for the project to fetch.
335 lock: Lock for accessing objects that are shared amongst multiple
336 _FetchHelper() threads.
337 fetched: set object that we will add project.gitdir to when we're done
338 (with our lock held).
339 pm: Instance of a Project object. We will call pm.update() (with our
340 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900341 err_event: We'll set this event in the case of an error (after printing
342 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700343 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700344
345 Returns:
346 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900347 """
348 # We'll set to true once we've locked the lock.
349 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700350
David Pursehousec1b86a22012-11-14 11:36:51 +0900351 # Encapsulate everything in a try/except/finally so that:
352 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700354 start = time.time()
355 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500356 buf = io.StringIO()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500357 with lock:
358 pm.start(project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700360 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900361 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900362 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500363 verbose=opt.verbose,
Mike Frysinger7b586f22021-02-23 18:38:39 -0500364 output_redir=buf,
David Pursehouseabdf7502020-02-12 14:58:39 +0900365 current_branch_only=opt.current_branch_only,
366 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500367 clone_bundle=opt.clone_bundle,
368 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900369 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600370 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900371 prune=opt.prune,
372 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900373 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700374
David Pursehousec1b86a22012-11-14 11:36:51 +0900375 # Lock around all the rest of the code, since printing, updating a set
376 # and Progress.update() are not thread safe.
377 lock.acquire()
378 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700379
Mike Frysinger7b586f22021-02-23 18:38:39 -0500380 output = buf.getvalue()
381 if opt.verbose and output:
382 pm.update(inc=0, msg=output.rstrip())
383
David Pursehousec1b86a22012-11-14 11:36:51 +0900384 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800385 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700386 print('error: Cannot fetch %s from %s'
387 % (project.name, project.remote.url),
388 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400389 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900390 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700391
David Pursehousec1b86a22012-11-14 11:36:51 +0900392 fetched.add(project.gitdir)
David Pursehousec1b86a22012-11-14 11:36:51 +0900393 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800394 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400395 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900396 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900397 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900398 err_event.set()
399 raise
400 finally:
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500401 if not did_lock:
402 lock.acquire()
403 pm.finish(project.name)
404 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700405 finish = time.time()
406 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
407 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800408
David James8d201162013-10-11 17:03:19 -0700409 return success
410
Mike Frysinger5a033082019-09-23 19:21:20 -0400411 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700412 fetched = set()
David James89ece422014-01-09 18:51:58 -0800413 lock = _threading.Lock()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500414 pm = Progress('Fetching', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800415
David James89ece422014-01-09 18:51:58 -0800416 objdir_project_map = dict()
417 for project in projects:
418 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700419
David James89ece422014-01-09 18:51:58 -0800420 threads = set()
421 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800422 for project_list in objdir_project_map.values():
423 # Check for any errors before running any more tasks.
424 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500425 if err_event.is_set() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800426 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700427
David James89ece422014-01-09 18:51:58 -0800428 sem.acquire()
429 kwargs = dict(opt=opt,
430 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500431 sem=sem,
David James89ece422014-01-09 18:51:58 -0800432 lock=lock,
433 fetched=fetched,
434 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700435 err_event=err_event,
436 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800437 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900438 t = _threading.Thread(target=self._FetchProjectList,
439 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200440 # Ensure that Ctrl-C will not freeze the repo process.
441 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800442 threads.add(t)
443 t.start()
David James89ece422014-01-09 18:51:58 -0800444 else:
445 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800446
David James89ece422014-01-09 18:51:58 -0800447 for t in threads:
448 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800449
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700450 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700451 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700452
Julien Campergue335f5ef2013-10-16 11:02:35 +0200453 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400454 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200455
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700456 return fetched
457
Mike Frysingerebf04a42021-02-23 20:48:04 -0500458 def _CheckoutOne(self, opt, project):
Xin Li745be2e2019-06-03 11:24:30 -0700459 """Checkout work tree for one project
460
461 Args:
462 opt: Program options returned from optparse. See _Options().
463 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700464
465 Returns:
466 Whether the fetch was successful.
467 """
Xin Li745be2e2019-06-03 11:24:30 -0700468 start = time.time()
469 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
470 detach_head=opt.detach_head)
471 success = False
472 try:
Mike Frysingerebf04a42021-02-23 20:48:04 -0500473 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
474 success = syncbuf.Finish()
475 except Exception as e:
476 print('error: Cannot checkout %s: %s: %s' %
477 (project.name, type(e).__name__, str(e)),
478 file=sys.stderr)
479 raise
Xin Li745be2e2019-06-03 11:24:30 -0700480
Mike Frysingerebf04a42021-02-23 20:48:04 -0500481 if not success:
482 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
483 finish = time.time()
484 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700485
Mike Frysingerebf04a42021-02-23 20:48:04 -0500486 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700487 """Checkout projects listed in all_projects
488
489 Args:
490 all_projects: List of all projects that should be checked out.
491 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500492 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700493 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500494 ret = True
Xin Li745be2e2019-06-03 11:24:30 -0700495
Mike Frysingerebf04a42021-02-23 20:48:04 -0500496 # Only checkout projects with worktrees.
497 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700498
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500499 pm = Progress('Checking out', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700500
Mike Frysingerebf04a42021-02-23 20:48:04 -0500501 def _ProcessResults(results):
502 for (success, project, start, finish) in results:
503 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
504 start, finish, success)
505 # Check for any errors before running any more tasks.
506 # ...we'll let existing threads finish, though.
507 if not success:
508 err_results.append(project.relpath)
509 if opt.fail_fast:
510 return False
511 pm.update(msg=project.name)
512 return True
Xin Li745be2e2019-06-03 11:24:30 -0700513
Mike Frysingerebf04a42021-02-23 20:48:04 -0500514 # NB: Multiprocessing is heavy, so don't spin it up for one job.
515 if len(all_projects) == 1 or opt.jobs == 1:
516 if not _ProcessResults(self._CheckoutOne(opt, x) for x in all_projects):
517 ret = False
518 else:
519 with multiprocessing.Pool(opt.jobs) as pool:
520 results = pool.imap_unordered(
521 functools.partial(self._CheckoutOne, opt),
522 all_projects,
523 chunksize=WORKER_BATCH_SIZE)
524 if not _ProcessResults(results):
525 ret = False
526 pool.close()
Xin Li745be2e2019-06-03 11:24:30 -0700527
528 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700529
Mike Frysingerebf04a42021-02-23 20:48:04 -0500530 return ret
531
Mike Frysinger5a033082019-09-23 19:21:20 -0400532 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700533 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700534 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500535 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500536 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900537 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100538 if not opt.quiet:
539 print('%s: Shared project %s found, disabling pruning.' %
540 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500541 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500542 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500543 else:
544 # This isn't perfect, but it's the best we can do with old git.
545 print('%s: WARNING: shared projects are unreliable when using old '
546 'versions of git; please upgrade to git-2.7.0+.'
547 % (project.relpath,),
548 file=sys.stderr)
549 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700550 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700551
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500552 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700553 cpu_count = multiprocessing.cpu_count()
554 else:
555 cpu_count = 1
556 jobs = min(self.jobs, cpu_count)
557
558 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700559 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700560 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700561 return
562
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400563 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700564
565 threads = set()
566 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700567
David James8d201162013-10-11 17:03:19 -0700568 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700569 try:
570 try:
David James8d201162013-10-11 17:03:19 -0700571 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700572 except GitError:
573 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900574 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700575 err_event.set()
576 raise
577 finally:
578 sem.release()
579
Gabe Black2ff30292014-10-09 17:54:35 -0700580 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500581 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700582 break
583 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700584 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700585 t.daemon = True
586 threads.add(t)
587 t.start()
588
589 for t in threads:
590 t.join()
591
Tim Kilbourn07669002013-03-08 15:02:49 -0800592 def _ReloadManifest(self, manifest_name=None):
593 if manifest_name:
594 # Override calls _Unload already
595 self.manifest.Override(manifest_name)
596 else:
597 self.manifest._Unload()
598
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500599 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700600 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700601 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700602 if project.relpath:
603 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700604 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500605 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700606 old_project_paths = []
607
608 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500609 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700610 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800611 # In reversed order, so subfolders are deleted before parent folder.
612 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700613 if not path:
614 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700615 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900616 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700617 gitdir = os.path.join(self.manifest.topdir, path, '.git')
618 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900619 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900620 manifest=self.manifest,
621 name=path,
622 remote=RemoteSpec('origin'),
623 gitdir=gitdir,
624 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500625 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900626 worktree=os.path.join(self.manifest.topdir, path),
627 relpath=path,
628 revisionExpr='HEAD',
629 revisionId=None,
630 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500631 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900632 quiet=opt.quiet,
633 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400634 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700635
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700636 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500637 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700638 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700639 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700640 return 0
641
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400642 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
643 if not self.manifest.manifest_server:
644 print('error: cannot smart sync: no manifest server defined in '
645 'manifest', file=sys.stderr)
646 sys.exit(1)
647
648 manifest_server = self.manifest.manifest_server
649 if not opt.quiet:
650 print('Using manifest server %s' % manifest_server)
651
David Pursehouseeeff3532020-02-12 11:24:10 +0900652 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400653 username = None
654 password = None
655 if opt.manifest_server_username and opt.manifest_server_password:
656 username = opt.manifest_server_username
657 password = opt.manifest_server_password
658 else:
659 try:
660 info = netrc.netrc()
661 except IOError:
662 # .netrc file does not exist or could not be opened
663 pass
664 else:
665 try:
666 parse_result = urllib.parse.urlparse(manifest_server)
667 if parse_result.hostname:
668 auth = info.authenticators(parse_result.hostname)
669 if auth:
670 username, _account, password = auth
671 else:
672 print('No credentials found for %s in .netrc'
673 % parse_result.hostname, file=sys.stderr)
674 except netrc.NetrcParseError as e:
675 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
676
677 if (username and password):
678 manifest_server = manifest_server.replace('://', '://%s:%s@' %
679 (username, password),
680 1)
681
682 transport = PersistentTransport(manifest_server)
683 if manifest_server.startswith('persistent-'):
684 manifest_server = manifest_server[len('persistent-'):]
685
686 try:
687 server = xmlrpc.client.Server(manifest_server, transport=transport)
688 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800689 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400690
Mike Frysinger56ce3462019-12-04 19:30:48 -0500691 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500692 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400693 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500694 elif ('TARGET_PRODUCT' in os.environ and
695 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500696 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
697 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400698 [success, manifest_str] = server.GetApprovedManifest(branch, target)
699 else:
700 [success, manifest_str] = server.GetApprovedManifest(branch)
701 else:
702 assert(opt.smart_tag)
703 [success, manifest_str] = server.GetManifest(opt.smart_tag)
704
705 if success:
706 manifest_name = os.path.basename(smart_sync_manifest_path)
707 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500708 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400709 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400710 except IOError as e:
711 print('error: cannot write manifest to %s:\n%s'
712 % (smart_sync_manifest_path, e),
713 file=sys.stderr)
714 sys.exit(1)
715 self._ReloadManifest(manifest_name)
716 else:
717 print('error: manifest server RPC call failed: %s' %
718 manifest_str, file=sys.stderr)
719 sys.exit(1)
720 except (socket.error, IOError, xmlrpc.client.Fault) as e:
721 print('error: cannot connect to manifest server %s:\n%s'
722 % (self.manifest.manifest_server, e), file=sys.stderr)
723 sys.exit(1)
724 except xmlrpc.client.ProtocolError as e:
725 print('error: cannot connect to manifest server %s:\n%d %s'
726 % (self.manifest.manifest_server, e.errcode, e.errmsg),
727 file=sys.stderr)
728 sys.exit(1)
729
730 return manifest_name
731
Mike Frysingerfb527e32019-08-27 02:34:32 -0400732 def _UpdateManifestProject(self, opt, mp, manifest_name):
733 """Fetch & update the local manifest project."""
734 if not opt.local_only:
735 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500736 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400737 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200738 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500739 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400740 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600741 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400742 submodules=self.manifest.HasSubmodules,
743 clone_filter=self.manifest.CloneFilter)
744 finish = time.time()
745 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
746 start, finish, success)
747
748 if mp.HasChanges:
749 syncbuf = SyncBuffer(mp.config)
750 start = time.time()
751 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
752 clean = syncbuf.Finish()
753 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
754 start, time.time(), clean)
755 if not clean:
756 sys.exit(1)
757 self._ReloadManifest(opt.manifest_name)
758 if opt.jobs is None:
759 self.jobs = self.manifest.default.sync_j
760
Mike Frysingerae6cb082019-08-27 01:10:59 -0400761 def ValidateOptions(self, opt, args):
762 if opt.force_broken:
763 print('warning: -f/--force-broken is now the default behavior, and the '
764 'options are deprecated', file=sys.stderr)
765 if opt.network_only and opt.detach_head:
766 self.OptionParser.error('cannot combine -n and -d')
767 if opt.network_only and opt.local_only:
768 self.OptionParser.error('cannot combine -n and -l')
769 if opt.manifest_name and opt.smart_sync:
770 self.OptionParser.error('cannot combine -m and -s')
771 if opt.manifest_name and opt.smart_tag:
772 self.OptionParser.error('cannot combine -m and -t')
773 if opt.manifest_server_username or opt.manifest_server_password:
774 if not (opt.smart_sync or opt.smart_tag):
775 self.OptionParser.error('-u and -p may only be combined with -s or -t')
776 if None in [opt.manifest_server_username, opt.manifest_server_password]:
777 self.OptionParser.error('both -u and -p must be given')
778
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700779 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800780 if opt.jobs:
781 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700782 if self.jobs > 1:
783 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400784 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700785
Mike Frysinger521d01b2020-02-17 01:51:49 -0500786 opt.quiet = opt.output_mode is False
787 opt.verbose = opt.output_mode is True
788
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500789 if opt.manifest_name:
790 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700791
Chirayu Desaia892b102013-06-11 14:18:46 +0530792 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900793 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900794 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530795
Xin Lid79a4bc2020-05-20 16:03:45 -0700796 if opt.clone_bundle is None:
797 opt.clone_bundle = self.manifest.CloneBundle
798
Victor Boivie08c880d2011-04-19 10:32:52 +0200799 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400800 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
801 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900802 if os.path.isfile(smart_sync_manifest_path):
803 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800804 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900805 except OSError as e:
806 print('error: failed to remove existing smart sync override manifest: %s' %
807 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700808
Mike Frysinger5a033082019-09-23 19:21:20 -0400809 err_event = _threading.Event()
810
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811 rp = self.manifest.repoProject
812 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500813 cb = rp.CurrentBranch
814 if cb:
815 base = rp.GetBranch(cb).merge
816 if not base or not base.startswith('refs/heads/'):
817 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400818 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500819 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820
821 mp = self.manifest.manifestProject
822 mp.PreSync()
823
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800824 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700825 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800826
Fredrik de Grootcc960972019-11-22 09:04:31 +0100827 if not opt.mp_update:
828 print('Skipping update of local manifest project.')
829 else:
830 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700831
Raman Tenneti55d6a5a2021-02-24 14:37:01 -0800832 if (opt.use_superproject or
833 self.manifest.manifestProject.config.GetBoolean(
834 'repo.superproject')):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800835 manifest_name = self._UpdateProjectsRevisionId(opt, args)
836
Simran Basib9a1b732015-08-20 12:19:28 -0700837 if self.gitc_manifest:
838 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700839 missing_ok=True)
840 gitc_projects = []
841 opened_projects = []
842 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700843 if project.relpath in self.gitc_manifest.paths and \
844 self.gitc_manifest.paths[project.relpath].old_revision:
845 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700846 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700847 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700848
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700849 if not args:
850 gitc_projects = None
851
852 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700853 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700854 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
855 if manifest_name:
856 manifest.Override(manifest_name)
857 else:
858 manifest.Override(self.manifest.manifestFile)
859 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
860 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700861 gitc_projects)
862 print('GITC client successfully synced.')
863
864 # The opened projects need to be synced as normal, therefore we
865 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700866 # TODO: make this more reliable -- if there's a project name/path overlap,
867 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900868 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
869 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700870 if not args:
871 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800872 all_projects = self.GetProjects(args,
873 missing_ok=True,
874 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700875
Mike Frysinger5a033082019-09-23 19:21:20 -0400876 err_network_sync = False
877 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400878
Dave Borowitz67700e92012-10-23 15:00:54 -0700879 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700880 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700881 to_fetch = []
882 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700883 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700884 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900885 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700886 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700887
Mike Frysinger5a033082019-09-23 19:21:20 -0400888 fetched = self._Fetch(to_fetch, opt, err_event)
889
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500890 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700891 if opt.network_only:
892 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500893 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400894 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
895 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700896 return
897
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800898 # Iteratively fetch missing and/or nested unregistered submodules
899 previously_missing_set = set()
900 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100901 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800902 all_projects = self.GetProjects(args,
903 missing_ok=True,
904 submodules_ok=opt.fetch_submodules)
905 missing = []
906 for project in all_projects:
907 if project.gitdir not in fetched:
908 missing.append(project)
909 if not missing:
910 break
911 # Stop us from non-stopped fetching actually-missing repos: If set of
912 # missing repos has not been changed from last fetch, we break.
913 missing_set = set(p.name for p in missing)
914 if previously_missing_set == missing_set:
915 break
916 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400917 fetched.update(self._Fetch(missing, opt, err_event))
918
919 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500920 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400921 err_network_sync = True
922 if opt.fail_fast:
923 print('\nerror: Exited sync due to fetch errors.\n'
924 'Local checkouts *not* updated. Resolve network issues & '
925 'retry.\n'
926 '`repo sync -l` will update some local checkouts.',
927 file=sys.stderr)
928 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800929
Julien Campergue335f5ef2013-10-16 11:02:35 +0200930 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700931 # bail out now, we have no working tree
932 return
933
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500934 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400935 err_event.set()
936 err_update_projects = True
937 if opt.fail_fast:
938 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
939 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700940
Mike Frysinger5a033082019-09-23 19:21:20 -0400941 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -0500942 # NB: We don't exit here because this is the last step.
943 err_checkout = not self._Checkout(all_projects, opt, err_results)
944 if err_checkout:
945 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700946
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700947 # If there's a notice that's supposed to print at the end of the sync, print
948 # it now...
949 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700950 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700951
Mike Frysinger5a033082019-09-23 19:21:20 -0400952 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500953 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400954 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
955 if err_network_sync:
956 print('error: Downloading network changes failed.', file=sys.stderr)
957 if err_update_projects:
958 print('error: Updating local project lists failed.', file=sys.stderr)
959 if err_checkout:
960 print('error: Checking out local projects failed.', file=sys.stderr)
961 if err_results:
962 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
963 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
964 file=sys.stderr)
965 sys.exit(1)
966
Mike Frysingere19d9e12020-02-12 11:23:32 -0500967 if not opt.quiet:
968 print('repo sync has finished successfully.')
969
David Pursehouse819827a2020-02-12 15:20:19 +0900970
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700971def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800972 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700973 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700974 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800975 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700976 if project.Exists:
977 project.PostRepoUpgrade()
978
David Pursehouse819827a2020-02-12 15:20:19 +0900979
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500980def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700981 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700982 print('info: A new version of repo is available', file=sys.stderr)
983 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500984 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700985 syncbuf = SyncBuffer(rp.config)
986 rp.Sync_LocalHalf(syncbuf)
987 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700988 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700989 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700990 raise RepoChangedException(['--repo-upgraded'])
991 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700992 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700993 else:
994 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700995 print('repo version %s is current' % rp.work_git.describe(HEAD),
996 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700997
David Pursehouse819827a2020-02-12 15:20:19 +0900998
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700999def _VerifyTag(project):
1000 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1001 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001002 print('warning: GnuPG was not available during last "repo init"\n'
1003 'warning: Cannot automatically authenticate repo."""',
1004 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001005 return True
1006
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001007 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001008 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001009 except GitError:
1010 cur = None
1011
1012 if not cur \
1013 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001014 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001015 if rev.startswith(R_HEADS):
1016 rev = rev[len(R_HEADS):]
1017
Sarah Owenscecd1d82012-11-01 22:59:27 -07001018 print(file=sys.stderr)
1019 print("warning: project '%s' branch '%s' is not signed"
1020 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021 return False
1022
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001023 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001024 env['GIT_DIR'] = project.gitdir
1025 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001026
1027 cmd = [GIT, 'tag', '-v', cur]
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001028 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
1029 env=env, check=False)
1030 if result.returncode:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001031 print(file=sys.stderr)
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001032 print(result.stdout, file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001033 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001034 return False
1035 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001036
David Rileye0684ad2017-04-05 00:02:59 -07001037
Dave Borowitz67700e92012-10-23 15:00:54 -07001038class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001039 _ALPHA = 0.5
1040
Dave Borowitz67700e92012-10-23 15:00:54 -07001041 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001042 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001043 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001044 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001045
1046 def Get(self, project):
1047 self._Load()
1048 return self._times.get(project.name, _ONE_DAY_S)
1049
1050 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001051 self._Load()
1052 name = project.name
1053 old = self._times.get(name, t)
1054 self._seen.add(name)
1055 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001056 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001057
1058 def _Load(self):
1059 if self._times is None:
1060 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001061 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001062 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001063 except (IOError, ValueError):
1064 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001065 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001066 except OSError:
1067 pass
1068 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001069
1070 def Save(self):
1071 if self._times is None:
1072 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001073
1074 to_delete = []
1075 for name in self._times:
1076 if name not in self._seen:
1077 to_delete.append(name)
1078 for name in to_delete:
1079 del self._times[name]
1080
Dave Borowitz67700e92012-10-23 15:00:54 -07001081 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001082 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001083 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001084 except (IOError, TypeError):
1085 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001086 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001087 except OSError:
1088 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001089
1090# This is a replacement for xmlrpc.client.Transport using urllib2
1091# and supporting persistent-http[s]. It cannot change hosts from
1092# request to request like the normal transport, the real url
1093# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001094
1095
Dan Willemsen0745bb22015-08-17 13:41:45 -07001096class PersistentTransport(xmlrpc.client.Transport):
1097 def __init__(self, orig_host):
1098 self.orig_host = orig_host
1099
1100 def request(self, host, handler, request_body, verbose=False):
1101 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1102 # Python doesn't understand cookies with the #HttpOnly_ prefix
1103 # Since we're only using them for HTTP, copy the file temporarily,
1104 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001105 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001106 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001107 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001108 try:
1109 with open(cookiefile) as f:
1110 for line in f:
1111 if line.startswith("#HttpOnly_"):
1112 line = line[len("#HttpOnly_"):]
1113 tmpcookiefile.write(line)
1114 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001115
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001116 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001117 try:
1118 cookiejar.load()
1119 except cookielib.LoadError:
1120 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001121 finally:
1122 tmpcookiefile.close()
1123 else:
1124 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001125
1126 proxyhandler = urllib.request.ProxyHandler
1127 if proxy:
1128 proxyhandler = urllib.request.ProxyHandler({
1129 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001130 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001131
1132 opener = urllib.request.build_opener(
1133 urllib.request.HTTPCookieProcessor(cookiejar),
1134 proxyhandler)
1135
1136 url = urllib.parse.urljoin(self.orig_host, handler)
1137 parse_results = urllib.parse.urlparse(url)
1138
1139 scheme = parse_results.scheme
1140 if scheme == 'persistent-http':
1141 scheme = 'http'
1142 if scheme == 'persistent-https':
1143 # If we're proxying through persistent-https, use http. The
1144 # proxy itself will do the https.
1145 if proxy:
1146 scheme = 'http'
1147 else:
1148 scheme = 'https'
1149
1150 # Parse out any authentication information using the base class
1151 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1152
1153 url = urllib.parse.urlunparse((
1154 scheme,
1155 host,
1156 parse_results.path,
1157 parse_results.params,
1158 parse_results.query,
1159 parse_results.fragment))
1160
1161 request = urllib.request.Request(url, request_body)
1162 if extra_headers is not None:
1163 for (name, header) in extra_headers:
1164 request.add_header(name, header)
1165 request.add_header('Content-Type', 'text/xml')
1166 try:
1167 response = opener.open(request)
1168 except urllib.error.HTTPError as e:
1169 if e.code == 501:
1170 # We may have been redirected through a login process
1171 # but our POST turned into a GET. Retry.
1172 response = opener.open(request)
1173 else:
1174 raise
1175
1176 p, u = xmlrpc.client.getparser()
1177 while 1:
1178 data = response.read(1024)
1179 if not data:
1180 break
1181 p.feed(data)
1182 p.close()
1183 return u.close()
1184
1185 def close(self):
1186 pass