blob: b7542ccac0e4a2b0f238653b3e305b21963a0085 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
LaMont Jonesbdcba7d2022-04-11 22:50:11 +000015import collections
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
LaMont Jones891e8f72022-09-08 20:17:58 +000024import shutil
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070025import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070026import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070027import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070028import time
LaMont Jones78dcd372022-10-25 22:38:07 +000029from typing import NamedTuple, List, Set
Mike Frysingeracf63b22019-06-13 02:24:21 -040030import urllib.error
31import urllib.parse
32import urllib.request
Mike Frysinger5951e302022-05-20 23:34:44 -040033import xml.parsers.expat
Mike Frysingeracf63b22019-06-13 02:24:21 -040034import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070035
Roy Lee18afd7f2010-05-09 04:32:08 +080036try:
37 import threading as _threading
38except ImportError:
39 import dummy_threading as _threading
40
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070041try:
42 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090043
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070044 def _rlimit_nofile():
45 return resource.getrlimit(resource.RLIMIT_NOFILE)
46except ImportError:
47 def _rlimit_nofile():
48 return (256, 256)
49
David Rileye0684ad2017-04-05 00:02:59 -070050import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040051from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090052from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090053from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080054import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070055import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070056from project import Project
57from project import RemoteSpec
Mike Frysinger355f4392022-07-20 17:15:29 -040058from command import Command, DEFAULT_LOCAL_JOBS, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080059from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070060import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070061from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070062from progress import Progress
Joanna Wanga6c52f52022-11-03 16:51:19 -040063from repo_trace import Trace
Mike Frysinger19e409c2021-05-05 19:44:35 -040064import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080065from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070066from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070067
Dave Borowitz67700e92012-10-23 15:00:54 -070068_ONE_DAY_S = 24 * 60 * 60
LaMont Jones891e8f72022-09-08 20:17:58 +000069# Env var to implicitly turn off object backups.
70REPO_BACKUP_OBJECTS = 'REPO_BACKUP_OBJECTS'
71
72_BACKUP_OBJECTS = os.environ.get(REPO_BACKUP_OBJECTS) != '0'
Dave Borowitz67700e92012-10-23 15:00:54 -070073
David Pursehouse819827a2020-02-12 15:20:19 +090074
LaMont Jones1eddca82022-09-01 15:15:04 +000075class _FetchOneResult(NamedTuple):
76 """_FetchOne return value.
77
78 Attributes:
79 success (bool): True if successful.
80 project (Project): The fetched project.
81 start (float): The starting time.time().
82 finish (float): The ending time.time().
83 remote_fetched (bool): True if the remote was actually queried.
84 """
85 success: bool
86 project: Project
87 start: float
88 finish: float
89 remote_fetched: bool
90
91
92class _FetchResult(NamedTuple):
93 """_Fetch return value.
94
95 Attributes:
96 success (bool): True if successful.
LaMont Jones78dcd372022-10-25 22:38:07 +000097 projects (Set[str]): The names of the git directories of fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +000098 """
99 success: bool
LaMont Jones78dcd372022-10-25 22:38:07 +0000100 projects: Set[str]
LaMont Jones1eddca82022-09-01 15:15:04 +0000101
102
103class _FetchMainResult(NamedTuple):
104 """_FetchMain return value.
105
106 Attributes:
LaMont Jones78dcd372022-10-25 22:38:07 +0000107 all_projects (List[Project]): The fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000108 """
LaMont Jones78dcd372022-10-25 22:38:07 +0000109 all_projects: List[Project]
LaMont Jones1eddca82022-09-01 15:15:04 +0000110
111
112class _CheckoutOneResult(NamedTuple):
113 """_CheckoutOne return value.
114
115 Attributes:
116 success (bool): True if successful.
117 project (Project): The project.
118 start (float): The starting time.time().
119 finish (float): The ending time.time().
120 """
121 success: bool
122 project: Project
123 start: float
124 finish: float
125
126
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800127class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -0400128 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000129 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700130 helpSummary = "Update working tree to the latest revision"
131 helpUsage = """
132%prog [<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 Jones7efab532022-09-01 15:41:12 +0000315 p.add_option('--auto-gc', action='store_true',
316 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
Mike Frysinger7b586f22021-02-23 18:38:39 -0500473 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900474 try:
LaMont Jones1eddca82022-09-01 15:15:04 +0000475 sync_result = project.Sync_NetworkHalf(
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500476 quiet=opt.quiet,
477 verbose=opt.verbose,
478 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000479 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500480 force_sync=opt.force_sync,
481 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000482 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500483 optimized_fetch=opt.optimized_fetch,
484 retry_fetches=opt.retry_fetches,
485 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400486 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000487 clone_filter=project.manifest.CloneFilter,
488 partial_clone_exclude=project.manifest.PartialCloneExclude)
LaMont Jones1eddca82022-09-01 15:15:04 +0000489 success = sync_result.success
Doug Andersonfc06ced2011-03-16 15:49:18 -0700490
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500491 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400492 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500493 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700494
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500495 if not success:
496 print('error: Cannot fetch %s from %s'
497 % (project.name, project.remote.url),
498 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700499 except GitError as e:
500 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500501 except Exception as e:
502 print('error: Cannot fetch %s (%s: %s)'
503 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
504 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500505
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500506 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000507 return _FetchOneResult(success, project, start, finish,
508 sync_result.remote_fetched)
David James8d201162013-10-11 17:03:19 -0700509
Mike Frysinger339f2df2021-05-06 00:44:42 -0400510 @classmethod
511 def _FetchInitChild(cls, ssh_proxy):
512 cls.ssh_proxy = ssh_proxy
513
514 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500515 ret = True
516
Mike Frysinger355f4392022-07-20 17:15:29 -0400517 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700518 fetched = set()
LaMont Jones1eddca82022-09-01 15:15:04 +0000519 remote_fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400520 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800521
David James89ece422014-01-09 18:51:58 -0800522 objdir_project_map = dict()
523 for project in projects:
524 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500525 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700526
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500527 def _ProcessResults(results_sets):
528 ret = True
529 for results in results_sets:
LaMont Jones1eddca82022-09-01 15:15:04 +0000530 for result in results:
531 success = result.success
532 project = result.project
533 start = result.start
534 finish = result.finish
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500535 self._fetch_times.Set(project, finish - start)
536 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
537 start, finish, success)
LaMont Jones1eddca82022-09-01 15:15:04 +0000538 if result.remote_fetched:
539 remote_fetched.add(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500540 # Check for any errors before running any more tasks.
541 # ...we'll let existing jobs finish, though.
542 if not success:
543 ret = False
544 else:
545 fetched.add(project.gitdir)
546 pm.update(msg=project.name)
547 if not ret and opt.fail_fast:
548 break
549 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700550
Mike Frysinger339f2df2021-05-06 00:44:42 -0400551 # We pass the ssh proxy settings via the class. This allows multiprocessing
552 # to pickle it up when spawning children. We can't pass it as an argument
553 # to _FetchProjectList below as multiprocessing is unable to pickle those.
554 Sync.ssh_proxy = None
555
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500556 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400557 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400558 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500559 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
560 ret = False
561 else:
562 # Favor throughput over responsiveness when quiet. It seems that imap()
563 # will yield results in batches relative to chunksize, so even as the
564 # children finish a sync, we won't see the result until one child finishes
565 # ~chunksize jobs. When using a large --jobs with large chunksize, this
566 # can be jarring as there will be a large initial delay where repo looks
567 # like it isn't doing anything and sits at 0%, but then suddenly completes
568 # a lot of jobs all at once. Since this code is more network bound, we
569 # can accept a bit more CPU overhead with a smaller chunksize so that the
570 # user sees more immediate & continuous feedback.
571 if opt.quiet:
572 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800573 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500574 pm.update(inc=0, msg='warming up')
575 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800576 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
577 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500578 results = pool.imap_unordered(
579 functools.partial(self._FetchProjectList, opt),
580 projects_list,
581 chunksize=chunksize)
582 if not _ProcessResults(results):
583 ret = False
584 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800585
Mike Frysinger339f2df2021-05-06 00:44:42 -0400586 # Cleanup the reference now that we're done with it, and we're going to
587 # release any resources it points to. If we don't, later multiprocessing
588 # usage (e.g. checkouts) will try to pickle and then crash.
589 del Sync.ssh_proxy
590
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700591 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700592 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700593
LaMont Jonesa46047a2022-04-07 21:57:06 +0000594 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400595 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200596
LaMont Jones1eddca82022-09-01 15:15:04 +0000597 return _FetchResult(ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700598
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000599 def _FetchMain(self, opt, args, all_projects, err_event,
600 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400601 """The main network fetch loop.
602
603 Args:
604 opt: Program options returned from optparse. See _Options().
605 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200606 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400607 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400608 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000609 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200610
611 Returns:
612 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400613 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000614 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400615
616 to_fetch = []
617 now = time.time()
618 if _ONE_DAY_S <= (now - rp.LastFetch):
619 to_fetch.append(rp)
620 to_fetch.extend(all_projects)
621 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
622
LaMont Jones1eddca82022-09-01 15:15:04 +0000623 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
624 success = result.success
625 fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400626 if not success:
627 err_event.set()
628
629 _PostRepoFetch(rp, opt.repo_verify)
630 if opt.network_only:
631 # bail out now; the rest touches the working tree
632 if err_event.is_set():
633 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
634 sys.exit(1)
LaMont Jones1eddca82022-09-01 15:15:04 +0000635 return _FetchMainResult([])
Mike Frysingerb4429432021-05-05 20:03:26 -0400636
637 # Iteratively fetch missing and/or nested unregistered submodules
638 previously_missing_set = set()
639 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000640 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400641 all_projects = self.GetProjects(args,
642 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000643 submodules_ok=opt.fetch_submodules,
644 manifest=manifest,
645 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400646 missing = []
647 for project in all_projects:
648 if project.gitdir not in fetched:
649 missing.append(project)
650 if not missing:
651 break
652 # Stop us from non-stopped fetching actually-missing repos: If set of
653 # missing repos has not been changed from last fetch, we break.
654 missing_set = set(p.name for p in missing)
655 if previously_missing_set == missing_set:
656 break
657 previously_missing_set = missing_set
LaMont Jones1eddca82022-09-01 15:15:04 +0000658 result = self._Fetch(missing, opt, err_event, ssh_proxy)
659 success = result.success
660 new_fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400661 if not success:
662 err_event.set()
663 fetched.update(new_fetched)
664
LaMont Jones1eddca82022-09-01 15:15:04 +0000665 return _FetchMainResult(all_projects)
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200666
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500667 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700668 """Checkout work tree for one project
669
670 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500671 detach_head: Whether to leave a detached HEAD.
672 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700673 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700674
675 Returns:
676 Whether the fetch was successful.
677 """
Xin Li745be2e2019-06-03 11:24:30 -0700678 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000679 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500680 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700681 success = False
682 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500683 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500684 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700685 except GitError as e:
686 print('error.GitError: Cannot checkout %s: %s' %
687 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500688 except Exception as e:
689 print('error: Cannot checkout %s: %s: %s' %
690 (project.name, type(e).__name__, str(e)),
691 file=sys.stderr)
692 raise
Xin Li745be2e2019-06-03 11:24:30 -0700693
Mike Frysingerebf04a42021-02-23 20:48:04 -0500694 if not success:
695 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
696 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000697 return _CheckoutOneResult(success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700698
Mike Frysingerebf04a42021-02-23 20:48:04 -0500699 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700700 """Checkout projects listed in all_projects
701
702 Args:
703 all_projects: List of all projects that should be checked out.
704 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500705 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700706 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500707 # Only checkout projects with worktrees.
708 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700709
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500710 def _ProcessResults(pool, pm, results):
711 ret = True
LaMont Jones1eddca82022-09-01 15:15:04 +0000712 for result in results:
713 success = result.success
714 project = result.project
715 start = result.start
716 finish = result.finish
Mike Frysingerebf04a42021-02-23 20:48:04 -0500717 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
718 start, finish, success)
719 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500720 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500721 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500722 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500723 err_results.append(project.relpath)
724 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500725 if pool:
726 pool.close()
727 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500728 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500729 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700730
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500731 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400732 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500733 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
734 all_projects,
735 callback=_ProcessResults,
736 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500737
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000738 def _backup_cruft(self, bare_git):
739 """Save a copy of any cruft from `git gc`."""
740 # Find any cruft packs in the current gitdir, and save them.
741 # b/221065125 (repo sync complains that objects are missing). This does
742 # not prevent that state, but makes it so that the missing objects are
743 # available.
744 objdir = bare_git._project.objdir
745 pack_dir = os.path.join(objdir, 'pack')
746 bak_dir = os.path.join(objdir, '.repo', 'pack.bak')
747 if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir):
748 return
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000749 files = set(platform_utils.listdir(pack_dir))
750 to_backup = []
751 for f in files:
752 base, ext = os.path.splitext(f)
753 if base + '.mtimes' in files:
754 to_backup.append(f)
755 if to_backup:
756 os.makedirs(bak_dir, exist_ok=True)
757 for fname in to_backup:
758 bak_fname = os.path.join(bak_dir, fname)
759 if not os.path.exists(bak_fname):
Joanna Wanga6c52f52022-11-03 16:51:19 -0400760 with Trace('%s saved %s', bare_git._project.name, fname):
761 # Use a tmp file so that we are sure of a complete copy.
762 shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp')
763 shutil.move(bak_fname + '.tmp', bak_fname)
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000764
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000765 @staticmethod
766 def _GetPreciousObjectsState(project: Project, opt):
767 """Get the preciousObjects state for the project.
768
769 Args:
770 project (Project): the project to examine, and possibly correct.
771 opt (optparse.Values): options given to sync.
772
773 Returns:
774 Expected state of extensions.preciousObjects:
775 False: Should be disabled. (not present)
776 True: Should be enabled.
777 """
778 if project.use_git_worktrees:
779 return False
780 projects = project.manifest.GetProjectsWithName(project.name,
781 all_manifests=True)
782 if len(projects) == 1:
783 return False
784 relpath = project.RelPath(local=opt.this_manifest_only)
785 if len(projects) > 1:
786 # Objects are potentially shared with another project.
787 # See the logic in Project.Sync_NetworkHalf regarding UseAlternates.
788 # - When False, shared projects share (via symlink)
789 # .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects
790 # directory. All objects are precious, since there is no project with a
791 # complete set of refs.
792 # - When True, shared projects share (via info/alternates)
793 # .repo/project-objects/{PROJECT_NAME}.git as an alternate object store,
794 # which is written only on the first clone of the project, and is not
795 # written subsequently. (When Sync_NetworkHalf sees that it exists, it
796 # makes sure that the alternates file points there, and uses a
797 # project-local .git/objects directory for all syncs going forward.
798 # We do not support switching between the options. The environment
799 # variable is present for testing and migration only.
800 return not project.UseAlternates
801 print(f'\r{relpath}: project not found in manifest.', file=sys.stderr)
802 return False
803
804 def _RepairPreciousObjectsState(self, project: Project, opt):
805 """Correct the preciousObjects state for the project.
806
807 Args:
808 project (Project): the project to examine, and possibly correct.
809 opt (optparse.Values): options given to sync.
810 """
811 expected = self._GetPreciousObjectsState(project, opt)
812 actual = project.config.GetBoolean('extensions.preciousObjects') or False
813 relpath = project.RelPath(local = opt.this_manifest_only)
814
815 if (expected != actual and
816 not project.config.GetBoolean('repo.preservePreciousObjects')):
817 # If this is unexpected, log it and repair.
818 Trace(f'{relpath} expected preciousObjects={expected}, got {actual}')
819 if expected:
820 if not opt.quiet:
821 print('\r%s: Shared project %s found, disabling pruning.' %
822 (relpath, project.name))
823 if git_require((2, 7, 0)):
824 project.EnableRepositoryExtension('preciousObjects')
825 else:
826 # This isn't perfect, but it's the best we can do with old git.
827 print('\r%s: WARNING: shared projects are unreliable when using '
828 'old versions of git; please upgrade to git-2.7.0+.'
829 % (relpath,),
830 file=sys.stderr)
831 project.config.SetString('gc.pruneExpire', 'never')
832 else:
833 if not opt.quiet:
834 print(f'\r{relpath}: not shared, disabling pruning.')
835 project.config.SetString('extensions.preciousObjects', None)
836 project.config.SetString('gc.pruneExpire', None)
837
Mike Frysinger5a033082019-09-23 19:21:20 -0400838 def _GCProjects(self, projects, opt, err_event):
LaMont Jones7efab532022-09-01 15:41:12 +0000839 """Perform garbage collection.
840
841 If We are skipping garbage collection (opt.auto_gc not set), we still want
842 to potentially mark objects precious, so that `git gc` does not discard
843 shared objects.
844 """
845 pm = Progress(f'{"" if opt.auto_gc else "NOT "}Garbage collecting',
846 len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400847 pm.update(inc=0, msg='prescan')
848
Allen Webb4ee4a452021-10-07 10:42:38 -0500849 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700850 for project in projects:
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000851 self._RepairPreciousObjectsState(project, opt)
852
Allen Webb669efd02021-10-01 15:25:31 -0500853 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500854 # Only call git gc once per objdir, but call pack-refs for the remainder.
855 if project.objdir not in tidy_dirs:
856 tidy_dirs[project.objdir] = (
857 True, # Run a full gc.
858 project.bare_git,
859 )
860 elif project.gitdir not in tidy_dirs:
861 tidy_dirs[project.gitdir] = (
862 False, # Do not run a full gc; just run pack-refs.
863 project.bare_git,
864 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400865
LaMont Jones7efab532022-09-01 15:41:12 +0000866 if not opt.auto_gc:
867 pm.end()
868 return
869
Mike Frysinger355f4392022-07-20 17:15:29 -0400870 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700871
LaMont Jonesacc4c852022-09-22 19:05:01 +0000872 gc_args = ['--auto']
873 backup_cruft = False
874 if git_require((2, 37, 0)):
875 gc_args.append('--cruft')
876 backup_cruft = True
LaMont Jones891e8f72022-09-08 20:17:58 +0000877 pack_refs_args = ()
Dave Borowitz18857212012-10-23 17:02:59 -0700878 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500879 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400880 pm.update(msg=bare_git._project.name)
LaMont Jones891e8f72022-09-08 20:17:58 +0000881
Allen Webb4ee4a452021-10-07 10:42:38 -0500882 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000883 bare_git.gc(*gc_args)
Allen Webb4ee4a452021-10-07 10:42:38 -0500884 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000885 bare_git.pack_refs(*pack_refs_args)
LaMont Jonesacc4c852022-09-22 19:05:01 +0000886 if backup_cruft:
887 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400888 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700889 return
890
Mike Frysinger355f4392022-07-20 17:15:29 -0400891 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400892 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700893
894 threads = set()
895 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700896
Allen Webb4ee4a452021-10-07 10:42:38 -0500897 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400898 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700899 try:
900 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500901 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000902 bare_git.gc(*gc_args, config=config)
Allen Webb4ee4a452021-10-07 10:42:38 -0500903 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000904 bare_git.pack_refs(*pack_refs_args, config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700905 except GitError:
906 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900907 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700908 err_event.set()
909 raise
910 finally:
LaMont Jonesacc4c852022-09-22 19:05:01 +0000911 if backup_cruft:
912 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400913 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700914 sem.release()
915
Allen Webb4ee4a452021-10-07 10:42:38 -0500916 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500917 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700918 break
919 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500920 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700921 t.daemon = True
922 threads.add(t)
923 t.start()
924
925 for t in threads:
926 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400927 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700928
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000929 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700930 """Reload the manfiest from the file specified by the |manifest_name|.
931
932 It unloads the manifest if |manifest_name| is None.
933
934 Args:
935 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000936 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700937 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800938 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000939 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000940 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800941 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000942 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800943
LaMont Jonesa46047a2022-04-07 21:57:06 +0000944 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000945 """Update the cached projects list for |manifest|
946
947 In a multi-manifest checkout, each manifest has its own project.list.
948
949 Args:
950 opt: Program options returned from optparse. See _Options().
951 manifest: The manifest to use.
952
953 Returns:
954 0: success
955 1: failure
956 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700957 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000958 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
959 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700960 if project.relpath:
961 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700962 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000963 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700964 old_project_paths = []
965
966 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500967 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700968 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800969 # In reversed order, so subfolders are deleted before parent folder.
970 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700971 if not path:
972 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700973 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900974 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000975 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700976 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900977 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000978 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900979 name=path,
980 remote=RemoteSpec('origin'),
981 gitdir=gitdir,
982 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500983 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000984 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900985 relpath=path,
986 revisionExpr='HEAD',
987 revisionId=None,
988 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500989 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900990 quiet=opt.quiet,
991 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400992 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700993
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700994 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500995 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700996 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700997 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700998 return 0
999
LaMont Jonesa46047a2022-04-07 21:57:06 +00001000 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +08001001 """Save all dests of copyfile and linkfile, and update them if needed.
1002
1003 Returns:
1004 Whether update was successful.
1005 """
1006 new_paths = {}
1007 new_linkfile_paths = []
1008 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001009 for project in self.GetProjects(None, missing_ok=True,
1010 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +08001011 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
1012 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
1013
1014 new_paths = {
1015 'linkfile': new_linkfile_paths,
1016 'copyfile': new_copyfile_paths,
1017 }
1018
1019 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001020 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +08001021 old_copylinkfile_paths = {}
1022
1023 if os.path.exists(copylinkfile_path):
1024 with open(copylinkfile_path, 'rb') as fp:
1025 try:
1026 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001027 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +08001028 print('error: %s is not a json formatted file.' %
1029 copylinkfile_path, file=sys.stderr)
1030 platform_utils.remove(copylinkfile_path)
1031 return False
1032
1033 need_remove_files = []
1034 need_remove_files.extend(
1035 set(old_copylinkfile_paths.get('linkfile', [])) -
1036 set(new_linkfile_paths))
1037 need_remove_files.extend(
1038 set(old_copylinkfile_paths.get('copyfile', [])) -
1039 set(new_copyfile_paths))
1040
1041 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -04001042 # Try to remove the updated copyfile or linkfile.
1043 # So, if the file is not exist, nothing need to do.
1044 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +08001045
1046 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
1047 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
1048 json.dump(new_paths, fp)
1049 return True
1050
LaMont Jonesa46047a2022-04-07 21:57:06 +00001051 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1052 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001053 print('error: cannot smart sync: no manifest server defined in '
1054 'manifest', file=sys.stderr)
1055 sys.exit(1)
1056
LaMont Jonesa46047a2022-04-07 21:57:06 +00001057 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001058 if not opt.quiet:
1059 print('Using manifest server %s' % manifest_server)
1060
David Pursehouseeeff3532020-02-12 11:24:10 +09001061 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001062 username = None
1063 password = None
1064 if opt.manifest_server_username and opt.manifest_server_password:
1065 username = opt.manifest_server_username
1066 password = opt.manifest_server_password
1067 else:
1068 try:
1069 info = netrc.netrc()
1070 except IOError:
1071 # .netrc file does not exist or could not be opened
1072 pass
1073 else:
1074 try:
1075 parse_result = urllib.parse.urlparse(manifest_server)
1076 if parse_result.hostname:
1077 auth = info.authenticators(parse_result.hostname)
1078 if auth:
1079 username, _account, password = auth
1080 else:
1081 print('No credentials found for %s in .netrc'
1082 % parse_result.hostname, file=sys.stderr)
1083 except netrc.NetrcParseError as e:
1084 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
1085
1086 if (username and password):
1087 manifest_server = manifest_server.replace('://', '://%s:%s@' %
1088 (username, password),
1089 1)
1090
1091 transport = PersistentTransport(manifest_server)
1092 if manifest_server.startswith('persistent-'):
1093 manifest_server = manifest_server[len('persistent-'):]
1094
1095 try:
1096 server = xmlrpc.client.Server(manifest_server, transport=transport)
1097 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001098 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001099
Mike Frysinger56ce3462019-12-04 19:30:48 -05001100 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -05001101 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001102 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -05001103 elif ('TARGET_PRODUCT' in os.environ and
1104 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -05001105 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
1106 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001107 [success, manifest_str] = server.GetApprovedManifest(branch, target)
1108 else:
1109 [success, manifest_str] = server.GetApprovedManifest(branch)
1110 else:
1111 assert(opt.smart_tag)
1112 [success, manifest_str] = server.GetManifest(opt.smart_tag)
1113
1114 if success:
1115 manifest_name = os.path.basename(smart_sync_manifest_path)
1116 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001117 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001118 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001119 except IOError as e:
1120 print('error: cannot write manifest to %s:\n%s'
1121 % (smart_sync_manifest_path, e),
1122 file=sys.stderr)
1123 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001124 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001125 else:
1126 print('error: manifest server RPC call failed: %s' %
1127 manifest_str, file=sys.stderr)
1128 sys.exit(1)
1129 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1130 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001131 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001132 sys.exit(1)
1133 except xmlrpc.client.ProtocolError as e:
1134 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001135 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001136 file=sys.stderr)
1137 sys.exit(1)
1138
1139 return manifest_name
1140
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001141 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
1142 """Fetch & update the local manifest project.
1143
1144 After syncing the manifest project, if the manifest has any sub manifests,
1145 those are recursively processed.
1146
1147 Args:
1148 opt: Program options returned from optparse. See _Options().
1149 mp: the manifestProject to query.
1150 manifest_name: Manifest file to be reloaded.
1151 """
1152 if not mp.standalone_manifest_url:
1153 self._UpdateManifestProject(opt, mp, manifest_name)
1154
1155 if mp.manifest.submanifests:
1156 for submanifest in mp.manifest.submanifests.values():
1157 child = submanifest.repo_client.manifest
1158 child.manifestProject.SyncWithPossibleInit(
1159 submanifest,
1160 current_branch_only=self._GetCurrentBranchOnly(opt, child),
1161 verbose=opt.verbose,
1162 tags=opt.tags,
1163 git_event_log=self.git_event_log,
1164 )
1165 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
1166
Mike Frysingerfb527e32019-08-27 02:34:32 -04001167 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001168 """Fetch & update the local manifest project.
1169
1170 Args:
1171 opt: Program options returned from optparse. See _Options().
1172 mp: the manifestProject to query.
1173 manifest_name: Manifest file to be reloaded.
1174 """
Mike Frysingerfb527e32019-08-27 02:34:32 -04001175 if not opt.local_only:
1176 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -05001177 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001178 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +02001179 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001180 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -04001181 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001182 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001183 submodules=mp.manifest.HasSubmodules,
1184 clone_filter=mp.manifest.CloneFilter,
1185 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001186 finish = time.time()
1187 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1188 start, finish, success)
1189
1190 if mp.HasChanges:
1191 syncbuf = SyncBuffer(mp.config)
1192 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001193 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001194 clean = syncbuf.Finish()
1195 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1196 start, time.time(), clean)
1197 if not clean:
1198 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001199 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001200
Mike Frysingerae6cb082019-08-27 01:10:59 -04001201 def ValidateOptions(self, opt, args):
1202 if opt.force_broken:
1203 print('warning: -f/--force-broken is now the default behavior, and the '
1204 'options are deprecated', file=sys.stderr)
1205 if opt.network_only and opt.detach_head:
1206 self.OptionParser.error('cannot combine -n and -d')
1207 if opt.network_only and opt.local_only:
1208 self.OptionParser.error('cannot combine -n and -l')
1209 if opt.manifest_name and opt.smart_sync:
1210 self.OptionParser.error('cannot combine -m and -s')
1211 if opt.manifest_name and opt.smart_tag:
1212 self.OptionParser.error('cannot combine -m and -t')
1213 if opt.manifest_server_username or opt.manifest_server_password:
1214 if not (opt.smart_sync or opt.smart_tag):
1215 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1216 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1217 self.OptionParser.error('both -u and -p must be given')
1218
Mike Frysinger0531a622021-11-05 15:22:01 -04001219 if opt.prune is None:
1220 opt.prune = True
1221
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001222 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001223 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001224 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001225 manifest = self.manifest
1226
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001227 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001228 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001229
Chirayu Desaia892b102013-06-11 14:18:46 +05301230 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001231 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001232 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301233
Xin Lid79a4bc2020-05-20 16:03:45 -07001234 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001235 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001236
Victor Boivie08c880d2011-04-19 10:32:52 +02001237 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001238 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001239 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001240 if os.path.isfile(smart_sync_manifest_path):
1241 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001242 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001243 except OSError as e:
1244 print('error: failed to remove existing smart sync override manifest: %s' %
1245 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001246
Mike Frysingerc99322a2021-05-04 15:32:43 -04001247 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001248
LaMont Jonesa46047a2022-04-07 21:57:06 +00001249 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001250 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001251 cb = rp.CurrentBranch
1252 if cb:
1253 base = rp.GetBranch(cb).merge
1254 if not base or not base.startswith('refs/heads/'):
1255 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001256 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001257 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001258
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001259 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001260 if not m.manifestProject.standalone_manifest_url:
1261 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001262
LaMont Jones4112c072022-08-24 17:32:25 +00001263 if opt.repo_upgraded:
1264 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001265
LaMont Jones4112c072022-08-24 17:32:25 +00001266 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001267 if opt.mp_update:
1268 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1269 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001270 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001271
Mike Frysinger355f4392022-07-20 17:15:29 -04001272 # Now that the manifests are up-to-date, setup the jobs value.
1273 if opt.jobs is None:
1274 # User has not made a choice, so use the manifest settings.
1275 opt.jobs = mp.default.sync_j
1276 if opt.jobs is not None:
1277 # Neither user nor manifest have made a choice.
1278 if opt.jobs_network is None:
1279 opt.jobs_network = opt.jobs
1280 if opt.jobs_checkout is None:
1281 opt.jobs_checkout = opt.jobs
1282 # Setup defaults if jobs==0.
1283 if not opt.jobs:
1284 if not opt.jobs_network:
1285 opt.jobs_network = 1
1286 if not opt.jobs_checkout:
1287 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1288 opt.jobs = os.cpu_count()
1289
1290 # Try to stay under user rlimit settings.
1291 #
1292 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1293 # that to scale down the number of jobs. Unfortunately there isn't an easy
1294 # way to determine this reliably as systems change, but it was last measured
1295 # by hand in 2011.
1296 soft_limit, _ = _rlimit_nofile()
1297 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1298 opt.jobs = min(opt.jobs, jobs_soft_limit)
1299 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1300 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1301
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001302 superproject_logging_data = {}
1303 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1304 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001305
Simran Basib9a1b732015-08-20 12:19:28 -07001306 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001307 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001308 gitc_projects = []
1309 opened_projects = []
1310 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001311 if project.relpath in self.gitc_manifest.paths and \
1312 self.gitc_manifest.paths[project.relpath].old_revision:
1313 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001314 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001315 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001316
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001317 if not args:
1318 gitc_projects = None
1319
1320 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001321 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001322 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1323 if manifest_name:
1324 manifest.Override(manifest_name)
1325 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001326 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001327 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1328 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001329 gitc_projects)
1330 print('GITC client successfully synced.')
1331
1332 # The opened projects need to be synced as normal, therefore we
1333 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001334 # TODO: make this more reliable -- if there's a project name/path overlap,
1335 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001336 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001337 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001338 if not args:
1339 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001340
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001341 all_projects = self.GetProjects(args,
1342 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001343 submodules_ok=opt.fetch_submodules,
1344 manifest=manifest,
1345 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001346
Mike Frysinger5a033082019-09-23 19:21:20 -04001347 err_network_sync = False
1348 err_update_projects = False
LaMont Jonesb6cfa092022-10-26 16:34:40 +00001349 err_update_linkfiles = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001350
LaMont Jonesa46047a2022-04-07 21:57:06 +00001351 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001352 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001353 with multiprocessing.Manager() as manager:
1354 with ssh.ProxyManager(manager) as ssh_proxy:
1355 # Initialize the socket dir once in the parent.
1356 ssh_proxy.sock()
LaMont Jones1eddca82022-09-01 15:15:04 +00001357 result = self._FetchMain(opt, args, all_projects, err_event,
1358 ssh_proxy, manifest)
1359 all_projects = result.all_projects
Mike Frysinger339f2df2021-05-06 00:44:42 -04001360
1361 if opt.network_only:
1362 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001363
1364 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001365 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001366 err_network_sync = True
1367 if opt.fail_fast:
1368 print('\nerror: Exited sync due to fetch errors.\n'
1369 'Local checkouts *not* updated. Resolve network issues & '
1370 'retry.\n'
1371 '`repo sync -l` will update some local checkouts.',
1372 file=sys.stderr)
1373 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001374
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001375 for m in self.ManifestList(opt):
1376 if m.IsMirror or m.IsArchive:
1377 # bail out now, we have no working tree
1378 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001379
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001380 if self.UpdateProjectList(opt, m):
1381 err_event.set()
1382 err_update_projects = True
1383 if opt.fail_fast:
1384 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1385 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001386
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001387 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1388 if err_update_linkfiles:
1389 err_event.set()
1390 if opt.fail_fast:
1391 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1392 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001393
Mike Frysinger5a033082019-09-23 19:21:20 -04001394 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001395 # NB: We don't exit here because this is the last step.
1396 err_checkout = not self._Checkout(all_projects, opt, err_results)
1397 if err_checkout:
1398 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001399
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001400 printed_notices = set()
1401 # If there's a notice that's supposed to print at the end of the sync,
1402 # print it now... But avoid printing duplicate messages, and preserve
1403 # order.
1404 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1405 if m.notice and m.notice not in printed_notices:
1406 print(m.notice)
1407 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001408
Mike Frysinger5a033082019-09-23 19:21:20 -04001409 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001410 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001411 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1412 if err_network_sync:
1413 print('error: Downloading network changes failed.', file=sys.stderr)
1414 if err_update_projects:
1415 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001416 if err_update_linkfiles:
1417 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001418 if err_checkout:
1419 print('error: Checking out local projects failed.', file=sys.stderr)
1420 if err_results:
1421 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1422 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1423 file=sys.stderr)
1424 sys.exit(1)
1425
Raman Tenneti7954de12021-07-28 14:36:49 -07001426 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001427 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1428 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001429
1430 # Update and log with the new sync analysis state.
1431 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001432 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1433 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001434
Mike Frysingere19d9e12020-02-12 11:23:32 -05001435 if not opt.quiet:
1436 print('repo sync has finished successfully.')
1437
David Pursehouse819827a2020-02-12 15:20:19 +09001438
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001439def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001440 # Link the docs for the internal .repo/ layout for people
1441 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1442 if not platform_utils.islink(link):
1443 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1444 try:
1445 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001446 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001447 pass
1448
Conley Owens094cdbe2014-01-30 15:09:59 -08001449 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001450 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001451 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001452 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001453 if project.Exists:
1454 project.PostRepoUpgrade()
1455
David Pursehouse819827a2020-02-12 15:20:19 +09001456
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001457def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001458 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001459 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001460 wrapper = Wrapper()
1461 try:
1462 rev = rp.bare_git.describe(rp.GetRevisionId())
1463 except GitError:
1464 rev = None
1465 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1466 # See if we're held back due to missing signed tag.
1467 current_revid = rp.bare_git.rev_parse('HEAD')
1468 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1469 if current_revid != new_revid:
1470 # We want to switch to the new rev, but also not trash any uncommitted
1471 # changes. This helps with local testing/hacking.
1472 # If a local change has been made, we will throw that away.
1473 # We also have to make sure this will switch to an older commit if that's
1474 # the latest tag in order to support release rollback.
1475 try:
1476 rp.work_git.reset('--keep', new_rev)
1477 except GitError as e:
1478 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001479 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001480 raise RepoChangedException(['--repo-upgraded'])
1481 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001482 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001483 else:
1484 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001485 print('repo version %s is current' % rp.work_git.describe(HEAD),
1486 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001487
David Pursehouse819827a2020-02-12 15:20:19 +09001488
Dave Borowitz67700e92012-10-23 15:00:54 -07001489class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001490 _ALPHA = 0.5
1491
Dave Borowitz67700e92012-10-23 15:00:54 -07001492 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001493 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001494 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001495 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001496
1497 def Get(self, project):
1498 self._Load()
1499 return self._times.get(project.name, _ONE_DAY_S)
1500
1501 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001502 self._Load()
1503 name = project.name
1504 old = self._times.get(name, t)
1505 self._seen.add(name)
1506 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001507 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001508
1509 def _Load(self):
1510 if self._times is None:
1511 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001512 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001513 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001514 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001515 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001516 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001517
1518 def Save(self):
1519 if self._times is None:
1520 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001521
1522 to_delete = []
1523 for name in self._times:
1524 if name not in self._seen:
1525 to_delete.append(name)
1526 for name in to_delete:
1527 del self._times[name]
1528
Dave Borowitz67700e92012-10-23 15:00:54 -07001529 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001530 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001531 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001532 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001533 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001534
1535# This is a replacement for xmlrpc.client.Transport using urllib2
1536# and supporting persistent-http[s]. It cannot change hosts from
1537# request to request like the normal transport, the real url
1538# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001539
1540
Dan Willemsen0745bb22015-08-17 13:41:45 -07001541class PersistentTransport(xmlrpc.client.Transport):
1542 def __init__(self, orig_host):
1543 self.orig_host = orig_host
1544
1545 def request(self, host, handler, request_body, verbose=False):
1546 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1547 # Python doesn't understand cookies with the #HttpOnly_ prefix
1548 # Since we're only using them for HTTP, copy the file temporarily,
1549 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001550 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001551 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001552 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001553 try:
1554 with open(cookiefile) as f:
1555 for line in f:
1556 if line.startswith("#HttpOnly_"):
1557 line = line[len("#HttpOnly_"):]
1558 tmpcookiefile.write(line)
1559 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001560
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001561 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001562 try:
1563 cookiejar.load()
1564 except cookielib.LoadError:
1565 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001566 finally:
1567 tmpcookiefile.close()
1568 else:
1569 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001570
1571 proxyhandler = urllib.request.ProxyHandler
1572 if proxy:
1573 proxyhandler = urllib.request.ProxyHandler({
1574 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001575 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001576
1577 opener = urllib.request.build_opener(
1578 urllib.request.HTTPCookieProcessor(cookiejar),
1579 proxyhandler)
1580
1581 url = urllib.parse.urljoin(self.orig_host, handler)
1582 parse_results = urllib.parse.urlparse(url)
1583
1584 scheme = parse_results.scheme
1585 if scheme == 'persistent-http':
1586 scheme = 'http'
1587 if scheme == 'persistent-https':
1588 # If we're proxying through persistent-https, use http. The
1589 # proxy itself will do the https.
1590 if proxy:
1591 scheme = 'http'
1592 else:
1593 scheme = 'https'
1594
1595 # Parse out any authentication information using the base class
1596 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1597
1598 url = urllib.parse.urlunparse((
1599 scheme,
1600 host,
1601 parse_results.path,
1602 parse_results.params,
1603 parse_results.query,
1604 parse_results.fragment))
1605
1606 request = urllib.request.Request(url, request_body)
1607 if extra_headers is not None:
1608 for (name, header) in extra_headers:
1609 request.add_header(name, header)
1610 request.add_header('Content-Type', 'text/xml')
1611 try:
1612 response = opener.open(request)
1613 except urllib.error.HTTPError as e:
1614 if e.code == 501:
1615 # We may have been redirected through a login process
1616 # but our POST turned into a GET. Retry.
1617 response = opener.open(request)
1618 else:
1619 raise
1620
1621 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001622 # Response should be fairly small, so read it all at once.
1623 # This way we can show it to the user in case of error (e.g. HTML).
1624 data = response.read()
1625 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001626 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001627 except xml.parsers.expat.ExpatError as e:
1628 raise IOError(
1629 f'Parsing the manifest failed: {e}\n'
1630 f'Please report this to your manifest server admin.\n'
1631 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001632 p.close()
1633 return u.close()
1634
1635 def close(self):
1636 pass