blob: e4e7a97158c112c29ff978e13c26a1a28a9a31a7 [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
LaMont Jones100a2142022-12-02 22:54:11 +000070# revert a change in default behavior in v2.29.9. Remove after 2023-04-01.
LaMont Jones5ed8c632022-11-10 00:10:44 +000071_REPO_AUTO_GC = 'REPO_AUTO_GC'
72_AUTO_GC = os.environ.get(_REPO_AUTO_GC) == '1'
73
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 [<project>...]
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
LaMont Jones7efab532022-09-01 15:41:12 +0000203The --auto-gc option can be used to trigger garbage collection on all
204projects. By default, repo does not run garbage collection.
205
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400206# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700207
208If at least one project remote URL uses an SSH connection (ssh://,
209git+ssh://, or user@host:path syntax) repo will automatically
210enable the SSH ControlMaster option when connecting to that host.
211This feature permits other projects in the same '%prog' session to
212reuse the same SSH tunnel, saving connection setup overheads.
213
214To disable this behavior on UNIX platforms, set the GIT_SSH
215environment variable to 'ssh'. For example:
216
217 export GIT_SSH=ssh
218 %prog
219
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400220# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700221
222This feature is automatically disabled on Windows, due to the lack
223of UNIX domain socket support.
224
225This feature is not compatible with url.insteadof rewrites in the
226user's ~/.gitconfig. '%prog' is currently not able to perform the
227rewrite early enough to establish the ControlMaster tunnel.
228
229If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
230later is required to fix a server side protocol bug.
231
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400233 # A value of 0 means we want parallel jobs, but we'll determine the default
234 # value later on.
235 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700236
Mike Frysinger9180a072021-04-13 14:57:40 -0400237 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400238 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400239 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400240 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400241 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
242 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400243
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500244 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200245 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400246 help='obsolete option (to be deleted in the future)')
247 p.add_option('--fail-fast',
248 dest='fail_fast', action='store_true',
249 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700250 p.add_option('--force-sync',
251 dest='force_sync', action='store_true',
252 help="overwrite an existing git directory if it needs to "
253 "point to a different object directory. WARNING: this "
254 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500255 p.add_option('--force-remove-dirty',
256 dest='force_remove_dirty', action='store_true',
257 help="force remove projects with uncommitted modifications if "
258 "projects no longer exist in the manifest. "
259 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900260 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700261 dest='local_only', action='store_true',
262 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900263 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100264 dest='mp_update', action='store_false', default='true',
265 help='use the existing manifest checkout as-is. '
266 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900267 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700268 dest='network_only', action='store_true',
269 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900270 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700271 dest='detach_head', action='store_true',
272 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900273 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700274 dest='current_branch_only', action='store_true',
275 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400276 p.add_option('--no-current-branch',
277 dest='current_branch_only', action='store_false',
278 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500279 p.add_option('-m', '--manifest-name',
280 dest='manifest_name',
281 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700282 p.add_option('--clone-bundle', action='store_true',
283 help='enable use of /clone.bundle on HTTP/HTTPS')
284 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700285 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800286 p.add_option('-u', '--manifest-server-username', action='store',
287 dest='manifest_server_username',
288 help='username to authenticate with the manifest server')
289 p.add_option('-p', '--manifest-server-password', action='store',
290 dest='manifest_server_password',
291 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800292 p.add_option('--fetch-submodules',
293 dest='fetch_submodules', action='store_true',
294 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800295 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700296 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700297 p.add_option('--no-use-superproject', action='store_false',
298 dest='use_superproject',
299 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400300 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400301 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700302 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400303 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400304 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900305 p.add_option('--optimized-fetch',
306 dest='optimized_fetch', action='store_true',
307 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600308 p.add_option('--retry-fetches',
309 default=0, action='store', type='int',
310 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400311 p.add_option('--prune', action='store_true',
312 help='delete refs that no longer exist on the remote (default)')
313 p.add_option('--no-prune', dest='prune', action='store_false',
314 help='do not delete refs that no longer exist on the remote')
LaMont Jones5ed8c632022-11-10 00:10:44 +0000315 p.add_option('--auto-gc', action='store_true', default=None,
LaMont Jones7efab532022-09-01 15:41:12 +0000316 help='run garbage collection on all synced projects')
317 p.add_option('--no-auto-gc', dest='auto_gc', action='store_false',
318 help='do not run garbage collection on any projects (default)')
Nico Sallembien6623b212010-05-11 12:57:01 -0700319 if show_smart:
320 p.add_option('-s', '--smart-sync',
321 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900322 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200323 p.add_option('-t', '--smart-tag',
324 dest='smart_tag', action='store',
325 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700326
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700327 g = p.add_option_group('repo Version options')
328 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500329 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700330 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700331 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800332 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700333 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700334
LaMont Jonesa46047a2022-04-07 21:57:06 +0000335 def _GetBranch(self, manifest_project):
336 """Returns the branch name for getting the approved smartsync manifest.
337
338 Args:
339 manifest_project: the manifestProject to query.
340 """
341 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800342 branch = b.merge
343 if branch.startswith(R_HEADS):
344 branch = branch[len(R_HEADS):]
345 return branch
346
LaMont Jonesa46047a2022-04-07 21:57:06 +0000347 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200348 """Returns whether current-branch or use-superproject options are enabled.
349
LaMont Jonesa46047a2022-04-07 21:57:06 +0000350 Args:
351 opt: Program options returned from optparse. See _Options().
352 manifest: The manifest to use.
353
Daniel Anderssond52ca422022-04-01 12:55:38 +0200354 Returns:
355 True if a superproject is requested, otherwise the value of the
356 current_branch option (True, False or None).
357 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000358 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700359
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000360 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
361 manifest):
362 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800363
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000364 This function updates each project's revisionId with the commit hash from
365 the superproject. It writes the updated manifest into a file and reloads
366 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800367
368 Args:
369 opt: Program options returned from optparse. See _Options().
370 args: Arguments to pass to GetProjects. See the GetProjects
371 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000372 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000373 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800374 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000375 have_superproject = manifest.superproject or any(
376 m.superproject for m in manifest.all_children)
377 if not have_superproject:
378 return
379
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000380 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000381 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700382 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000383 self._ReloadManifest(manifest_path, manifest)
384 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700385
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800386 all_projects = self.GetProjects(args,
387 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000388 submodules_ok=opt.fetch_submodules,
389 manifest=manifest,
390 all_manifests=not opt.this_manifest_only)
391
392 per_manifest = collections.defaultdict(list)
393 manifest_paths = {}
394 if opt.this_manifest_only:
395 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700396 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000397 for p in all_projects:
398 per_manifest[p.manifest.path_prefix].append(p)
399
400 superproject_logging_data = {}
401 need_unload = False
402 for m in self.ManifestList(opt):
403 if not m.path_prefix in per_manifest:
404 continue
405 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
406 if superproject_logging_data:
407 superproject_logging_data['multimanifest'] = True
408 superproject_logging_data.update(
409 superproject=use_super,
410 haslocalmanifests=bool(m.HasLocalManifests),
411 hassuperprojecttag=bool(m.superproject),
412 )
413 if use_super and (m.IsMirror or m.IsArchive):
414 # Don't use superproject, because we have no working tree.
415 use_super = False
416 superproject_logging_data['superproject'] = False
417 superproject_logging_data['noworktree'] = True
418 if opt.use_superproject is not False:
419 print(f'{m.path_prefix}: not using superproject because there is no '
420 'working tree.')
421
422 if not use_super:
423 continue
424 m.superproject.SetQuiet(opt.quiet)
425 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
426 m.superproject.SetPrintMessages(print_messages)
427 update_result = m.superproject.UpdateProjectsRevisionId(
428 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
429 manifest_path = update_result.manifest_path
430 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
431 if manifest_path:
432 m.SetManifestOverride(manifest_path)
433 need_unload = True
434 else:
435 if print_messages:
436 print(f'{m.path_prefix}: warning: Update of revisionId from '
437 'superproject has failed, repo sync will not use superproject '
438 'to fetch the source. ',
439 'Please resync with the --no-use-superproject option to avoid '
440 'this repo warning.',
441 file=sys.stderr)
442 if update_result.fatal and opt.use_superproject is not None:
443 sys.exit(1)
444 if need_unload:
445 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800446
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500447 def _FetchProjectList(self, opt, projects):
448 """Main function of the fetch worker.
449
450 The projects we're given share the same underlying git object store, so we
451 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800452
David James8d201162013-10-11 17:03:19 -0700453 Delegates most of the work to _FetchHelper.
454
455 Args:
456 opt: Program options returned from optparse. See _Options().
457 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700458 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500459 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700460
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500461 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700462 """Fetch git objects for a single project.
463
David Pursehousec1b86a22012-11-14 11:36:51 +0900464 Args:
465 opt: Program options returned from optparse. See _Options().
466 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700467
468 Returns:
469 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900470 """
David Rileye0684ad2017-04-05 00:02:59 -0700471 start = time.time()
472 success = False
Karsten Tausche802cd0c2022-12-06 09:56:28 +0100473 remote_fetched = 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
Karsten Tausche802cd0c2022-12-06 09:56:28 +0100491 remote_fetched = sync_result.remote_fetched
Doug Andersonfc06ced2011-03-16 15:49:18 -0700492
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500493 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400494 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500495 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700496
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500497 if not success:
498 print('error: Cannot fetch %s from %s'
499 % (project.name, project.remote.url),
500 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700501 except GitError as e:
502 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500503 except Exception as e:
504 print('error: Cannot fetch %s (%s: %s)'
505 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
506 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500507
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500508 finish = time.time()
Karsten Tausche802cd0c2022-12-06 09:56:28 +0100509 return _FetchOneResult(success, project, start, finish, 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.",
LaMont Jones100a2142022-12-02 22:54:11 +00001189 f'{_REPO_AUTO_GC} is deprecated and will be removed in a future',
LaMont Jonesd7935532022-12-01 20:18:46 +00001190 'release. Use `--auto-gc` instead.', file=sys.stderr)
LaMont Jones100a2142022-12-02 22:54:11 +00001191 opt.auto_gc = True
LaMont Jones5ed8c632022-11-10 00:10:44 +00001192
Mike Frysingerf159ce02022-12-07 09:41:47 -05001193 def _ValidateOptionsWithManifest(self, opt, mp):
1194 """Like ValidateOptions, but after we've updated the manifest.
1195
1196 Needed to handle sync-xxx option defaults in the manifest.
1197
1198 Args:
1199 opt: The options to process.
1200 mp: The manifest project to pull defaults from.
1201 """
1202 if not opt.jobs:
1203 # If the user hasn't made a choice, use the manifest value.
1204 opt.jobs = mp.manifest.default.sync_j
1205 if opt.jobs:
1206 # If --jobs has a non-default value, propagate it as the default for
1207 # --jobs-xxx flags too.
1208 if not opt.jobs_network:
1209 opt.jobs_network = opt.jobs
1210 if not opt.jobs_checkout:
1211 opt.jobs_checkout = opt.jobs
1212 else:
1213 # Neither user nor manifest have made a choice, so setup defaults.
1214 if not opt.jobs_network:
1215 opt.jobs_network = 1
1216 if not opt.jobs_checkout:
1217 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1218 opt.jobs = os.cpu_count()
1219
1220 # Try to stay under user rlimit settings.
1221 #
1222 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1223 # that to scale down the number of jobs. Unfortunately there isn't an easy
1224 # way to determine this reliably as systems change, but it was last measured
1225 # by hand in 2011.
1226 soft_limit, _ = _rlimit_nofile()
1227 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1228 opt.jobs = min(opt.jobs, jobs_soft_limit)
1229 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1230 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1231
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001232 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001233 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001234 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001235 manifest = self.manifest
1236
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001237 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001238 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001239
Chirayu Desaia892b102013-06-11 14:18:46 +05301240 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001241 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001242 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301243
Xin Lid79a4bc2020-05-20 16:03:45 -07001244 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001245 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001246
Victor Boivie08c880d2011-04-19 10:32:52 +02001247 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001248 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001249 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001250 if os.path.isfile(smart_sync_manifest_path):
1251 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001252 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001253 except OSError as e:
1254 print('error: failed to remove existing smart sync override manifest: %s' %
1255 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001256
Mike Frysingerc99322a2021-05-04 15:32:43 -04001257 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001258
LaMont Jonesa46047a2022-04-07 21:57:06 +00001259 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001260 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001261 cb = rp.CurrentBranch
1262 if cb:
1263 base = rp.GetBranch(cb).merge
1264 if not base or not base.startswith('refs/heads/'):
1265 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001266 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001267 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001268
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001269 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001270 if not m.manifestProject.standalone_manifest_url:
1271 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001272
LaMont Jones4112c072022-08-24 17:32:25 +00001273 if opt.repo_upgraded:
1274 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001275
LaMont Jones4112c072022-08-24 17:32:25 +00001276 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001277 if opt.mp_update:
1278 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1279 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001280 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001281
Mike Frysingerf159ce02022-12-07 09:41:47 -05001282 # Now that the manifests are up-to-date, setup options whose defaults might
1283 # be in the manifest.
1284 self._ValidateOptionsWithManifest(opt, mp)
Mike Frysinger355f4392022-07-20 17:15:29 -04001285
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001286 superproject_logging_data = {}
1287 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1288 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001289
Simran Basib9a1b732015-08-20 12:19:28 -07001290 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001291 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001292 gitc_projects = []
1293 opened_projects = []
1294 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001295 if project.relpath in self.gitc_manifest.paths and \
1296 self.gitc_manifest.paths[project.relpath].old_revision:
1297 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001298 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001299 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001300
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001301 if not args:
1302 gitc_projects = None
1303
1304 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001305 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001306 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1307 if manifest_name:
1308 manifest.Override(manifest_name)
1309 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001310 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001311 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1312 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001313 gitc_projects)
1314 print('GITC client successfully synced.')
1315
1316 # The opened projects need to be synced as normal, therefore we
1317 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001318 # TODO: make this more reliable -- if there's a project name/path overlap,
1319 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001320 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001321 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001322 if not args:
1323 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001324
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001325 all_projects = self.GetProjects(args,
1326 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001327 submodules_ok=opt.fetch_submodules,
1328 manifest=manifest,
1329 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001330
Mike Frysinger5a033082019-09-23 19:21:20 -04001331 err_network_sync = False
1332 err_update_projects = False
LaMont Jonesb6cfa092022-10-26 16:34:40 +00001333 err_update_linkfiles = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001334
LaMont Jonesa46047a2022-04-07 21:57:06 +00001335 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001336 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001337 with multiprocessing.Manager() as manager:
1338 with ssh.ProxyManager(manager) as ssh_proxy:
1339 # Initialize the socket dir once in the parent.
1340 ssh_proxy.sock()
LaMont Jones1eddca82022-09-01 15:15:04 +00001341 result = self._FetchMain(opt, args, all_projects, err_event,
1342 ssh_proxy, manifest)
1343 all_projects = result.all_projects
Mike Frysinger339f2df2021-05-06 00:44:42 -04001344
1345 if opt.network_only:
1346 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001347
1348 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001349 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001350 err_network_sync = True
1351 if opt.fail_fast:
1352 print('\nerror: Exited sync due to fetch errors.\n'
1353 'Local checkouts *not* updated. Resolve network issues & '
1354 'retry.\n'
1355 '`repo sync -l` will update some local checkouts.',
1356 file=sys.stderr)
1357 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001358
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001359 for m in self.ManifestList(opt):
1360 if m.IsMirror or m.IsArchive:
1361 # bail out now, we have no working tree
1362 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001363
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001364 if self.UpdateProjectList(opt, m):
1365 err_event.set()
1366 err_update_projects = True
1367 if opt.fail_fast:
1368 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1369 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001370
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001371 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1372 if err_update_linkfiles:
1373 err_event.set()
1374 if opt.fail_fast:
1375 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1376 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001377
Mike Frysinger5a033082019-09-23 19:21:20 -04001378 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001379 # NB: We don't exit here because this is the last step.
1380 err_checkout = not self._Checkout(all_projects, opt, err_results)
1381 if err_checkout:
1382 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001383
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001384 printed_notices = set()
1385 # If there's a notice that's supposed to print at the end of the sync,
1386 # print it now... But avoid printing duplicate messages, and preserve
1387 # order.
1388 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1389 if m.notice and m.notice not in printed_notices:
1390 print(m.notice)
1391 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001392
Mike Frysinger5a033082019-09-23 19:21:20 -04001393 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001394 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001395 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1396 if err_network_sync:
1397 print('error: Downloading network changes failed.', file=sys.stderr)
1398 if err_update_projects:
1399 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001400 if err_update_linkfiles:
1401 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001402 if err_checkout:
1403 print('error: Checking out local projects failed.', file=sys.stderr)
1404 if err_results:
1405 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1406 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1407 file=sys.stderr)
1408 sys.exit(1)
1409
Raman Tenneti7954de12021-07-28 14:36:49 -07001410 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001411 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1412 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001413
1414 # Update and log with the new sync analysis state.
1415 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001416 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1417 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001418
Mike Frysingere19d9e12020-02-12 11:23:32 -05001419 if not opt.quiet:
1420 print('repo sync has finished successfully.')
1421
David Pursehouse819827a2020-02-12 15:20:19 +09001422
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001423def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001424 # Link the docs for the internal .repo/ layout for people
1425 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1426 if not platform_utils.islink(link):
1427 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1428 try:
1429 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001430 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001431 pass
1432
Conley Owens094cdbe2014-01-30 15:09:59 -08001433 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001434 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001435 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001436 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001437 if project.Exists:
1438 project.PostRepoUpgrade()
1439
David Pursehouse819827a2020-02-12 15:20:19 +09001440
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001441def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001442 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001443 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001444 wrapper = Wrapper()
1445 try:
1446 rev = rp.bare_git.describe(rp.GetRevisionId())
1447 except GitError:
1448 rev = None
1449 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1450 # See if we're held back due to missing signed tag.
1451 current_revid = rp.bare_git.rev_parse('HEAD')
1452 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1453 if current_revid != new_revid:
1454 # We want to switch to the new rev, but also not trash any uncommitted
1455 # changes. This helps with local testing/hacking.
1456 # If a local change has been made, we will throw that away.
1457 # We also have to make sure this will switch to an older commit if that's
1458 # the latest tag in order to support release rollback.
1459 try:
1460 rp.work_git.reset('--keep', new_rev)
1461 except GitError as e:
1462 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001463 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001464 raise RepoChangedException(['--repo-upgraded'])
1465 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001466 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001467 else:
1468 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001469 print('repo version %s is current' % rp.work_git.describe(HEAD),
1470 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001471
David Pursehouse819827a2020-02-12 15:20:19 +09001472
Dave Borowitz67700e92012-10-23 15:00:54 -07001473class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001474 _ALPHA = 0.5
1475
Dave Borowitz67700e92012-10-23 15:00:54 -07001476 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001477 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001478 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001479 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001480
1481 def Get(self, project):
1482 self._Load()
1483 return self._times.get(project.name, _ONE_DAY_S)
1484
1485 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001486 self._Load()
1487 name = project.name
1488 old = self._times.get(name, t)
1489 self._seen.add(name)
1490 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001491 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001492
1493 def _Load(self):
1494 if self._times is None:
1495 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001496 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001497 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001498 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001499 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001500 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001501
1502 def Save(self):
1503 if self._times is None:
1504 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001505
1506 to_delete = []
1507 for name in self._times:
1508 if name not in self._seen:
1509 to_delete.append(name)
1510 for name in to_delete:
1511 del self._times[name]
1512
Dave Borowitz67700e92012-10-23 15:00:54 -07001513 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001514 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001515 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001516 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001517 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001518
1519# This is a replacement for xmlrpc.client.Transport using urllib2
1520# and supporting persistent-http[s]. It cannot change hosts from
1521# request to request like the normal transport, the real url
1522# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001523
1524
Dan Willemsen0745bb22015-08-17 13:41:45 -07001525class PersistentTransport(xmlrpc.client.Transport):
1526 def __init__(self, orig_host):
1527 self.orig_host = orig_host
1528
1529 def request(self, host, handler, request_body, verbose=False):
1530 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1531 # Python doesn't understand cookies with the #HttpOnly_ prefix
1532 # Since we're only using them for HTTP, copy the file temporarily,
1533 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001534 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001535 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001536 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001537 try:
1538 with open(cookiefile) as f:
1539 for line in f:
1540 if line.startswith("#HttpOnly_"):
1541 line = line[len("#HttpOnly_"):]
1542 tmpcookiefile.write(line)
1543 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001544
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001545 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001546 try:
1547 cookiejar.load()
1548 except cookielib.LoadError:
1549 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001550 finally:
1551 tmpcookiefile.close()
1552 else:
1553 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001554
1555 proxyhandler = urllib.request.ProxyHandler
1556 if proxy:
1557 proxyhandler = urllib.request.ProxyHandler({
1558 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001559 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001560
1561 opener = urllib.request.build_opener(
1562 urllib.request.HTTPCookieProcessor(cookiejar),
1563 proxyhandler)
1564
1565 url = urllib.parse.urljoin(self.orig_host, handler)
1566 parse_results = urllib.parse.urlparse(url)
1567
1568 scheme = parse_results.scheme
1569 if scheme == 'persistent-http':
1570 scheme = 'http'
1571 if scheme == 'persistent-https':
1572 # If we're proxying through persistent-https, use http. The
1573 # proxy itself will do the https.
1574 if proxy:
1575 scheme = 'http'
1576 else:
1577 scheme = 'https'
1578
1579 # Parse out any authentication information using the base class
1580 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1581
1582 url = urllib.parse.urlunparse((
1583 scheme,
1584 host,
1585 parse_results.path,
1586 parse_results.params,
1587 parse_results.query,
1588 parse_results.fragment))
1589
1590 request = urllib.request.Request(url, request_body)
1591 if extra_headers is not None:
1592 for (name, header) in extra_headers:
1593 request.add_header(name, header)
1594 request.add_header('Content-Type', 'text/xml')
1595 try:
1596 response = opener.open(request)
1597 except urllib.error.HTTPError as e:
1598 if e.code == 501:
1599 # We may have been redirected through a login process
1600 # but our POST turned into a GET. Retry.
1601 response = opener.open(request)
1602 else:
1603 raise
1604
1605 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001606 # Response should be fairly small, so read it all at once.
1607 # This way we can show it to the user in case of error (e.g. HTML).
1608 data = response.read()
1609 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001610 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001611 except xml.parsers.expat.ExpatError as e:
1612 raise IOError(
1613 f'Parsing the manifest failed: {e}\n'
1614 f'Please report this to your manifest server admin.\n'
1615 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001616 p.close()
1617 return u.close()
1618
1619 def close(self):
1620 pass