blob: 83c9ad36256ece74634995a244eaad2b2b88c096 [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
LaMont Jonesbdcba7d2022-04-11 22:50:11 +000015import collections
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
LaMont Jones891e8f72022-09-08 20:17:58 +000024import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070025import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070027import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070028import time
LaMont Jones78dcd372022-10-25 22:38:07 +000029from typing import NamedTuple, List, Set
Mike Frysingeracf63b22019-06-13 02:24:21 -040030import urllib.error
31import urllib.parse
32import urllib.request
Mike Frysinger5951e302022-05-20 23:34:44 -040033import xml.parsers.expat
Mike Frysingeracf63b22019-06-13 02:24:21 -040034import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070035
Roy Lee18afd7f2010-05-09 04:32:08 +080036try:
37 import threading as _threading
38except ImportError:
39 import dummy_threading as _threading
40
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070041try:
42 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090043
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070044 def _rlimit_nofile():
45 return resource.getrlimit(resource.RLIMIT_NOFILE)
46except ImportError:
47 def _rlimit_nofile():
48 return (256, 256)
49
David Rileye0684ad2017-04-05 00:02:59 -070050import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040051from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090052from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090053from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080054import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070055import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070056from project import Project
57from project import RemoteSpec
Mike Frysinger355f4392022-07-20 17:15:29 -040058from command import Command, DEFAULT_LOCAL_JOBS, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080059from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070060import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070061from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070062from progress import Progress
Joanna Wanga6c52f52022-11-03 16:51:19 -040063from repo_trace import Trace
Mike Frysinger19e409c2021-05-05 19:44:35 -040064import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080065from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070066from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070067
Dave Borowitz67700e92012-10-23 15:00:54 -070068_ONE_DAY_S = 24 * 60 * 60
LaMont Jones891e8f72022-09-08 20:17:58 +000069# Env var to implicitly turn off object backups.
70REPO_BACKUP_OBJECTS = 'REPO_BACKUP_OBJECTS'
71
72_BACKUP_OBJECTS = os.environ.get(REPO_BACKUP_OBJECTS) != '0'
Dave Borowitz67700e92012-10-23 15:00:54 -070073
David Pursehouse819827a2020-02-12 15:20:19 +090074
LaMont Jones1eddca82022-09-01 15:15:04 +000075class _FetchOneResult(NamedTuple):
76 """_FetchOne return value.
77
78 Attributes:
79 success (bool): True if successful.
80 project (Project): The fetched project.
81 start (float): The starting time.time().
82 finish (float): The ending time.time().
83 remote_fetched (bool): True if the remote was actually queried.
84 """
85 success: bool
86 project: Project
87 start: float
88 finish: float
89 remote_fetched: bool
90
91
92class _FetchResult(NamedTuple):
93 """_Fetch return value.
94
95 Attributes:
96 success (bool): True if successful.
LaMont Jones78dcd372022-10-25 22:38:07 +000097 projects (Set[str]): The names of the git directories of fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +000098 """
99 success: bool
LaMont Jones78dcd372022-10-25 22:38:07 +0000100 projects: Set[str]
LaMont Jones1eddca82022-09-01 15:15:04 +0000101
102
103class _FetchMainResult(NamedTuple):
104 """_FetchMain return value.
105
106 Attributes:
LaMont Jones78dcd372022-10-25 22:38:07 +0000107 all_projects (List[Project]): The fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000108 """
LaMont Jones78dcd372022-10-25 22:38:07 +0000109 all_projects: List[Project]
LaMont Jones1eddca82022-09-01 15:15:04 +0000110
111
112class _CheckoutOneResult(NamedTuple):
113 """_CheckoutOne return value.
114
115 Attributes:
116 success (bool): True if successful.
117 project (Project): The project.
118 start (float): The starting time.time().
119 finish (float): The ending time.time().
120 """
121 success: bool
122 project: Project
123 start: float
124 finish: float
125
126
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800127class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -0400128 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000129 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700130 helpSummary = "Update working tree to the latest revision"
131 helpUsage = """
132%prog [...]
133"""
134 helpDescription = """
135The '%prog' command synchronizes local project directories
136with the remote repositories specified in the manifest. If a local
137project does not yet exist, it will clone a new local directory from
138the remote repository and set up tracking branches as specified in
139the manifest. If the local project already exists, '%prog'
140will update the remote branches and rebase any new local changes
141on top of the new remote changes.
142
143'%prog' will synchronize all projects listed at the command
144line. Projects can be specified either by name, or by a relative
145or absolute path to the project's local directory. If no projects
146are specified, '%prog' will synchronize all projects listed in
147the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700148
149The -d/--detach option can be used to switch specified projects
150back to the manifest revision. This option is especially helpful
151if the project is currently on a topic branch, but the manifest
152revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700153
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700154The -s/--smart-sync option can be used to sync to a known good
155build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200156manifest. The -t/--smart-tag option is similar and allows you to
157specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700158
David Pursehousecf76b1b2012-09-14 10:31:42 +0900159The -u/--manifest-server-username and -p/--manifest-server-password
160options can be used to specify a username and password to authenticate
161with the manifest server when using the -s or -t option.
162
163If -u and -p are not specified when using the -s or -t option, '%prog'
164will attempt to read authentication credentials for the manifest server
165from the user's .netrc file.
166
167'%prog' will not use authentication credentials from -u/-p or .netrc
168if the manifest server specified in the manifest file already includes
169credentials.
170
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400171By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400172to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500173
Kevin Degiabaa7f32014-11-12 11:27:45 -0700174The --force-sync option can be used to overwrite existing git
175directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900176object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700177refs may be removed when overwriting.
178
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500179The --force-remove-dirty option can be used to remove previously used
180projects with uncommitted changes. WARNING: This may cause data to be
181lost since uncommitted changes may be removed with projects that no longer
182exist in the manifest.
183
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700184The --no-clone-bundle option disables any attempt to use
185$URL/clone.bundle to bootstrap a new Git repository from a
186resumeable bundle file on a content delivery network. This
187may be necessary if there are problems with the local Python
188HTTP client or proxy configuration, but the Git binary works.
189
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800190The --fetch-submodules option enables fetching Git submodules
191of a project from server.
192
David Pursehousef2fad612015-01-29 14:36:28 +0900193The -c/--current-branch option can be used to only fetch objects that
194are on the branch specified by a project's revision.
195
David Pursehouseb1553542014-09-04 21:28:09 +0900196The --optimized-fetch option can be used to only fetch projects that
197are fixed to a sha1 revision if the sha1 revision does not already
198exist locally.
199
David Pursehouse74cfd272015-10-14 10:50:15 +0900200The --prune option can be used to remove any refs that no longer
201exist on the remote.
202
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400203# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700204
205If at least one project remote URL uses an SSH connection (ssh://,
206git+ssh://, or user@host:path syntax) repo will automatically
207enable the SSH ControlMaster option when connecting to that host.
208This feature permits other projects in the same '%prog' session to
209reuse the same SSH tunnel, saving connection setup overheads.
210
211To disable this behavior on UNIX platforms, set the GIT_SSH
212environment variable to 'ssh'. For example:
213
214 export GIT_SSH=ssh
215 %prog
216
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400217# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700218
219This feature is automatically disabled on Windows, due to the lack
220of UNIX domain socket support.
221
222This feature is not compatible with url.insteadof rewrites in the
223user's ~/.gitconfig. '%prog' is currently not able to perform the
224rewrite early enough to establish the ControlMaster tunnel.
225
226If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
227later is required to fix a server side protocol bug.
228
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400230 # A value of 0 means we want parallel jobs, but we'll determine the default
231 # value later on.
232 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700233
Mike Frysinger9180a072021-04-13 14:57:40 -0400234 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400235 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400236 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400237 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400238 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
239 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400240
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500241 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200242 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400243 help='obsolete option (to be deleted in the future)')
244 p.add_option('--fail-fast',
245 dest='fail_fast', action='store_true',
246 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700247 p.add_option('--force-sync',
248 dest='force_sync', action='store_true',
249 help="overwrite an existing git directory if it needs to "
250 "point to a different object directory. WARNING: this "
251 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500252 p.add_option('--force-remove-dirty',
253 dest='force_remove_dirty', action='store_true',
254 help="force remove projects with uncommitted modifications if "
255 "projects no longer exist in the manifest. "
256 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900257 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700258 dest='local_only', action='store_true',
259 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900260 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100261 dest='mp_update', action='store_false', default='true',
262 help='use the existing manifest checkout as-is. '
263 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900264 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700265 dest='network_only', action='store_true',
266 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900267 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700268 dest='detach_head', action='store_true',
269 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900270 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700271 dest='current_branch_only', action='store_true',
272 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400273 p.add_option('--no-current-branch',
274 dest='current_branch_only', action='store_false',
275 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500276 p.add_option('-m', '--manifest-name',
277 dest='manifest_name',
278 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700279 p.add_option('--clone-bundle', action='store_true',
280 help='enable use of /clone.bundle on HTTP/HTTPS')
281 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700282 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800283 p.add_option('-u', '--manifest-server-username', action='store',
284 dest='manifest_server_username',
285 help='username to authenticate with the manifest server')
286 p.add_option('-p', '--manifest-server-password', action='store',
287 dest='manifest_server_password',
288 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800289 p.add_option('--fetch-submodules',
290 dest='fetch_submodules', action='store_true',
291 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800292 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700293 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700294 p.add_option('--no-use-superproject', action='store_false',
295 dest='use_superproject',
296 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400297 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400298 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700299 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400300 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400301 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900302 p.add_option('--optimized-fetch',
303 dest='optimized_fetch', action='store_true',
304 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600305 p.add_option('--retry-fetches',
306 default=0, action='store', type='int',
307 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400308 p.add_option('--prune', action='store_true',
309 help='delete refs that no longer exist on the remote (default)')
310 p.add_option('--no-prune', dest='prune', action='store_false',
311 help='do not delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700312 if show_smart:
313 p.add_option('-s', '--smart-sync',
314 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900315 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200316 p.add_option('-t', '--smart-tag',
317 dest='smart_tag', action='store',
318 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700319
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700320 g = p.add_option_group('repo Version options')
321 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500322 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700323 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700324 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800325 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700326 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700327
LaMont Jonesa46047a2022-04-07 21:57:06 +0000328 def _GetBranch(self, manifest_project):
329 """Returns the branch name for getting the approved smartsync manifest.
330
331 Args:
332 manifest_project: the manifestProject to query.
333 """
334 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800335 branch = b.merge
336 if branch.startswith(R_HEADS):
337 branch = branch[len(R_HEADS):]
338 return branch
339
LaMont Jonesa46047a2022-04-07 21:57:06 +0000340 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200341 """Returns whether current-branch or use-superproject options are enabled.
342
LaMont Jonesa46047a2022-04-07 21:57:06 +0000343 Args:
344 opt: Program options returned from optparse. See _Options().
345 manifest: The manifest to use.
346
Daniel Anderssond52ca422022-04-01 12:55:38 +0200347 Returns:
348 True if a superproject is requested, otherwise the value of the
349 current_branch option (True, False or None).
350 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000351 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700352
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000353 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
354 manifest):
355 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800356
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000357 This function updates each project's revisionId with the commit hash from
358 the superproject. It writes the updated manifest into a file and reloads
359 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800360
361 Args:
362 opt: Program options returned from optparse. See _Options().
363 args: Arguments to pass to GetProjects. See the GetProjects
364 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000365 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000366 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800367 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000368 have_superproject = manifest.superproject or any(
369 m.superproject for m in manifest.all_children)
370 if not have_superproject:
371 return
372
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000373 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000374 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700375 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000376 self._ReloadManifest(manifest_path, manifest)
377 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700378
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800379 all_projects = self.GetProjects(args,
380 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000381 submodules_ok=opt.fetch_submodules,
382 manifest=manifest,
383 all_manifests=not opt.this_manifest_only)
384
385 per_manifest = collections.defaultdict(list)
386 manifest_paths = {}
387 if opt.this_manifest_only:
388 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700389 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000390 for p in all_projects:
391 per_manifest[p.manifest.path_prefix].append(p)
392
393 superproject_logging_data = {}
394 need_unload = False
395 for m in self.ManifestList(opt):
396 if not m.path_prefix in per_manifest:
397 continue
398 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
399 if superproject_logging_data:
400 superproject_logging_data['multimanifest'] = True
401 superproject_logging_data.update(
402 superproject=use_super,
403 haslocalmanifests=bool(m.HasLocalManifests),
404 hassuperprojecttag=bool(m.superproject),
405 )
406 if use_super and (m.IsMirror or m.IsArchive):
407 # Don't use superproject, because we have no working tree.
408 use_super = False
409 superproject_logging_data['superproject'] = False
410 superproject_logging_data['noworktree'] = True
411 if opt.use_superproject is not False:
412 print(f'{m.path_prefix}: not using superproject because there is no '
413 'working tree.')
414
415 if not use_super:
416 continue
417 m.superproject.SetQuiet(opt.quiet)
418 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
419 m.superproject.SetPrintMessages(print_messages)
420 update_result = m.superproject.UpdateProjectsRevisionId(
421 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
422 manifest_path = update_result.manifest_path
423 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
424 if manifest_path:
425 m.SetManifestOverride(manifest_path)
426 need_unload = True
427 else:
428 if print_messages:
429 print(f'{m.path_prefix}: warning: Update of revisionId from '
430 'superproject has failed, repo sync will not use superproject '
431 'to fetch the source. ',
432 'Please resync with the --no-use-superproject option to avoid '
433 'this repo warning.',
434 file=sys.stderr)
435 if update_result.fatal and opt.use_superproject is not None:
436 sys.exit(1)
437 if need_unload:
438 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800439
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500440 def _FetchProjectList(self, opt, projects):
441 """Main function of the fetch worker.
442
443 The projects we're given share the same underlying git object store, so we
444 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800445
David James8d201162013-10-11 17:03:19 -0700446 Delegates most of the work to _FetchHelper.
447
448 Args:
449 opt: Program options returned from optparse. See _Options().
450 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700451 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500452 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700453
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500454 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700455 """Fetch git objects for a single project.
456
David Pursehousec1b86a22012-11-14 11:36:51 +0900457 Args:
458 opt: Program options returned from optparse. See _Options().
459 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700460
461 Returns:
462 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900463 """
David Rileye0684ad2017-04-05 00:02:59 -0700464 start = time.time()
465 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500466 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900467 try:
LaMont Jones1eddca82022-09-01 15:15:04 +0000468 sync_result = project.Sync_NetworkHalf(
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500469 quiet=opt.quiet,
470 verbose=opt.verbose,
471 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000472 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500473 force_sync=opt.force_sync,
474 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000475 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500476 optimized_fetch=opt.optimized_fetch,
477 retry_fetches=opt.retry_fetches,
478 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400479 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000480 clone_filter=project.manifest.CloneFilter,
481 partial_clone_exclude=project.manifest.PartialCloneExclude)
LaMont Jones1eddca82022-09-01 15:15:04 +0000482 success = sync_result.success
Doug Andersonfc06ced2011-03-16 15:49:18 -0700483
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500484 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400485 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500486 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700487
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500488 if not success:
489 print('error: Cannot fetch %s from %s'
490 % (project.name, project.remote.url),
491 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700492 except GitError as e:
493 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500494 except Exception as e:
495 print('error: Cannot fetch %s (%s: %s)'
496 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
497 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500498
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500499 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000500 return _FetchOneResult(success, project, start, finish,
501 sync_result.remote_fetched)
David James8d201162013-10-11 17:03:19 -0700502
Mike Frysinger339f2df2021-05-06 00:44:42 -0400503 @classmethod
504 def _FetchInitChild(cls, ssh_proxy):
505 cls.ssh_proxy = ssh_proxy
506
507 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500508 ret = True
509
Mike Frysinger355f4392022-07-20 17:15:29 -0400510 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700511 fetched = set()
LaMont Jones1eddca82022-09-01 15:15:04 +0000512 remote_fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400513 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800514
David James89ece422014-01-09 18:51:58 -0800515 objdir_project_map = dict()
516 for project in projects:
517 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500518 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700519
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500520 def _ProcessResults(results_sets):
521 ret = True
522 for results in results_sets:
LaMont Jones1eddca82022-09-01 15:15:04 +0000523 for result in results:
524 success = result.success
525 project = result.project
526 start = result.start
527 finish = result.finish
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500528 self._fetch_times.Set(project, finish - start)
529 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
530 start, finish, success)
LaMont Jones1eddca82022-09-01 15:15:04 +0000531 if result.remote_fetched:
532 remote_fetched.add(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500533 # Check for any errors before running any more tasks.
534 # ...we'll let existing jobs finish, though.
535 if not success:
536 ret = False
537 else:
538 fetched.add(project.gitdir)
539 pm.update(msg=project.name)
540 if not ret and opt.fail_fast:
541 break
542 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700543
Mike Frysinger339f2df2021-05-06 00:44:42 -0400544 # We pass the ssh proxy settings via the class. This allows multiprocessing
545 # to pickle it up when spawning children. We can't pass it as an argument
546 # to _FetchProjectList below as multiprocessing is unable to pickle those.
547 Sync.ssh_proxy = None
548
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500549 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400550 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400551 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500552 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
553 ret = False
554 else:
555 # Favor throughput over responsiveness when quiet. It seems that imap()
556 # will yield results in batches relative to chunksize, so even as the
557 # children finish a sync, we won't see the result until one child finishes
558 # ~chunksize jobs. When using a large --jobs with large chunksize, this
559 # can be jarring as there will be a large initial delay where repo looks
560 # like it isn't doing anything and sits at 0%, but then suddenly completes
561 # a lot of jobs all at once. Since this code is more network bound, we
562 # can accept a bit more CPU overhead with a smaller chunksize so that the
563 # user sees more immediate & continuous feedback.
564 if opt.quiet:
565 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800566 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500567 pm.update(inc=0, msg='warming up')
568 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800569 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
570 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500571 results = pool.imap_unordered(
572 functools.partial(self._FetchProjectList, opt),
573 projects_list,
574 chunksize=chunksize)
575 if not _ProcessResults(results):
576 ret = False
577 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800578
Mike Frysinger339f2df2021-05-06 00:44:42 -0400579 # Cleanup the reference now that we're done with it, and we're going to
580 # release any resources it points to. If we don't, later multiprocessing
581 # usage (e.g. checkouts) will try to pickle and then crash.
582 del Sync.ssh_proxy
583
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700584 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700585 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700586
LaMont Jonesa46047a2022-04-07 21:57:06 +0000587 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400588 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200589
LaMont Jones1eddca82022-09-01 15:15:04 +0000590 return _FetchResult(ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700591
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000592 def _FetchMain(self, opt, args, all_projects, err_event,
593 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400594 """The main network fetch loop.
595
596 Args:
597 opt: Program options returned from optparse. See _Options().
598 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200599 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400600 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400601 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000602 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200603
604 Returns:
605 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400606 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000607 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400608
609 to_fetch = []
610 now = time.time()
611 if _ONE_DAY_S <= (now - rp.LastFetch):
612 to_fetch.append(rp)
613 to_fetch.extend(all_projects)
614 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
615
LaMont Jones1eddca82022-09-01 15:15:04 +0000616 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
617 success = result.success
618 fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400619 if not success:
620 err_event.set()
621
622 _PostRepoFetch(rp, opt.repo_verify)
623 if opt.network_only:
624 # bail out now; the rest touches the working tree
625 if err_event.is_set():
626 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
627 sys.exit(1)
LaMont Jones1eddca82022-09-01 15:15:04 +0000628 return _FetchMainResult([])
Mike Frysingerb4429432021-05-05 20:03:26 -0400629
630 # Iteratively fetch missing and/or nested unregistered submodules
631 previously_missing_set = set()
632 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000633 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400634 all_projects = self.GetProjects(args,
635 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000636 submodules_ok=opt.fetch_submodules,
637 manifest=manifest,
638 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400639 missing = []
640 for project in all_projects:
641 if project.gitdir not in fetched:
642 missing.append(project)
643 if not missing:
644 break
645 # Stop us from non-stopped fetching actually-missing repos: If set of
646 # missing repos has not been changed from last fetch, we break.
647 missing_set = set(p.name for p in missing)
648 if previously_missing_set == missing_set:
649 break
650 previously_missing_set = missing_set
LaMont Jones1eddca82022-09-01 15:15:04 +0000651 result = self._Fetch(missing, opt, err_event, ssh_proxy)
652 success = result.success
653 new_fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400654 if not success:
655 err_event.set()
656 fetched.update(new_fetched)
657
LaMont Jones1eddca82022-09-01 15:15:04 +0000658 return _FetchMainResult(all_projects)
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200659
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500660 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700661 """Checkout work tree for one project
662
663 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500664 detach_head: Whether to leave a detached HEAD.
665 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700666 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700667
668 Returns:
669 Whether the fetch was successful.
670 """
Xin Li745be2e2019-06-03 11:24:30 -0700671 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000672 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500673 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700674 success = False
675 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500676 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500677 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700678 except GitError as e:
679 print('error.GitError: Cannot checkout %s: %s' %
680 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500681 except Exception as e:
682 print('error: Cannot checkout %s: %s: %s' %
683 (project.name, type(e).__name__, str(e)),
684 file=sys.stderr)
685 raise
Xin Li745be2e2019-06-03 11:24:30 -0700686
Mike Frysingerebf04a42021-02-23 20:48:04 -0500687 if not success:
688 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
689 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000690 return _CheckoutOneResult(success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700691
Mike Frysingerebf04a42021-02-23 20:48:04 -0500692 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700693 """Checkout projects listed in all_projects
694
695 Args:
696 all_projects: List of all projects that should be checked out.
697 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500698 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700699 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500700 # Only checkout projects with worktrees.
701 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700702
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500703 def _ProcessResults(pool, pm, results):
704 ret = True
LaMont Jones1eddca82022-09-01 15:15:04 +0000705 for result in results:
706 success = result.success
707 project = result.project
708 start = result.start
709 finish = result.finish
Mike Frysingerebf04a42021-02-23 20:48:04 -0500710 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
711 start, finish, success)
712 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500713 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500714 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500715 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500716 err_results.append(project.relpath)
717 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500718 if pool:
719 pool.close()
720 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500721 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500722 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700723
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500724 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400725 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500726 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
727 all_projects,
728 callback=_ProcessResults,
729 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500730
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000731 def _backup_cruft(self, bare_git):
732 """Save a copy of any cruft from `git gc`."""
733 # Find any cruft packs in the current gitdir, and save them.
734 # b/221065125 (repo sync complains that objects are missing). This does
735 # not prevent that state, but makes it so that the missing objects are
736 # available.
737 objdir = bare_git._project.objdir
738 pack_dir = os.path.join(objdir, 'pack')
739 bak_dir = os.path.join(objdir, '.repo', 'pack.bak')
740 if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir):
741 return
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000742 files = set(platform_utils.listdir(pack_dir))
743 to_backup = []
744 for f in files:
745 base, ext = os.path.splitext(f)
746 if base + '.mtimes' in files:
747 to_backup.append(f)
748 if to_backup:
749 os.makedirs(bak_dir, exist_ok=True)
750 for fname in to_backup:
751 bak_fname = os.path.join(bak_dir, fname)
752 if not os.path.exists(bak_fname):
Joanna Wanga6c52f52022-11-03 16:51:19 -0400753 with Trace('%s saved %s', bare_git._project.name, fname):
754 # Use a tmp file so that we are sure of a complete copy.
755 shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp')
756 shutil.move(bak_fname + '.tmp', bak_fname)
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000757
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000758 @staticmethod
759 def _GetPreciousObjectsState(project: Project, opt):
760 """Get the preciousObjects state for the project.
761
762 Args:
763 project (Project): the project to examine, and possibly correct.
764 opt (optparse.Values): options given to sync.
765
766 Returns:
767 Expected state of extensions.preciousObjects:
768 False: Should be disabled. (not present)
769 True: Should be enabled.
770 """
771 if project.use_git_worktrees:
772 return False
773 projects = project.manifest.GetProjectsWithName(project.name,
774 all_manifests=True)
775 if len(projects) == 1:
776 return False
777 relpath = project.RelPath(local=opt.this_manifest_only)
778 if len(projects) > 1:
779 # Objects are potentially shared with another project.
780 # See the logic in Project.Sync_NetworkHalf regarding UseAlternates.
781 # - When False, shared projects share (via symlink)
782 # .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects
783 # directory. All objects are precious, since there is no project with a
784 # complete set of refs.
785 # - When True, shared projects share (via info/alternates)
786 # .repo/project-objects/{PROJECT_NAME}.git as an alternate object store,
787 # which is written only on the first clone of the project, and is not
788 # written subsequently. (When Sync_NetworkHalf sees that it exists, it
789 # makes sure that the alternates file points there, and uses a
790 # project-local .git/objects directory for all syncs going forward.
791 # We do not support switching between the options. The environment
792 # variable is present for testing and migration only.
793 return not project.UseAlternates
794 print(f'\r{relpath}: project not found in manifest.', file=sys.stderr)
795 return False
796
797 def _RepairPreciousObjectsState(self, project: Project, opt):
798 """Correct the preciousObjects state for the project.
799
800 Args:
801 project (Project): the project to examine, and possibly correct.
802 opt (optparse.Values): options given to sync.
803 """
804 expected = self._GetPreciousObjectsState(project, opt)
805 actual = project.config.GetBoolean('extensions.preciousObjects') or False
806 relpath = project.RelPath(local = opt.this_manifest_only)
807
808 if (expected != actual and
809 not project.config.GetBoolean('repo.preservePreciousObjects')):
810 # If this is unexpected, log it and repair.
811 Trace(f'{relpath} expected preciousObjects={expected}, got {actual}')
812 if expected:
813 if not opt.quiet:
814 print('\r%s: Shared project %s found, disabling pruning.' %
815 (relpath, project.name))
816 if git_require((2, 7, 0)):
817 project.EnableRepositoryExtension('preciousObjects')
818 else:
819 # This isn't perfect, but it's the best we can do with old git.
820 print('\r%s: WARNING: shared projects are unreliable when using '
821 'old versions of git; please upgrade to git-2.7.0+.'
822 % (relpath,),
823 file=sys.stderr)
824 project.config.SetString('gc.pruneExpire', 'never')
825 else:
826 if not opt.quiet:
827 print(f'\r{relpath}: not shared, disabling pruning.')
828 project.config.SetString('extensions.preciousObjects', None)
829 project.config.SetString('gc.pruneExpire', None)
830
Mike Frysinger5a033082019-09-23 19:21:20 -0400831 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400832 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400833 pm.update(inc=0, msg='prescan')
834
Allen Webb4ee4a452021-10-07 10:42:38 -0500835 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700836 for project in projects:
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000837 self._RepairPreciousObjectsState(project, opt)
838
Allen Webb669efd02021-10-01 15:25:31 -0500839 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500840 # Only call git gc once per objdir, but call pack-refs for the remainder.
841 if project.objdir not in tidy_dirs:
842 tidy_dirs[project.objdir] = (
843 True, # Run a full gc.
844 project.bare_git,
845 )
846 elif project.gitdir not in tidy_dirs:
847 tidy_dirs[project.gitdir] = (
848 False, # Do not run a full gc; just run pack-refs.
849 project.bare_git,
850 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400851
Mike Frysinger355f4392022-07-20 17:15:29 -0400852 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700853
LaMont Jonesacc4c852022-09-22 19:05:01 +0000854 gc_args = ['--auto']
855 backup_cruft = False
856 if git_require((2, 37, 0)):
857 gc_args.append('--cruft')
858 backup_cruft = True
LaMont Jones891e8f72022-09-08 20:17:58 +0000859 pack_refs_args = ()
Dave Borowitz18857212012-10-23 17:02:59 -0700860 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500861 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400862 pm.update(msg=bare_git._project.name)
LaMont Jones891e8f72022-09-08 20:17:58 +0000863
Allen Webb4ee4a452021-10-07 10:42:38 -0500864 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000865 bare_git.gc(*gc_args)
Allen Webb4ee4a452021-10-07 10:42:38 -0500866 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000867 bare_git.pack_refs(*pack_refs_args)
LaMont Jonesacc4c852022-09-22 19:05:01 +0000868 if backup_cruft:
869 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400870 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700871 return
872
Mike Frysinger355f4392022-07-20 17:15:29 -0400873 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400874 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700875
876 threads = set()
877 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700878
Allen Webb4ee4a452021-10-07 10:42:38 -0500879 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400880 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700881 try:
882 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500883 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000884 bare_git.gc(*gc_args, config=config)
Allen Webb4ee4a452021-10-07 10:42:38 -0500885 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000886 bare_git.pack_refs(*pack_refs_args, config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700887 except GitError:
888 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900889 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700890 err_event.set()
891 raise
892 finally:
LaMont Jonesacc4c852022-09-22 19:05:01 +0000893 if backup_cruft:
894 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400895 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700896 sem.release()
897
Allen Webb4ee4a452021-10-07 10:42:38 -0500898 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500899 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700900 break
901 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500902 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700903 t.daemon = True
904 threads.add(t)
905 t.start()
906
907 for t in threads:
908 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400909 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700910
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000911 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700912 """Reload the manfiest from the file specified by the |manifest_name|.
913
914 It unloads the manifest if |manifest_name| is None.
915
916 Args:
917 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000918 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700919 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800920 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000921 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000922 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800923 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000924 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800925
LaMont Jonesa46047a2022-04-07 21:57:06 +0000926 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000927 """Update the cached projects list for |manifest|
928
929 In a multi-manifest checkout, each manifest has its own project.list.
930
931 Args:
932 opt: Program options returned from optparse. See _Options().
933 manifest: The manifest to use.
934
935 Returns:
936 0: success
937 1: failure
938 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700939 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000940 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
941 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700942 if project.relpath:
943 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700944 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000945 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700946 old_project_paths = []
947
948 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500949 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700950 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800951 # In reversed order, so subfolders are deleted before parent folder.
952 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700953 if not path:
954 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700955 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900956 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000957 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700958 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900959 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000960 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900961 name=path,
962 remote=RemoteSpec('origin'),
963 gitdir=gitdir,
964 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500965 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000966 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900967 relpath=path,
968 revisionExpr='HEAD',
969 revisionId=None,
970 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500971 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900972 quiet=opt.quiet,
973 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400974 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700975
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700976 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500977 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700978 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700979 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700980 return 0
981
LaMont Jonesa46047a2022-04-07 21:57:06 +0000982 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800983 """Save all dests of copyfile and linkfile, and update them if needed.
984
985 Returns:
986 Whether update was successful.
987 """
988 new_paths = {}
989 new_linkfile_paths = []
990 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000991 for project in self.GetProjects(None, missing_ok=True,
992 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +0800993 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
994 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
995
996 new_paths = {
997 'linkfile': new_linkfile_paths,
998 'copyfile': new_copyfile_paths,
999 }
1000
1001 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001002 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +08001003 old_copylinkfile_paths = {}
1004
1005 if os.path.exists(copylinkfile_path):
1006 with open(copylinkfile_path, 'rb') as fp:
1007 try:
1008 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001009 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +08001010 print('error: %s is not a json formatted file.' %
1011 copylinkfile_path, file=sys.stderr)
1012 platform_utils.remove(copylinkfile_path)
1013 return False
1014
1015 need_remove_files = []
1016 need_remove_files.extend(
1017 set(old_copylinkfile_paths.get('linkfile', [])) -
1018 set(new_linkfile_paths))
1019 need_remove_files.extend(
1020 set(old_copylinkfile_paths.get('copyfile', [])) -
1021 set(new_copyfile_paths))
1022
1023 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -04001024 # Try to remove the updated copyfile or linkfile.
1025 # So, if the file is not exist, nothing need to do.
1026 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +08001027
1028 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
1029 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
1030 json.dump(new_paths, fp)
1031 return True
1032
LaMont Jonesa46047a2022-04-07 21:57:06 +00001033 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1034 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001035 print('error: cannot smart sync: no manifest server defined in '
1036 'manifest', file=sys.stderr)
1037 sys.exit(1)
1038
LaMont Jonesa46047a2022-04-07 21:57:06 +00001039 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001040 if not opt.quiet:
1041 print('Using manifest server %s' % manifest_server)
1042
David Pursehouseeeff3532020-02-12 11:24:10 +09001043 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001044 username = None
1045 password = None
1046 if opt.manifest_server_username and opt.manifest_server_password:
1047 username = opt.manifest_server_username
1048 password = opt.manifest_server_password
1049 else:
1050 try:
1051 info = netrc.netrc()
1052 except IOError:
1053 # .netrc file does not exist or could not be opened
1054 pass
1055 else:
1056 try:
1057 parse_result = urllib.parse.urlparse(manifest_server)
1058 if parse_result.hostname:
1059 auth = info.authenticators(parse_result.hostname)
1060 if auth:
1061 username, _account, password = auth
1062 else:
1063 print('No credentials found for %s in .netrc'
1064 % parse_result.hostname, file=sys.stderr)
1065 except netrc.NetrcParseError as e:
1066 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
1067
1068 if (username and password):
1069 manifest_server = manifest_server.replace('://', '://%s:%s@' %
1070 (username, password),
1071 1)
1072
1073 transport = PersistentTransport(manifest_server)
1074 if manifest_server.startswith('persistent-'):
1075 manifest_server = manifest_server[len('persistent-'):]
1076
1077 try:
1078 server = xmlrpc.client.Server(manifest_server, transport=transport)
1079 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001080 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001081
Mike Frysinger56ce3462019-12-04 19:30:48 -05001082 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -05001083 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001084 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -05001085 elif ('TARGET_PRODUCT' in os.environ and
1086 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -05001087 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
1088 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001089 [success, manifest_str] = server.GetApprovedManifest(branch, target)
1090 else:
1091 [success, manifest_str] = server.GetApprovedManifest(branch)
1092 else:
1093 assert(opt.smart_tag)
1094 [success, manifest_str] = server.GetManifest(opt.smart_tag)
1095
1096 if success:
1097 manifest_name = os.path.basename(smart_sync_manifest_path)
1098 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001099 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001100 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001101 except IOError as e:
1102 print('error: cannot write manifest to %s:\n%s'
1103 % (smart_sync_manifest_path, e),
1104 file=sys.stderr)
1105 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001106 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001107 else:
1108 print('error: manifest server RPC call failed: %s' %
1109 manifest_str, file=sys.stderr)
1110 sys.exit(1)
1111 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1112 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001113 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001114 sys.exit(1)
1115 except xmlrpc.client.ProtocolError as e:
1116 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001117 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001118 file=sys.stderr)
1119 sys.exit(1)
1120
1121 return manifest_name
1122
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001123 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
1124 """Fetch & update the local manifest project.
1125
1126 After syncing the manifest project, if the manifest has any sub manifests,
1127 those are recursively processed.
1128
1129 Args:
1130 opt: Program options returned from optparse. See _Options().
1131 mp: the manifestProject to query.
1132 manifest_name: Manifest file to be reloaded.
1133 """
1134 if not mp.standalone_manifest_url:
1135 self._UpdateManifestProject(opt, mp, manifest_name)
1136
1137 if mp.manifest.submanifests:
1138 for submanifest in mp.manifest.submanifests.values():
1139 child = submanifest.repo_client.manifest
1140 child.manifestProject.SyncWithPossibleInit(
1141 submanifest,
1142 current_branch_only=self._GetCurrentBranchOnly(opt, child),
1143 verbose=opt.verbose,
1144 tags=opt.tags,
1145 git_event_log=self.git_event_log,
1146 )
1147 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
1148
Mike Frysingerfb527e32019-08-27 02:34:32 -04001149 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001150 """Fetch & update the local manifest project.
1151
1152 Args:
1153 opt: Program options returned from optparse. See _Options().
1154 mp: the manifestProject to query.
1155 manifest_name: Manifest file to be reloaded.
1156 """
Mike Frysingerfb527e32019-08-27 02:34:32 -04001157 if not opt.local_only:
1158 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -05001159 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001160 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +02001161 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001162 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -04001163 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001164 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001165 submodules=mp.manifest.HasSubmodules,
1166 clone_filter=mp.manifest.CloneFilter,
1167 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001168 finish = time.time()
1169 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1170 start, finish, success)
1171
1172 if mp.HasChanges:
1173 syncbuf = SyncBuffer(mp.config)
1174 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001175 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001176 clean = syncbuf.Finish()
1177 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1178 start, time.time(), clean)
1179 if not clean:
1180 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001181 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001182
Mike Frysingerae6cb082019-08-27 01:10:59 -04001183 def ValidateOptions(self, opt, args):
1184 if opt.force_broken:
1185 print('warning: -f/--force-broken is now the default behavior, and the '
1186 'options are deprecated', file=sys.stderr)
1187 if opt.network_only and opt.detach_head:
1188 self.OptionParser.error('cannot combine -n and -d')
1189 if opt.network_only and opt.local_only:
1190 self.OptionParser.error('cannot combine -n and -l')
1191 if opt.manifest_name and opt.smart_sync:
1192 self.OptionParser.error('cannot combine -m and -s')
1193 if opt.manifest_name and opt.smart_tag:
1194 self.OptionParser.error('cannot combine -m and -t')
1195 if opt.manifest_server_username or opt.manifest_server_password:
1196 if not (opt.smart_sync or opt.smart_tag):
1197 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1198 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1199 self.OptionParser.error('both -u and -p must be given')
1200
Mike Frysinger0531a622021-11-05 15:22:01 -04001201 if opt.prune is None:
1202 opt.prune = True
1203
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001204 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001205 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001206 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001207 manifest = self.manifest
1208
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001209 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001210 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001211
Chirayu Desaia892b102013-06-11 14:18:46 +05301212 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001213 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001214 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301215
Xin Lid79a4bc2020-05-20 16:03:45 -07001216 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001217 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001218
Victor Boivie08c880d2011-04-19 10:32:52 +02001219 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001220 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001221 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001222 if os.path.isfile(smart_sync_manifest_path):
1223 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001224 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001225 except OSError as e:
1226 print('error: failed to remove existing smart sync override manifest: %s' %
1227 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001228
Mike Frysingerc99322a2021-05-04 15:32:43 -04001229 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001230
LaMont Jonesa46047a2022-04-07 21:57:06 +00001231 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001232 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001233 cb = rp.CurrentBranch
1234 if cb:
1235 base = rp.GetBranch(cb).merge
1236 if not base or not base.startswith('refs/heads/'):
1237 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001238 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001239 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001240
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001241 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001242 if not m.manifestProject.standalone_manifest_url:
1243 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001244
LaMont Jones4112c072022-08-24 17:32:25 +00001245 if opt.repo_upgraded:
1246 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001247
LaMont Jones4112c072022-08-24 17:32:25 +00001248 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001249 if opt.mp_update:
1250 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1251 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001252 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001253
Mike Frysinger355f4392022-07-20 17:15:29 -04001254 # Now that the manifests are up-to-date, setup the jobs value.
1255 if opt.jobs is None:
1256 # User has not made a choice, so use the manifest settings.
1257 opt.jobs = mp.default.sync_j
1258 if opt.jobs is not None:
1259 # Neither user nor manifest have made a choice.
1260 if opt.jobs_network is None:
1261 opt.jobs_network = opt.jobs
1262 if opt.jobs_checkout is None:
1263 opt.jobs_checkout = opt.jobs
1264 # Setup defaults if jobs==0.
1265 if not opt.jobs:
1266 if not opt.jobs_network:
1267 opt.jobs_network = 1
1268 if not opt.jobs_checkout:
1269 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1270 opt.jobs = os.cpu_count()
1271
1272 # Try to stay under user rlimit settings.
1273 #
1274 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1275 # that to scale down the number of jobs. Unfortunately there isn't an easy
1276 # way to determine this reliably as systems change, but it was last measured
1277 # by hand in 2011.
1278 soft_limit, _ = _rlimit_nofile()
1279 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1280 opt.jobs = min(opt.jobs, jobs_soft_limit)
1281 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1282 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1283
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001284 superproject_logging_data = {}
1285 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1286 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001287
Simran Basib9a1b732015-08-20 12:19:28 -07001288 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001289 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001290 gitc_projects = []
1291 opened_projects = []
1292 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001293 if project.relpath in self.gitc_manifest.paths and \
1294 self.gitc_manifest.paths[project.relpath].old_revision:
1295 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001296 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001297 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001298
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001299 if not args:
1300 gitc_projects = None
1301
1302 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001303 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001304 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1305 if manifest_name:
1306 manifest.Override(manifest_name)
1307 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001308 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001309 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1310 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001311 gitc_projects)
1312 print('GITC client successfully synced.')
1313
1314 # The opened projects need to be synced as normal, therefore we
1315 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001316 # TODO: make this more reliable -- if there's a project name/path overlap,
1317 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001318 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001319 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001320 if not args:
1321 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001322
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001323 all_projects = self.GetProjects(args,
1324 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001325 submodules_ok=opt.fetch_submodules,
1326 manifest=manifest,
1327 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001328
Mike Frysinger5a033082019-09-23 19:21:20 -04001329 err_network_sync = False
1330 err_update_projects = False
LaMont Jonesb6cfa092022-10-26 16:34:40 +00001331 err_update_linkfiles = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001332
LaMont Jonesa46047a2022-04-07 21:57:06 +00001333 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001334 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001335 with multiprocessing.Manager() as manager:
1336 with ssh.ProxyManager(manager) as ssh_proxy:
1337 # Initialize the socket dir once in the parent.
1338 ssh_proxy.sock()
LaMont Jones1eddca82022-09-01 15:15:04 +00001339 result = self._FetchMain(opt, args, all_projects, err_event,
1340 ssh_proxy, manifest)
1341 all_projects = result.all_projects
Mike Frysinger339f2df2021-05-06 00:44:42 -04001342
1343 if opt.network_only:
1344 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001345
1346 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001347 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001348 err_network_sync = True
1349 if opt.fail_fast:
1350 print('\nerror: Exited sync due to fetch errors.\n'
1351 'Local checkouts *not* updated. Resolve network issues & '
1352 'retry.\n'
1353 '`repo sync -l` will update some local checkouts.',
1354 file=sys.stderr)
1355 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001356
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001357 for m in self.ManifestList(opt):
1358 if m.IsMirror or m.IsArchive:
1359 # bail out now, we have no working tree
1360 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001361
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001362 if self.UpdateProjectList(opt, m):
1363 err_event.set()
1364 err_update_projects = True
1365 if opt.fail_fast:
1366 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1367 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001368
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001369 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1370 if err_update_linkfiles:
1371 err_event.set()
1372 if opt.fail_fast:
1373 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1374 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001375
Mike Frysinger5a033082019-09-23 19:21:20 -04001376 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001377 # NB: We don't exit here because this is the last step.
1378 err_checkout = not self._Checkout(all_projects, opt, err_results)
1379 if err_checkout:
1380 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001381
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001382 printed_notices = set()
1383 # If there's a notice that's supposed to print at the end of the sync,
1384 # print it now... But avoid printing duplicate messages, and preserve
1385 # order.
1386 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1387 if m.notice and m.notice not in printed_notices:
1388 print(m.notice)
1389 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001390
Mike Frysinger5a033082019-09-23 19:21:20 -04001391 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001392 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001393 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1394 if err_network_sync:
1395 print('error: Downloading network changes failed.', file=sys.stderr)
1396 if err_update_projects:
1397 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001398 if err_update_linkfiles:
1399 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001400 if err_checkout:
1401 print('error: Checking out local projects failed.', file=sys.stderr)
1402 if err_results:
1403 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1404 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1405 file=sys.stderr)
1406 sys.exit(1)
1407
Raman Tenneti7954de12021-07-28 14:36:49 -07001408 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001409 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1410 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001411
1412 # Update and log with the new sync analysis state.
1413 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001414 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1415 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001416
Mike Frysingere19d9e12020-02-12 11:23:32 -05001417 if not opt.quiet:
1418 print('repo sync has finished successfully.')
1419
David Pursehouse819827a2020-02-12 15:20:19 +09001420
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001421def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001422 # Link the docs for the internal .repo/ layout for people
1423 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1424 if not platform_utils.islink(link):
1425 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1426 try:
1427 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001428 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001429 pass
1430
Conley Owens094cdbe2014-01-30 15:09:59 -08001431 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001432 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001433 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001434 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001435 if project.Exists:
1436 project.PostRepoUpgrade()
1437
David Pursehouse819827a2020-02-12 15:20:19 +09001438
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001439def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001440 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001441 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001442 wrapper = Wrapper()
1443 try:
1444 rev = rp.bare_git.describe(rp.GetRevisionId())
1445 except GitError:
1446 rev = None
1447 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1448 # See if we're held back due to missing signed tag.
1449 current_revid = rp.bare_git.rev_parse('HEAD')
1450 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1451 if current_revid != new_revid:
1452 # We want to switch to the new rev, but also not trash any uncommitted
1453 # changes. This helps with local testing/hacking.
1454 # If a local change has been made, we will throw that away.
1455 # We also have to make sure this will switch to an older commit if that's
1456 # the latest tag in order to support release rollback.
1457 try:
1458 rp.work_git.reset('--keep', new_rev)
1459 except GitError as e:
1460 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001461 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001462 raise RepoChangedException(['--repo-upgraded'])
1463 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001464 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001465 else:
1466 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001467 print('repo version %s is current' % rp.work_git.describe(HEAD),
1468 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001469
David Pursehouse819827a2020-02-12 15:20:19 +09001470
Dave Borowitz67700e92012-10-23 15:00:54 -07001471class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001472 _ALPHA = 0.5
1473
Dave Borowitz67700e92012-10-23 15:00:54 -07001474 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001475 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001476 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001477 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001478
1479 def Get(self, project):
1480 self._Load()
1481 return self._times.get(project.name, _ONE_DAY_S)
1482
1483 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001484 self._Load()
1485 name = project.name
1486 old = self._times.get(name, t)
1487 self._seen.add(name)
1488 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001489 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001490
1491 def _Load(self):
1492 if self._times is None:
1493 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001494 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001495 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001496 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001497 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001498 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001499
1500 def Save(self):
1501 if self._times is None:
1502 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001503
1504 to_delete = []
1505 for name in self._times:
1506 if name not in self._seen:
1507 to_delete.append(name)
1508 for name in to_delete:
1509 del self._times[name]
1510
Dave Borowitz67700e92012-10-23 15:00:54 -07001511 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001512 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001513 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001514 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001515 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001516
1517# This is a replacement for xmlrpc.client.Transport using urllib2
1518# and supporting persistent-http[s]. It cannot change hosts from
1519# request to request like the normal transport, the real url
1520# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001521
1522
Dan Willemsen0745bb22015-08-17 13:41:45 -07001523class PersistentTransport(xmlrpc.client.Transport):
1524 def __init__(self, orig_host):
1525 self.orig_host = orig_host
1526
1527 def request(self, host, handler, request_body, verbose=False):
1528 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1529 # Python doesn't understand cookies with the #HttpOnly_ prefix
1530 # Since we're only using them for HTTP, copy the file temporarily,
1531 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001532 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001533 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001534 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001535 try:
1536 with open(cookiefile) as f:
1537 for line in f:
1538 if line.startswith("#HttpOnly_"):
1539 line = line[len("#HttpOnly_"):]
1540 tmpcookiefile.write(line)
1541 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001542
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001543 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001544 try:
1545 cookiejar.load()
1546 except cookielib.LoadError:
1547 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001548 finally:
1549 tmpcookiefile.close()
1550 else:
1551 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001552
1553 proxyhandler = urllib.request.ProxyHandler
1554 if proxy:
1555 proxyhandler = urllib.request.ProxyHandler({
1556 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001557 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001558
1559 opener = urllib.request.build_opener(
1560 urllib.request.HTTPCookieProcessor(cookiejar),
1561 proxyhandler)
1562
1563 url = urllib.parse.urljoin(self.orig_host, handler)
1564 parse_results = urllib.parse.urlparse(url)
1565
1566 scheme = parse_results.scheme
1567 if scheme == 'persistent-http':
1568 scheme = 'http'
1569 if scheme == 'persistent-https':
1570 # If we're proxying through persistent-https, use http. The
1571 # proxy itself will do the https.
1572 if proxy:
1573 scheme = 'http'
1574 else:
1575 scheme = 'https'
1576
1577 # Parse out any authentication information using the base class
1578 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1579
1580 url = urllib.parse.urlunparse((
1581 scheme,
1582 host,
1583 parse_results.path,
1584 parse_results.params,
1585 parse_results.query,
1586 parse_results.fragment))
1587
1588 request = urllib.request.Request(url, request_body)
1589 if extra_headers is not None:
1590 for (name, header) in extra_headers:
1591 request.add_header(name, header)
1592 request.add_header('Content-Type', 'text/xml')
1593 try:
1594 response = opener.open(request)
1595 except urllib.error.HTTPError as e:
1596 if e.code == 501:
1597 # We may have been redirected through a login process
1598 # but our POST turned into a GET. Retry.
1599 response = opener.open(request)
1600 else:
1601 raise
1602
1603 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001604 # Response should be fairly small, so read it all at once.
1605 # This way we can show it to the user in case of error (e.g. HTML).
1606 data = response.read()
1607 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001608 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001609 except xml.parsers.expat.ExpatError as e:
1610 raise IOError(
1611 f'Parsing the manifest failed: {e}\n'
1612 f'Please report this to your manifest server admin.\n'
1613 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001614 p.close()
1615 return u.close()
1616
1617 def close(self):
1618 pass