blob: 755120312fbb3817fc081c5df400cb6a4df75c1b [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
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
LaMont Jones78dcd372022-10-25 22:38:07 +000028from typing import NamedTuple, List, Set
Mike Frysingeracf63b22019-06-13 02:24:21 -040029import urllib.error
30import urllib.parse
31import urllib.request
Mike Frysinger5951e302022-05-20 23:34:44 -040032import xml.parsers.expat
Mike Frysingeracf63b22019-06-13 02:24:21 -040033import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070034
Roy Lee18afd7f2010-05-09 04:32:08 +080035try:
36 import threading as _threading
37except ImportError:
38 import dummy_threading as _threading
39
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070040try:
41 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090042
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070043 def _rlimit_nofile():
44 return resource.getrlimit(resource.RLIMIT_NOFILE)
45except ImportError:
46 def _rlimit_nofile():
47 return (256, 256)
48
David Rileye0684ad2017-04-05 00:02:59 -070049import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040050from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090051from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090052from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080053import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070054import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070055from project import Project
56from project import RemoteSpec
Mike Frysinger355f4392022-07-20 17:15:29 -040057from command import Command, DEFAULT_LOCAL_JOBS, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080058from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070059import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070060from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070061from progress import Progress
Joanna Wanga6c52f52022-11-03 16:51:19 -040062from repo_trace import Trace
Mike Frysinger19e409c2021-05-05 19:44:35 -040063import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080064from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070065from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070066
Dave Borowitz67700e92012-10-23 15:00:54 -070067_ONE_DAY_S = 24 * 60 * 60
68
LaMont Jonesd7935532022-12-01 20:18:46 +000069# Env var to implicitly turn auto-gc back on. This was added to allow a user to
70# revert a change in default behavior in v2.29.9, and will be removed in a
71# future release.
LaMont Jones5ed8c632022-11-10 00:10:44 +000072_REPO_AUTO_GC = 'REPO_AUTO_GC'
73_AUTO_GC = os.environ.get(_REPO_AUTO_GC) == '1'
74
David Pursehouse819827a2020-02-12 15:20:19 +090075
LaMont Jones1eddca82022-09-01 15:15:04 +000076class _FetchOneResult(NamedTuple):
77 """_FetchOne return value.
78
79 Attributes:
80 success (bool): True if successful.
81 project (Project): The fetched project.
82 start (float): The starting time.time().
83 finish (float): The ending time.time().
84 remote_fetched (bool): True if the remote was actually queried.
85 """
86 success: bool
87 project: Project
88 start: float
89 finish: float
90 remote_fetched: bool
91
92
93class _FetchResult(NamedTuple):
94 """_Fetch return value.
95
96 Attributes:
97 success (bool): True if successful.
LaMont Jones78dcd372022-10-25 22:38:07 +000098 projects (Set[str]): The names of the git directories of fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +000099 """
100 success: bool
LaMont Jones78dcd372022-10-25 22:38:07 +0000101 projects: Set[str]
LaMont Jones1eddca82022-09-01 15:15:04 +0000102
103
104class _FetchMainResult(NamedTuple):
105 """_FetchMain return value.
106
107 Attributes:
LaMont Jones78dcd372022-10-25 22:38:07 +0000108 all_projects (List[Project]): The fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000109 """
LaMont Jones78dcd372022-10-25 22:38:07 +0000110 all_projects: List[Project]
LaMont Jones1eddca82022-09-01 15:15:04 +0000111
112
113class _CheckoutOneResult(NamedTuple):
114 """_CheckoutOne return value.
115
116 Attributes:
117 success (bool): True if successful.
118 project (Project): The project.
119 start (float): The starting time.time().
120 finish (float): The ending time.time().
121 """
122 success: bool
123 project: Project
124 start: float
125 finish: float
126
127
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800128class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -0400129 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000130 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700131 helpSummary = "Update working tree to the latest revision"
132 helpUsage = """
133%prog [<project>...]
134"""
135 helpDescription = """
136The '%prog' command synchronizes local project directories
137with the remote repositories specified in the manifest. If a local
138project does not yet exist, it will clone a new local directory from
139the remote repository and set up tracking branches as specified in
140the manifest. If the local project already exists, '%prog'
141will update the remote branches and rebase any new local changes
142on top of the new remote changes.
143
144'%prog' will synchronize all projects listed at the command
145line. Projects can be specified either by name, or by a relative
146or absolute path to the project's local directory. If no projects
147are specified, '%prog' will synchronize all projects listed in
148the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700149
150The -d/--detach option can be used to switch specified projects
151back to the manifest revision. This option is especially helpful
152if the project is currently on a topic branch, but the manifest
153revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700154
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700155The -s/--smart-sync option can be used to sync to a known good
156build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200157manifest. The -t/--smart-tag option is similar and allows you to
158specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700159
David Pursehousecf76b1b2012-09-14 10:31:42 +0900160The -u/--manifest-server-username and -p/--manifest-server-password
161options can be used to specify a username and password to authenticate
162with the manifest server when using the -s or -t option.
163
164If -u and -p are not specified when using the -s or -t option, '%prog'
165will attempt to read authentication credentials for the manifest server
166from the user's .netrc file.
167
168'%prog' will not use authentication credentials from -u/-p or .netrc
169if the manifest server specified in the manifest file already includes
170credentials.
171
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400172By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400173to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500174
Kevin Degiabaa7f32014-11-12 11:27:45 -0700175The --force-sync option can be used to overwrite existing git
176directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900177object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700178refs may be removed when overwriting.
179
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500180The --force-remove-dirty option can be used to remove previously used
181projects with uncommitted changes. WARNING: This may cause data to be
182lost since uncommitted changes may be removed with projects that no longer
183exist in the manifest.
184
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700185The --no-clone-bundle option disables any attempt to use
186$URL/clone.bundle to bootstrap a new Git repository from a
187resumeable bundle file on a content delivery network. This
188may be necessary if there are problems with the local Python
189HTTP client or proxy configuration, but the Git binary works.
190
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800191The --fetch-submodules option enables fetching Git submodules
192of a project from server.
193
David Pursehousef2fad612015-01-29 14:36:28 +0900194The -c/--current-branch option can be used to only fetch objects that
195are on the branch specified by a project's revision.
196
David Pursehouseb1553542014-09-04 21:28:09 +0900197The --optimized-fetch option can be used to only fetch projects that
198are fixed to a sha1 revision if the sha1 revision does not already
199exist locally.
200
David Pursehouse74cfd272015-10-14 10:50:15 +0900201The --prune option can be used to remove any refs that no longer
202exist on the remote.
203
LaMont Jones7efab532022-09-01 15:41:12 +0000204The --auto-gc option can be used to trigger garbage collection on all
205projects. By default, repo does not run garbage collection.
206
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400207# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700208
209If at least one project remote URL uses an SSH connection (ssh://,
210git+ssh://, or user@host:path syntax) repo will automatically
211enable the SSH ControlMaster option when connecting to that host.
212This feature permits other projects in the same '%prog' session to
213reuse the same SSH tunnel, saving connection setup overheads.
214
215To disable this behavior on UNIX platforms, set the GIT_SSH
216environment variable to 'ssh'. For example:
217
218 export GIT_SSH=ssh
219 %prog
220
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400221# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700222
223This feature is automatically disabled on Windows, due to the lack
224of UNIX domain socket support.
225
226This feature is not compatible with url.insteadof rewrites in the
227user's ~/.gitconfig. '%prog' is currently not able to perform the
228rewrite early enough to establish the ControlMaster tunnel.
229
230If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
231later is required to fix a server side protocol bug.
232
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700233"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400234 # A value of 0 means we want parallel jobs, but we'll determine the default
235 # value later on.
236 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700237
Mike Frysinger9180a072021-04-13 14:57:40 -0400238 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400239 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400240 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400241 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400242 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
243 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400244
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500245 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200246 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400247 help='obsolete option (to be deleted in the future)')
248 p.add_option('--fail-fast',
249 dest='fail_fast', action='store_true',
250 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700251 p.add_option('--force-sync',
252 dest='force_sync', action='store_true',
253 help="overwrite an existing git directory if it needs to "
254 "point to a different object directory. WARNING: this "
255 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500256 p.add_option('--force-remove-dirty',
257 dest='force_remove_dirty', action='store_true',
258 help="force remove projects with uncommitted modifications if "
259 "projects no longer exist in the manifest. "
260 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900261 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700262 dest='local_only', action='store_true',
263 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900264 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100265 dest='mp_update', action='store_false', default='true',
266 help='use the existing manifest checkout as-is. '
267 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900268 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700269 dest='network_only', action='store_true',
270 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900271 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700272 dest='detach_head', action='store_true',
273 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900274 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700275 dest='current_branch_only', action='store_true',
276 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400277 p.add_option('--no-current-branch',
278 dest='current_branch_only', action='store_false',
279 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500280 p.add_option('-m', '--manifest-name',
281 dest='manifest_name',
282 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700283 p.add_option('--clone-bundle', action='store_true',
284 help='enable use of /clone.bundle on HTTP/HTTPS')
285 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700286 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800287 p.add_option('-u', '--manifest-server-username', action='store',
288 dest='manifest_server_username',
289 help='username to authenticate with the manifest server')
290 p.add_option('-p', '--manifest-server-password', action='store',
291 dest='manifest_server_password',
292 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800293 p.add_option('--fetch-submodules',
294 dest='fetch_submodules', action='store_true',
295 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800296 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700297 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700298 p.add_option('--no-use-superproject', action='store_false',
299 dest='use_superproject',
300 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400301 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400302 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700303 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400304 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400305 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900306 p.add_option('--optimized-fetch',
307 dest='optimized_fetch', action='store_true',
308 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600309 p.add_option('--retry-fetches',
310 default=0, action='store', type='int',
311 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400312 p.add_option('--prune', action='store_true',
313 help='delete refs that no longer exist on the remote (default)')
314 p.add_option('--no-prune', dest='prune', action='store_false',
315 help='do not delete refs that no longer exist on the remote')
LaMont Jones5ed8c632022-11-10 00:10:44 +0000316 p.add_option('--auto-gc', action='store_true', default=None,
LaMont Jones7efab532022-09-01 15:41:12 +0000317 help='run garbage collection on all synced projects')
318 p.add_option('--no-auto-gc', dest='auto_gc', action='store_false',
319 help='do not run garbage collection on any projects (default)')
Nico Sallembien6623b212010-05-11 12:57:01 -0700320 if show_smart:
321 p.add_option('-s', '--smart-sync',
322 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900323 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200324 p.add_option('-t', '--smart-tag',
325 dest='smart_tag', action='store',
326 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700327
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700328 g = p.add_option_group('repo Version options')
329 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500330 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700331 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700332 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800333 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700334 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700335
LaMont Jonesa46047a2022-04-07 21:57:06 +0000336 def _GetBranch(self, manifest_project):
337 """Returns the branch name for getting the approved smartsync manifest.
338
339 Args:
340 manifest_project: the manifestProject to query.
341 """
342 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800343 branch = b.merge
344 if branch.startswith(R_HEADS):
345 branch = branch[len(R_HEADS):]
346 return branch
347
LaMont Jonesa46047a2022-04-07 21:57:06 +0000348 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200349 """Returns whether current-branch or use-superproject options are enabled.
350
LaMont Jonesa46047a2022-04-07 21:57:06 +0000351 Args:
352 opt: Program options returned from optparse. See _Options().
353 manifest: The manifest to use.
354
Daniel Anderssond52ca422022-04-01 12:55:38 +0200355 Returns:
356 True if a superproject is requested, otherwise the value of the
357 current_branch option (True, False or None).
358 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000359 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700360
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000361 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
362 manifest):
363 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800364
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000365 This function updates each project's revisionId with the commit hash from
366 the superproject. It writes the updated manifest into a file and reloads
367 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800368
369 Args:
370 opt: Program options returned from optparse. See _Options().
371 args: Arguments to pass to GetProjects. See the GetProjects
372 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000373 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000374 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800375 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000376 have_superproject = manifest.superproject or any(
377 m.superproject for m in manifest.all_children)
378 if not have_superproject:
379 return
380
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000381 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000382 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700383 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000384 self._ReloadManifest(manifest_path, manifest)
385 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700386
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800387 all_projects = self.GetProjects(args,
388 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000389 submodules_ok=opt.fetch_submodules,
390 manifest=manifest,
391 all_manifests=not opt.this_manifest_only)
392
393 per_manifest = collections.defaultdict(list)
394 manifest_paths = {}
395 if opt.this_manifest_only:
396 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700397 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000398 for p in all_projects:
399 per_manifest[p.manifest.path_prefix].append(p)
400
401 superproject_logging_data = {}
402 need_unload = False
403 for m in self.ManifestList(opt):
404 if not m.path_prefix in per_manifest:
405 continue
406 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
407 if superproject_logging_data:
408 superproject_logging_data['multimanifest'] = True
409 superproject_logging_data.update(
410 superproject=use_super,
411 haslocalmanifests=bool(m.HasLocalManifests),
412 hassuperprojecttag=bool(m.superproject),
413 )
414 if use_super and (m.IsMirror or m.IsArchive):
415 # Don't use superproject, because we have no working tree.
416 use_super = False
417 superproject_logging_data['superproject'] = False
418 superproject_logging_data['noworktree'] = True
419 if opt.use_superproject is not False:
420 print(f'{m.path_prefix}: not using superproject because there is no '
421 'working tree.')
422
423 if not use_super:
424 continue
425 m.superproject.SetQuiet(opt.quiet)
426 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
427 m.superproject.SetPrintMessages(print_messages)
428 update_result = m.superproject.UpdateProjectsRevisionId(
429 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
430 manifest_path = update_result.manifest_path
431 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
432 if manifest_path:
433 m.SetManifestOverride(manifest_path)
434 need_unload = True
435 else:
436 if print_messages:
437 print(f'{m.path_prefix}: warning: Update of revisionId from '
438 'superproject has failed, repo sync will not use superproject '
439 'to fetch the source. ',
440 'Please resync with the --no-use-superproject option to avoid '
441 'this repo warning.',
442 file=sys.stderr)
443 if update_result.fatal and opt.use_superproject is not None:
444 sys.exit(1)
445 if need_unload:
446 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800447
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500448 def _FetchProjectList(self, opt, projects):
449 """Main function of the fetch worker.
450
451 The projects we're given share the same underlying git object store, so we
452 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800453
David James8d201162013-10-11 17:03:19 -0700454 Delegates most of the work to _FetchHelper.
455
456 Args:
457 opt: Program options returned from optparse. See _Options().
458 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700459 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500460 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700461
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500462 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700463 """Fetch git objects for a single project.
464
David Pursehousec1b86a22012-11-14 11:36:51 +0900465 Args:
466 opt: Program options returned from optparse. See _Options().
467 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700468
469 Returns:
470 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900471 """
David Rileye0684ad2017-04-05 00:02:59 -0700472 start = time.time()
473 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500474 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900475 try:
LaMont Jones1eddca82022-09-01 15:15:04 +0000476 sync_result = project.Sync_NetworkHalf(
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500477 quiet=opt.quiet,
478 verbose=opt.verbose,
479 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000480 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500481 force_sync=opt.force_sync,
482 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000483 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500484 optimized_fetch=opt.optimized_fetch,
485 retry_fetches=opt.retry_fetches,
486 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400487 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000488 clone_filter=project.manifest.CloneFilter,
489 partial_clone_exclude=project.manifest.PartialCloneExclude)
LaMont Jones1eddca82022-09-01 15:15:04 +0000490 success = sync_result.success
Doug Andersonfc06ced2011-03-16 15:49:18 -0700491
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500492 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400493 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500494 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700495
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500496 if not success:
497 print('error: Cannot fetch %s from %s'
498 % (project.name, project.remote.url),
499 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700500 except GitError as e:
501 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500502 except Exception as e:
503 print('error: Cannot fetch %s (%s: %s)'
504 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
505 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500506
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500507 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000508 return _FetchOneResult(success, project, start, finish,
509 sync_result.remote_fetched)
David James8d201162013-10-11 17:03:19 -0700510
Mike Frysinger339f2df2021-05-06 00:44:42 -0400511 @classmethod
512 def _FetchInitChild(cls, ssh_proxy):
513 cls.ssh_proxy = ssh_proxy
514
515 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500516 ret = True
517
Mike Frysinger355f4392022-07-20 17:15:29 -0400518 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700519 fetched = set()
LaMont Jones1eddca82022-09-01 15:15:04 +0000520 remote_fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400521 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800522
David James89ece422014-01-09 18:51:58 -0800523 objdir_project_map = dict()
524 for project in projects:
525 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500526 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700527
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500528 def _ProcessResults(results_sets):
529 ret = True
530 for results in results_sets:
LaMont Jones1eddca82022-09-01 15:15:04 +0000531 for result in results:
532 success = result.success
533 project = result.project
534 start = result.start
535 finish = result.finish
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500536 self._fetch_times.Set(project, finish - start)
537 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
538 start, finish, success)
LaMont Jones1eddca82022-09-01 15:15:04 +0000539 if result.remote_fetched:
540 remote_fetched.add(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500541 # Check for any errors before running any more tasks.
542 # ...we'll let existing jobs finish, though.
543 if not success:
544 ret = False
545 else:
546 fetched.add(project.gitdir)
547 pm.update(msg=project.name)
548 if not ret and opt.fail_fast:
549 break
550 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700551
Mike Frysinger339f2df2021-05-06 00:44:42 -0400552 # We pass the ssh proxy settings via the class. This allows multiprocessing
553 # to pickle it up when spawning children. We can't pass it as an argument
554 # to _FetchProjectList below as multiprocessing is unable to pickle those.
555 Sync.ssh_proxy = None
556
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500557 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400558 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400559 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500560 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
561 ret = False
562 else:
563 # Favor throughput over responsiveness when quiet. It seems that imap()
564 # will yield results in batches relative to chunksize, so even as the
565 # children finish a sync, we won't see the result until one child finishes
566 # ~chunksize jobs. When using a large --jobs with large chunksize, this
567 # can be jarring as there will be a large initial delay where repo looks
568 # like it isn't doing anything and sits at 0%, but then suddenly completes
569 # a lot of jobs all at once. Since this code is more network bound, we
570 # can accept a bit more CPU overhead with a smaller chunksize so that the
571 # user sees more immediate & continuous feedback.
572 if opt.quiet:
573 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800574 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500575 pm.update(inc=0, msg='warming up')
576 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800577 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
578 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500579 results = pool.imap_unordered(
580 functools.partial(self._FetchProjectList, opt),
581 projects_list,
582 chunksize=chunksize)
583 if not _ProcessResults(results):
584 ret = False
585 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800586
Mike Frysinger339f2df2021-05-06 00:44:42 -0400587 # Cleanup the reference now that we're done with it, and we're going to
588 # release any resources it points to. If we don't, later multiprocessing
589 # usage (e.g. checkouts) will try to pickle and then crash.
590 del Sync.ssh_proxy
591
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700592 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700593 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700594
LaMont Jonesa46047a2022-04-07 21:57:06 +0000595 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400596 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200597
LaMont Jones1eddca82022-09-01 15:15:04 +0000598 return _FetchResult(ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700599
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000600 def _FetchMain(self, opt, args, all_projects, err_event,
601 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400602 """The main network fetch loop.
603
604 Args:
605 opt: Program options returned from optparse. See _Options().
606 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200607 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400608 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400609 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000610 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200611
612 Returns:
613 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400614 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000615 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400616
617 to_fetch = []
618 now = time.time()
619 if _ONE_DAY_S <= (now - rp.LastFetch):
620 to_fetch.append(rp)
621 to_fetch.extend(all_projects)
622 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
623
LaMont Jones1eddca82022-09-01 15:15:04 +0000624 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
625 success = result.success
626 fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400627 if not success:
628 err_event.set()
629
630 _PostRepoFetch(rp, opt.repo_verify)
631 if opt.network_only:
632 # bail out now; the rest touches the working tree
633 if err_event.is_set():
634 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
635 sys.exit(1)
LaMont Jones1eddca82022-09-01 15:15:04 +0000636 return _FetchMainResult([])
Mike Frysingerb4429432021-05-05 20:03:26 -0400637
638 # Iteratively fetch missing and/or nested unregistered submodules
639 previously_missing_set = set()
640 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000641 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400642 all_projects = self.GetProjects(args,
643 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000644 submodules_ok=opt.fetch_submodules,
645 manifest=manifest,
646 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400647 missing = []
648 for project in all_projects:
649 if project.gitdir not in fetched:
650 missing.append(project)
651 if not missing:
652 break
653 # Stop us from non-stopped fetching actually-missing repos: If set of
654 # missing repos has not been changed from last fetch, we break.
655 missing_set = set(p.name for p in missing)
656 if previously_missing_set == missing_set:
657 break
658 previously_missing_set = missing_set
LaMont Jones1eddca82022-09-01 15:15:04 +0000659 result = self._Fetch(missing, opt, err_event, ssh_proxy)
660 success = result.success
661 new_fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400662 if not success:
663 err_event.set()
664 fetched.update(new_fetched)
665
LaMont Jones1eddca82022-09-01 15:15:04 +0000666 return _FetchMainResult(all_projects)
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200667
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500668 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700669 """Checkout work tree for one project
670
671 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500672 detach_head: Whether to leave a detached HEAD.
673 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700674 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700675
676 Returns:
677 Whether the fetch was successful.
678 """
Xin Li745be2e2019-06-03 11:24:30 -0700679 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000680 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500681 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700682 success = False
683 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500684 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500685 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700686 except GitError as e:
687 print('error.GitError: Cannot checkout %s: %s' %
688 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500689 except Exception as e:
690 print('error: Cannot checkout %s: %s: %s' %
691 (project.name, type(e).__name__, str(e)),
692 file=sys.stderr)
693 raise
Xin Li745be2e2019-06-03 11:24:30 -0700694
Mike Frysingerebf04a42021-02-23 20:48:04 -0500695 if not success:
696 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
697 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000698 return _CheckoutOneResult(success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700699
Mike Frysingerebf04a42021-02-23 20:48:04 -0500700 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700701 """Checkout projects listed in all_projects
702
703 Args:
704 all_projects: List of all projects that should be checked out.
705 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500706 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700707 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500708 # Only checkout projects with worktrees.
709 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700710
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500711 def _ProcessResults(pool, pm, results):
712 ret = True
LaMont Jones1eddca82022-09-01 15:15:04 +0000713 for result in results:
714 success = result.success
715 project = result.project
716 start = result.start
717 finish = result.finish
Mike Frysingerebf04a42021-02-23 20:48:04 -0500718 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
719 start, finish, success)
720 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500721 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500722 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500723 ret = False
LaMont Jonesbee4efb2022-09-30 17:46:52 +0000724 err_results.append(project.RelPath(local=opt.this_manifest_only))
Mike Frysingerebf04a42021-02-23 20:48:04 -0500725 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500726 if pool:
727 pool.close()
728 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500729 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500730 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700731
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500732 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400733 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500734 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
735 all_projects,
736 callback=_ProcessResults,
737 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500738
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000739 @staticmethod
740 def _GetPreciousObjectsState(project: Project, opt):
741 """Get the preciousObjects state for the project.
742
743 Args:
744 project (Project): the project to examine, and possibly correct.
745 opt (optparse.Values): options given to sync.
746
747 Returns:
748 Expected state of extensions.preciousObjects:
749 False: Should be disabled. (not present)
750 True: Should be enabled.
751 """
752 if project.use_git_worktrees:
753 return False
754 projects = project.manifest.GetProjectsWithName(project.name,
755 all_manifests=True)
756 if len(projects) == 1:
757 return False
758 relpath = project.RelPath(local=opt.this_manifest_only)
759 if len(projects) > 1:
760 # Objects are potentially shared with another project.
761 # See the logic in Project.Sync_NetworkHalf regarding UseAlternates.
762 # - When False, shared projects share (via symlink)
763 # .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects
764 # directory. All objects are precious, since there is no project with a
765 # complete set of refs.
766 # - When True, shared projects share (via info/alternates)
767 # .repo/project-objects/{PROJECT_NAME}.git as an alternate object store,
768 # which is written only on the first clone of the project, and is not
769 # written subsequently. (When Sync_NetworkHalf sees that it exists, it
770 # makes sure that the alternates file points there, and uses a
771 # project-local .git/objects directory for all syncs going forward.
772 # We do not support switching between the options. The environment
773 # variable is present for testing and migration only.
774 return not project.UseAlternates
775 print(f'\r{relpath}: project not found in manifest.', file=sys.stderr)
776 return False
777
LaMont Jones43549d82022-11-30 19:55:30 +0000778 def _SetPreciousObjectsState(self, project: Project, opt):
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000779 """Correct the preciousObjects state for the project.
780
781 Args:
LaMont Jones43549d82022-11-30 19:55:30 +0000782 project: the project to examine, and possibly correct.
783 opt: options given to sync.
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000784 """
785 expected = self._GetPreciousObjectsState(project, opt)
786 actual = project.config.GetBoolean('extensions.preciousObjects') or False
LaMont Jones43549d82022-11-30 19:55:30 +0000787 relpath = project.RelPath(local=opt.this_manifest_only)
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000788
LaMont Jones43549d82022-11-30 19:55:30 +0000789 if expected != actual:
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000790 # If this is unexpected, log it and repair.
791 Trace(f'{relpath} expected preciousObjects={expected}, got {actual}')
792 if expected:
793 if not opt.quiet:
794 print('\r%s: Shared project %s found, disabling pruning.' %
795 (relpath, project.name))
796 if git_require((2, 7, 0)):
797 project.EnableRepositoryExtension('preciousObjects')
798 else:
799 # This isn't perfect, but it's the best we can do with old git.
800 print('\r%s: WARNING: shared projects are unreliable when using '
801 'old versions of git; please upgrade to git-2.7.0+.'
802 % (relpath,),
803 file=sys.stderr)
804 project.config.SetString('gc.pruneExpire', 'never')
805 else:
806 if not opt.quiet:
807 print(f'\r{relpath}: not shared, disabling pruning.')
808 project.config.SetString('extensions.preciousObjects', None)
809 project.config.SetString('gc.pruneExpire', None)
810
Mike Frysinger5a033082019-09-23 19:21:20 -0400811 def _GCProjects(self, projects, opt, err_event):
LaMont Jones7efab532022-09-01 15:41:12 +0000812 """Perform garbage collection.
813
814 If We are skipping garbage collection (opt.auto_gc not set), we still want
815 to potentially mark objects precious, so that `git gc` does not discard
816 shared objects.
817 """
LaMont Jones43549d82022-11-30 19:55:30 +0000818 if not opt.auto_gc:
819 # Just repair preciousObjects state, and return.
820 for project in projects:
821 self._SetPreciousObjectsState(project, opt)
822 return
823
824 pm = Progress('Garbage collecting', len(projects), delay=False,
825 quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400826 pm.update(inc=0, msg='prescan')
827
Allen Webb4ee4a452021-10-07 10:42:38 -0500828 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700829 for project in projects:
LaMont Jones43549d82022-11-30 19:55:30 +0000830 self._SetPreciousObjectsState(project, opt)
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000831
Allen Webb669efd02021-10-01 15:25:31 -0500832 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500833 # Only call git gc once per objdir, but call pack-refs for the remainder.
834 if project.objdir not in tidy_dirs:
835 tidy_dirs[project.objdir] = (
836 True, # Run a full gc.
837 project.bare_git,
838 )
839 elif project.gitdir not in tidy_dirs:
840 tidy_dirs[project.gitdir] = (
841 False, # Do not run a full gc; just run pack-refs.
842 project.bare_git,
843 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400844
Mike Frysinger355f4392022-07-20 17:15:29 -0400845 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700846
847 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500848 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400849 pm.update(msg=bare_git._project.name)
LaMont Jones891e8f72022-09-08 20:17:58 +0000850
Allen Webb4ee4a452021-10-07 10:42:38 -0500851 if run_gc:
LaMont Jones55b71252022-12-01 21:17:15 +0000852 bare_git.gc('--auto')
Allen Webb4ee4a452021-10-07 10:42:38 -0500853 else:
LaMont Jones55b71252022-12-01 21:17:15 +0000854 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400855 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700856 return
857
Mike Frysinger355f4392022-07-20 17:15:29 -0400858 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400859 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700860
861 threads = set()
862 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700863
Allen Webb4ee4a452021-10-07 10:42:38 -0500864 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400865 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700866 try:
867 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500868 if run_gc:
LaMont Jones55b71252022-12-01 21:17:15 +0000869 bare_git.gc('--auto', config=config)
Allen Webb4ee4a452021-10-07 10:42:38 -0500870 else:
LaMont Jones55b71252022-12-01 21:17:15 +0000871 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700872 except GitError:
873 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900874 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700875 err_event.set()
876 raise
877 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400878 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700879 sem.release()
880
Allen Webb4ee4a452021-10-07 10:42:38 -0500881 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500882 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700883 break
884 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500885 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700886 t.daemon = True
887 threads.add(t)
888 t.start()
889
890 for t in threads:
891 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400892 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700893
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000894 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700895 """Reload the manfiest from the file specified by the |manifest_name|.
896
897 It unloads the manifest if |manifest_name| is None.
898
899 Args:
900 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000901 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700902 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800903 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000904 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000905 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800906 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000907 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800908
LaMont Jonesa46047a2022-04-07 21:57:06 +0000909 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000910 """Update the cached projects list for |manifest|
911
912 In a multi-manifest checkout, each manifest has its own project.list.
913
914 Args:
915 opt: Program options returned from optparse. See _Options().
916 manifest: The manifest to use.
917
918 Returns:
919 0: success
920 1: failure
921 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700922 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000923 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
924 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700925 if project.relpath:
926 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700927 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000928 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700929 old_project_paths = []
930
931 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500932 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700933 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800934 # In reversed order, so subfolders are deleted before parent folder.
935 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700936 if not path:
937 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700938 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900939 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000940 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700941 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900942 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000943 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900944 name=path,
945 remote=RemoteSpec('origin'),
946 gitdir=gitdir,
947 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500948 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000949 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900950 relpath=path,
951 revisionExpr='HEAD',
952 revisionId=None,
953 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500954 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900955 quiet=opt.quiet,
956 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400957 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700958
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700959 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500960 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700961 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700962 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700963 return 0
964
LaMont Jonesa46047a2022-04-07 21:57:06 +0000965 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800966 """Save all dests of copyfile and linkfile, and update them if needed.
967
968 Returns:
969 Whether update was successful.
970 """
971 new_paths = {}
972 new_linkfile_paths = []
973 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000974 for project in self.GetProjects(None, missing_ok=True,
975 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +0800976 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
977 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
978
979 new_paths = {
980 'linkfile': new_linkfile_paths,
981 'copyfile': new_copyfile_paths,
982 }
983
984 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000985 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800986 old_copylinkfile_paths = {}
987
988 if os.path.exists(copylinkfile_path):
989 with open(copylinkfile_path, 'rb') as fp:
990 try:
991 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800992 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800993 print('error: %s is not a json formatted file.' %
994 copylinkfile_path, file=sys.stderr)
995 platform_utils.remove(copylinkfile_path)
996 return False
997
998 need_remove_files = []
999 need_remove_files.extend(
1000 set(old_copylinkfile_paths.get('linkfile', [])) -
1001 set(new_linkfile_paths))
1002 need_remove_files.extend(
1003 set(old_copylinkfile_paths.get('copyfile', [])) -
1004 set(new_copyfile_paths))
1005
1006 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -04001007 # Try to remove the updated copyfile or linkfile.
1008 # So, if the file is not exist, nothing need to do.
1009 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +08001010
1011 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
1012 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
1013 json.dump(new_paths, fp)
1014 return True
1015
LaMont Jonesa46047a2022-04-07 21:57:06 +00001016 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1017 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001018 print('error: cannot smart sync: no manifest server defined in '
1019 'manifest', file=sys.stderr)
1020 sys.exit(1)
1021
LaMont Jonesa46047a2022-04-07 21:57:06 +00001022 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001023 if not opt.quiet:
1024 print('Using manifest server %s' % manifest_server)
1025
David Pursehouseeeff3532020-02-12 11:24:10 +09001026 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001027 username = None
1028 password = None
1029 if opt.manifest_server_username and opt.manifest_server_password:
1030 username = opt.manifest_server_username
1031 password = opt.manifest_server_password
1032 else:
1033 try:
1034 info = netrc.netrc()
1035 except IOError:
1036 # .netrc file does not exist or could not be opened
1037 pass
1038 else:
1039 try:
1040 parse_result = urllib.parse.urlparse(manifest_server)
1041 if parse_result.hostname:
1042 auth = info.authenticators(parse_result.hostname)
1043 if auth:
1044 username, _account, password = auth
1045 else:
1046 print('No credentials found for %s in .netrc'
1047 % parse_result.hostname, file=sys.stderr)
1048 except netrc.NetrcParseError as e:
1049 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
1050
1051 if (username and password):
1052 manifest_server = manifest_server.replace('://', '://%s:%s@' %
1053 (username, password),
1054 1)
1055
1056 transport = PersistentTransport(manifest_server)
1057 if manifest_server.startswith('persistent-'):
1058 manifest_server = manifest_server[len('persistent-'):]
1059
1060 try:
1061 server = xmlrpc.client.Server(manifest_server, transport=transport)
1062 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001063 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001064
Mike Frysinger56ce3462019-12-04 19:30:48 -05001065 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -05001066 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001067 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -05001068 elif ('TARGET_PRODUCT' in os.environ and
1069 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -05001070 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
1071 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001072 [success, manifest_str] = server.GetApprovedManifest(branch, target)
1073 else:
1074 [success, manifest_str] = server.GetApprovedManifest(branch)
1075 else:
1076 assert(opt.smart_tag)
1077 [success, manifest_str] = server.GetManifest(opt.smart_tag)
1078
1079 if success:
1080 manifest_name = os.path.basename(smart_sync_manifest_path)
1081 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001082 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001083 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001084 except IOError as e:
1085 print('error: cannot write manifest to %s:\n%s'
1086 % (smart_sync_manifest_path, e),
1087 file=sys.stderr)
1088 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001089 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001090 else:
1091 print('error: manifest server RPC call failed: %s' %
1092 manifest_str, file=sys.stderr)
1093 sys.exit(1)
1094 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1095 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001096 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001097 sys.exit(1)
1098 except xmlrpc.client.ProtocolError as e:
1099 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001100 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001101 file=sys.stderr)
1102 sys.exit(1)
1103
1104 return manifest_name
1105
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001106 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
1107 """Fetch & update the local manifest project.
1108
1109 After syncing the manifest project, if the manifest has any sub manifests,
1110 those are recursively processed.
1111
1112 Args:
1113 opt: Program options returned from optparse. See _Options().
1114 mp: the manifestProject to query.
1115 manifest_name: Manifest file to be reloaded.
1116 """
1117 if not mp.standalone_manifest_url:
1118 self._UpdateManifestProject(opt, mp, manifest_name)
1119
1120 if mp.manifest.submanifests:
1121 for submanifest in mp.manifest.submanifests.values():
1122 child = submanifest.repo_client.manifest
1123 child.manifestProject.SyncWithPossibleInit(
1124 submanifest,
1125 current_branch_only=self._GetCurrentBranchOnly(opt, child),
1126 verbose=opt.verbose,
1127 tags=opt.tags,
1128 git_event_log=self.git_event_log,
1129 )
1130 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
1131
Mike Frysingerfb527e32019-08-27 02:34:32 -04001132 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001133 """Fetch & update the local manifest project.
1134
1135 Args:
1136 opt: Program options returned from optparse. See _Options().
1137 mp: the manifestProject to query.
1138 manifest_name: Manifest file to be reloaded.
1139 """
Mike Frysingerfb527e32019-08-27 02:34:32 -04001140 if not opt.local_only:
1141 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -05001142 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001143 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +02001144 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001145 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -04001146 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001147 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001148 submodules=mp.manifest.HasSubmodules,
1149 clone_filter=mp.manifest.CloneFilter,
1150 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001151 finish = time.time()
1152 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1153 start, finish, success)
1154
1155 if mp.HasChanges:
1156 syncbuf = SyncBuffer(mp.config)
1157 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001158 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001159 clean = syncbuf.Finish()
1160 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1161 start, time.time(), clean)
1162 if not clean:
1163 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001164 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001165
Mike Frysingerae6cb082019-08-27 01:10:59 -04001166 def ValidateOptions(self, opt, args):
1167 if opt.force_broken:
1168 print('warning: -f/--force-broken is now the default behavior, and the '
1169 'options are deprecated', file=sys.stderr)
1170 if opt.network_only and opt.detach_head:
1171 self.OptionParser.error('cannot combine -n and -d')
1172 if opt.network_only and opt.local_only:
1173 self.OptionParser.error('cannot combine -n and -l')
1174 if opt.manifest_name and opt.smart_sync:
1175 self.OptionParser.error('cannot combine -m and -s')
1176 if opt.manifest_name and opt.smart_tag:
1177 self.OptionParser.error('cannot combine -m and -t')
1178 if opt.manifest_server_username or opt.manifest_server_password:
1179 if not (opt.smart_sync or opt.smart_tag):
1180 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1181 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1182 self.OptionParser.error('both -u and -p must be given')
1183
Mike Frysinger0531a622021-11-05 15:22:01 -04001184 if opt.prune is None:
1185 opt.prune = True
1186
LaMont Jones5ed8c632022-11-10 00:10:44 +00001187 if opt.auto_gc is None and _AUTO_GC:
1188 print(f"Will run `git gc --auto` because {_REPO_AUTO_GC} is set.",
1189 file=sys.stderr)
1190 opt.auto_gc = True
LaMont Jonesd7935532022-12-01 20:18:46 +00001191 print(f'{_REPO_AUTO_GC} is deprecated and will be removed in a future'
1192 'release. Use `--auto-gc` instead.', file=sys.stderr)
LaMont Jones5ed8c632022-11-10 00:10:44 +00001193
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001194 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001195 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001196 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001197 manifest = self.manifest
1198
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001199 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001200 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001201
Chirayu Desaia892b102013-06-11 14:18:46 +05301202 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001203 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001204 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301205
Xin Lid79a4bc2020-05-20 16:03:45 -07001206 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001207 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001208
Victor Boivie08c880d2011-04-19 10:32:52 +02001209 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001210 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001211 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001212 if os.path.isfile(smart_sync_manifest_path):
1213 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001214 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001215 except OSError as e:
1216 print('error: failed to remove existing smart sync override manifest: %s' %
1217 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001218
Mike Frysingerc99322a2021-05-04 15:32:43 -04001219 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001220
LaMont Jonesa46047a2022-04-07 21:57:06 +00001221 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001222 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001223 cb = rp.CurrentBranch
1224 if cb:
1225 base = rp.GetBranch(cb).merge
1226 if not base or not base.startswith('refs/heads/'):
1227 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001228 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001229 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001230
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001231 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001232 if not m.manifestProject.standalone_manifest_url:
1233 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001234
LaMont Jones4112c072022-08-24 17:32:25 +00001235 if opt.repo_upgraded:
1236 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001237
LaMont Jones4112c072022-08-24 17:32:25 +00001238 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001239 if opt.mp_update:
1240 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1241 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001242 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001243
Mike Frysinger355f4392022-07-20 17:15:29 -04001244 # Now that the manifests are up-to-date, setup the jobs value.
1245 if opt.jobs is None:
1246 # User has not made a choice, so use the manifest settings.
1247 opt.jobs = mp.default.sync_j
1248 if opt.jobs is not None:
1249 # Neither user nor manifest have made a choice.
1250 if opt.jobs_network is None:
1251 opt.jobs_network = opt.jobs
1252 if opt.jobs_checkout is None:
1253 opt.jobs_checkout = opt.jobs
1254 # Setup defaults if jobs==0.
1255 if not opt.jobs:
1256 if not opt.jobs_network:
1257 opt.jobs_network = 1
1258 if not opt.jobs_checkout:
1259 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1260 opt.jobs = os.cpu_count()
1261
1262 # Try to stay under user rlimit settings.
1263 #
1264 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1265 # that to scale down the number of jobs. Unfortunately there isn't an easy
1266 # way to determine this reliably as systems change, but it was last measured
1267 # by hand in 2011.
1268 soft_limit, _ = _rlimit_nofile()
1269 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1270 opt.jobs = min(opt.jobs, jobs_soft_limit)
1271 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1272 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1273
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001274 superproject_logging_data = {}
1275 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1276 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001277
Simran Basib9a1b732015-08-20 12:19:28 -07001278 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001279 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001280 gitc_projects = []
1281 opened_projects = []
1282 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001283 if project.relpath in self.gitc_manifest.paths and \
1284 self.gitc_manifest.paths[project.relpath].old_revision:
1285 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001286 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001287 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001288
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001289 if not args:
1290 gitc_projects = None
1291
1292 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001293 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001294 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1295 if manifest_name:
1296 manifest.Override(manifest_name)
1297 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001298 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001299 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1300 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001301 gitc_projects)
1302 print('GITC client successfully synced.')
1303
1304 # The opened projects need to be synced as normal, therefore we
1305 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001306 # TODO: make this more reliable -- if there's a project name/path overlap,
1307 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001308 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001309 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001310 if not args:
1311 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001312
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001313 all_projects = self.GetProjects(args,
1314 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001315 submodules_ok=opt.fetch_submodules,
1316 manifest=manifest,
1317 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001318
Mike Frysinger5a033082019-09-23 19:21:20 -04001319 err_network_sync = False
1320 err_update_projects = False
LaMont Jonesb6cfa092022-10-26 16:34:40 +00001321 err_update_linkfiles = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001322
LaMont Jonesa46047a2022-04-07 21:57:06 +00001323 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001324 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001325 with multiprocessing.Manager() as manager:
1326 with ssh.ProxyManager(manager) as ssh_proxy:
1327 # Initialize the socket dir once in the parent.
1328 ssh_proxy.sock()
LaMont Jones1eddca82022-09-01 15:15:04 +00001329 result = self._FetchMain(opt, args, all_projects, err_event,
1330 ssh_proxy, manifest)
1331 all_projects = result.all_projects
Mike Frysinger339f2df2021-05-06 00:44:42 -04001332
1333 if opt.network_only:
1334 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001335
1336 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001337 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001338 err_network_sync = True
1339 if opt.fail_fast:
1340 print('\nerror: Exited sync due to fetch errors.\n'
1341 'Local checkouts *not* updated. Resolve network issues & '
1342 'retry.\n'
1343 '`repo sync -l` will update some local checkouts.',
1344 file=sys.stderr)
1345 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001346
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001347 for m in self.ManifestList(opt):
1348 if m.IsMirror or m.IsArchive:
1349 # bail out now, we have no working tree
1350 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001351
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001352 if self.UpdateProjectList(opt, m):
1353 err_event.set()
1354 err_update_projects = True
1355 if opt.fail_fast:
1356 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1357 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001358
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001359 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1360 if err_update_linkfiles:
1361 err_event.set()
1362 if opt.fail_fast:
1363 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1364 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001365
Mike Frysinger5a033082019-09-23 19:21:20 -04001366 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001367 # NB: We don't exit here because this is the last step.
1368 err_checkout = not self._Checkout(all_projects, opt, err_results)
1369 if err_checkout:
1370 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001371
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001372 printed_notices = set()
1373 # If there's a notice that's supposed to print at the end of the sync,
1374 # print it now... But avoid printing duplicate messages, and preserve
1375 # order.
1376 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1377 if m.notice and m.notice not in printed_notices:
1378 print(m.notice)
1379 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001380
Mike Frysinger5a033082019-09-23 19:21:20 -04001381 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001382 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001383 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1384 if err_network_sync:
1385 print('error: Downloading network changes failed.', file=sys.stderr)
1386 if err_update_projects:
1387 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001388 if err_update_linkfiles:
1389 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001390 if err_checkout:
1391 print('error: Checking out local projects failed.', file=sys.stderr)
1392 if err_results:
1393 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1394 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1395 file=sys.stderr)
1396 sys.exit(1)
1397
Raman Tenneti7954de12021-07-28 14:36:49 -07001398 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001399 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1400 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001401
1402 # Update and log with the new sync analysis state.
1403 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001404 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1405 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001406
Mike Frysingere19d9e12020-02-12 11:23:32 -05001407 if not opt.quiet:
1408 print('repo sync has finished successfully.')
1409
David Pursehouse819827a2020-02-12 15:20:19 +09001410
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001411def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001412 # Link the docs for the internal .repo/ layout for people
1413 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1414 if not platform_utils.islink(link):
1415 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1416 try:
1417 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001418 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001419 pass
1420
Conley Owens094cdbe2014-01-30 15:09:59 -08001421 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001422 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001423 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001424 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001425 if project.Exists:
1426 project.PostRepoUpgrade()
1427
David Pursehouse819827a2020-02-12 15:20:19 +09001428
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001429def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001430 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001431 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001432 wrapper = Wrapper()
1433 try:
1434 rev = rp.bare_git.describe(rp.GetRevisionId())
1435 except GitError:
1436 rev = None
1437 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1438 # See if we're held back due to missing signed tag.
1439 current_revid = rp.bare_git.rev_parse('HEAD')
1440 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1441 if current_revid != new_revid:
1442 # We want to switch to the new rev, but also not trash any uncommitted
1443 # changes. This helps with local testing/hacking.
1444 # If a local change has been made, we will throw that away.
1445 # We also have to make sure this will switch to an older commit if that's
1446 # the latest tag in order to support release rollback.
1447 try:
1448 rp.work_git.reset('--keep', new_rev)
1449 except GitError as e:
1450 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001451 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001452 raise RepoChangedException(['--repo-upgraded'])
1453 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001454 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001455 else:
1456 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001457 print('repo version %s is current' % rp.work_git.describe(HEAD),
1458 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001459
David Pursehouse819827a2020-02-12 15:20:19 +09001460
Dave Borowitz67700e92012-10-23 15:00:54 -07001461class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001462 _ALPHA = 0.5
1463
Dave Borowitz67700e92012-10-23 15:00:54 -07001464 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001465 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001466 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001467 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001468
1469 def Get(self, project):
1470 self._Load()
1471 return self._times.get(project.name, _ONE_DAY_S)
1472
1473 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001474 self._Load()
1475 name = project.name
1476 old = self._times.get(name, t)
1477 self._seen.add(name)
1478 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001479 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001480
1481 def _Load(self):
1482 if self._times is None:
1483 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001484 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001485 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001486 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001487 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001488 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001489
1490 def Save(self):
1491 if self._times is None:
1492 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001493
1494 to_delete = []
1495 for name in self._times:
1496 if name not in self._seen:
1497 to_delete.append(name)
1498 for name in to_delete:
1499 del self._times[name]
1500
Dave Borowitz67700e92012-10-23 15:00:54 -07001501 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001502 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001503 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001504 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001505 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001506
1507# This is a replacement for xmlrpc.client.Transport using urllib2
1508# and supporting persistent-http[s]. It cannot change hosts from
1509# request to request like the normal transport, the real url
1510# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001511
1512
Dan Willemsen0745bb22015-08-17 13:41:45 -07001513class PersistentTransport(xmlrpc.client.Transport):
1514 def __init__(self, orig_host):
1515 self.orig_host = orig_host
1516
1517 def request(self, host, handler, request_body, verbose=False):
1518 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1519 # Python doesn't understand cookies with the #HttpOnly_ prefix
1520 # Since we're only using them for HTTP, copy the file temporarily,
1521 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001522 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001523 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001524 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001525 try:
1526 with open(cookiefile) as f:
1527 for line in f:
1528 if line.startswith("#HttpOnly_"):
1529 line = line[len("#HttpOnly_"):]
1530 tmpcookiefile.write(line)
1531 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001532
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001533 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001534 try:
1535 cookiejar.load()
1536 except cookielib.LoadError:
1537 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001538 finally:
1539 tmpcookiefile.close()
1540 else:
1541 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001542
1543 proxyhandler = urllib.request.ProxyHandler
1544 if proxy:
1545 proxyhandler = urllib.request.ProxyHandler({
1546 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001547 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001548
1549 opener = urllib.request.build_opener(
1550 urllib.request.HTTPCookieProcessor(cookiejar),
1551 proxyhandler)
1552
1553 url = urllib.parse.urljoin(self.orig_host, handler)
1554 parse_results = urllib.parse.urlparse(url)
1555
1556 scheme = parse_results.scheme
1557 if scheme == 'persistent-http':
1558 scheme = 'http'
1559 if scheme == 'persistent-https':
1560 # If we're proxying through persistent-https, use http. The
1561 # proxy itself will do the https.
1562 if proxy:
1563 scheme = 'http'
1564 else:
1565 scheme = 'https'
1566
1567 # Parse out any authentication information using the base class
1568 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1569
1570 url = urllib.parse.urlunparse((
1571 scheme,
1572 host,
1573 parse_results.path,
1574 parse_results.params,
1575 parse_results.query,
1576 parse_results.fragment))
1577
1578 request = urllib.request.Request(url, request_body)
1579 if extra_headers is not None:
1580 for (name, header) in extra_headers:
1581 request.add_header(name, header)
1582 request.add_header('Content-Type', 'text/xml')
1583 try:
1584 response = opener.open(request)
1585 except urllib.error.HTTPError as e:
1586 if e.code == 501:
1587 # We may have been redirected through a login process
1588 # but our POST turned into a GET. Retry.
1589 response = opener.open(request)
1590 else:
1591 raise
1592
1593 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001594 # Response should be fairly small, so read it all at once.
1595 # This way we can show it to the user in case of error (e.g. HTML).
1596 data = response.read()
1597 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001598 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001599 except xml.parsers.expat.ExpatError as e:
1600 raise IOError(
1601 f'Parsing the manifest failed: {e}\n'
1602 f'Please report this to your manifest server admin.\n'
1603 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001604 p.close()
1605 return u.close()
1606
1607 def close(self):
1608 pass