blob: 862c7e2c173ccf70db73b1ba4156e2ab8e36489c [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'
LaMont Jones891e8f72022-09-08 20:17:58 +000071_BACKUP_OBJECTS = os.environ.get(REPO_BACKUP_OBJECTS) != '0'
Dave Borowitz67700e92012-10-23 15:00:54 -070072
LaMont Jones5ed8c632022-11-10 00:10:44 +000073# Env var to implicitly turn auto-gc back on.
74_REPO_AUTO_GC = 'REPO_AUTO_GC'
75_AUTO_GC = os.environ.get(_REPO_AUTO_GC) == '1'
76
David Pursehouse819827a2020-02-12 15:20:19 +090077
LaMont Jones1eddca82022-09-01 15:15:04 +000078class _FetchOneResult(NamedTuple):
79 """_FetchOne return value.
80
81 Attributes:
82 success (bool): True if successful.
83 project (Project): The fetched project.
84 start (float): The starting time.time().
85 finish (float): The ending time.time().
86 remote_fetched (bool): True if the remote was actually queried.
87 """
88 success: bool
89 project: Project
90 start: float
91 finish: float
92 remote_fetched: bool
93
94
95class _FetchResult(NamedTuple):
96 """_Fetch return value.
97
98 Attributes:
99 success (bool): True if successful.
LaMont Jones78dcd372022-10-25 22:38:07 +0000100 projects (Set[str]): The names of the git directories of fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000101 """
102 success: bool
LaMont Jones78dcd372022-10-25 22:38:07 +0000103 projects: Set[str]
LaMont Jones1eddca82022-09-01 15:15:04 +0000104
105
106class _FetchMainResult(NamedTuple):
107 """_FetchMain return value.
108
109 Attributes:
LaMont Jones78dcd372022-10-25 22:38:07 +0000110 all_projects (List[Project]): The fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000111 """
LaMont Jones78dcd372022-10-25 22:38:07 +0000112 all_projects: List[Project]
LaMont Jones1eddca82022-09-01 15:15:04 +0000113
114
115class _CheckoutOneResult(NamedTuple):
116 """_CheckoutOne return value.
117
118 Attributes:
119 success (bool): True if successful.
120 project (Project): The project.
121 start (float): The starting time.time().
122 finish (float): The ending time.time().
123 """
124 success: bool
125 project: Project
126 start: float
127 finish: float
128
129
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800130class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -0400131 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000132 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700133 helpSummary = "Update working tree to the latest revision"
134 helpUsage = """
135%prog [<project>...]
136"""
137 helpDescription = """
138The '%prog' command synchronizes local project directories
139with the remote repositories specified in the manifest. If a local
140project does not yet exist, it will clone a new local directory from
141the remote repository and set up tracking branches as specified in
142the manifest. If the local project already exists, '%prog'
143will update the remote branches and rebase any new local changes
144on top of the new remote changes.
145
146'%prog' will synchronize all projects listed at the command
147line. Projects can be specified either by name, or by a relative
148or absolute path to the project's local directory. If no projects
149are specified, '%prog' will synchronize all projects listed in
150the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700151
152The -d/--detach option can be used to switch specified projects
153back to the manifest revision. This option is especially helpful
154if the project is currently on a topic branch, but the manifest
155revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700156
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700157The -s/--smart-sync option can be used to sync to a known good
158build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200159manifest. The -t/--smart-tag option is similar and allows you to
160specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700161
David Pursehousecf76b1b2012-09-14 10:31:42 +0900162The -u/--manifest-server-username and -p/--manifest-server-password
163options can be used to specify a username and password to authenticate
164with the manifest server when using the -s or -t option.
165
166If -u and -p are not specified when using the -s or -t option, '%prog'
167will attempt to read authentication credentials for the manifest server
168from the user's .netrc file.
169
170'%prog' will not use authentication credentials from -u/-p or .netrc
171if the manifest server specified in the manifest file already includes
172credentials.
173
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400174By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400175to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500176
Kevin Degiabaa7f32014-11-12 11:27:45 -0700177The --force-sync option can be used to overwrite existing git
178directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900179object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700180refs may be removed when overwriting.
181
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500182The --force-remove-dirty option can be used to remove previously used
183projects with uncommitted changes. WARNING: This may cause data to be
184lost since uncommitted changes may be removed with projects that no longer
185exist in the manifest.
186
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700187The --no-clone-bundle option disables any attempt to use
188$URL/clone.bundle to bootstrap a new Git repository from a
189resumeable bundle file on a content delivery network. This
190may be necessary if there are problems with the local Python
191HTTP client or proxy configuration, but the Git binary works.
192
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800193The --fetch-submodules option enables fetching Git submodules
194of a project from server.
195
David Pursehousef2fad612015-01-29 14:36:28 +0900196The -c/--current-branch option can be used to only fetch objects that
197are on the branch specified by a project's revision.
198
David Pursehouseb1553542014-09-04 21:28:09 +0900199The --optimized-fetch option can be used to only fetch projects that
200are fixed to a sha1 revision if the sha1 revision does not already
201exist locally.
202
David Pursehouse74cfd272015-10-14 10:50:15 +0900203The --prune option can be used to remove any refs that no longer
204exist on the remote.
205
LaMont Jones7efab532022-09-01 15:41:12 +0000206The --auto-gc option can be used to trigger garbage collection on all
207projects. By default, repo does not run garbage collection.
208
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400209# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700210
211If at least one project remote URL uses an SSH connection (ssh://,
212git+ssh://, or user@host:path syntax) repo will automatically
213enable the SSH ControlMaster option when connecting to that host.
214This feature permits other projects in the same '%prog' session to
215reuse the same SSH tunnel, saving connection setup overheads.
216
217To disable this behavior on UNIX platforms, set the GIT_SSH
218environment variable to 'ssh'. For example:
219
220 export GIT_SSH=ssh
221 %prog
222
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400223# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700224
225This feature is automatically disabled on Windows, due to the lack
226of UNIX domain socket support.
227
228This feature is not compatible with url.insteadof rewrites in the
229user's ~/.gitconfig. '%prog' is currently not able to perform the
230rewrite early enough to establish the ControlMaster tunnel.
231
232If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
233later is required to fix a server side protocol bug.
234
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400236 # A value of 0 means we want parallel jobs, but we'll determine the default
237 # value later on.
238 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700239
Mike Frysinger9180a072021-04-13 14:57:40 -0400240 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400241 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400242 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400243 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400244 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
245 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400246
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500247 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200248 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400249 help='obsolete option (to be deleted in the future)')
250 p.add_option('--fail-fast',
251 dest='fail_fast', action='store_true',
252 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700253 p.add_option('--force-sync',
254 dest='force_sync', action='store_true',
255 help="overwrite an existing git directory if it needs to "
256 "point to a different object directory. WARNING: this "
257 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500258 p.add_option('--force-remove-dirty',
259 dest='force_remove_dirty', action='store_true',
260 help="force remove projects with uncommitted modifications if "
261 "projects no longer exist in the manifest. "
262 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900263 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700264 dest='local_only', action='store_true',
265 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900266 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100267 dest='mp_update', action='store_false', default='true',
268 help='use the existing manifest checkout as-is. '
269 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900270 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700271 dest='network_only', action='store_true',
272 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900273 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700274 dest='detach_head', action='store_true',
275 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900276 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700277 dest='current_branch_only', action='store_true',
278 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400279 p.add_option('--no-current-branch',
280 dest='current_branch_only', action='store_false',
281 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500282 p.add_option('-m', '--manifest-name',
283 dest='manifest_name',
284 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700285 p.add_option('--clone-bundle', action='store_true',
286 help='enable use of /clone.bundle on HTTP/HTTPS')
287 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700288 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800289 p.add_option('-u', '--manifest-server-username', action='store',
290 dest='manifest_server_username',
291 help='username to authenticate with the manifest server')
292 p.add_option('-p', '--manifest-server-password', action='store',
293 dest='manifest_server_password',
294 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800295 p.add_option('--fetch-submodules',
296 dest='fetch_submodules', action='store_true',
297 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800298 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700299 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700300 p.add_option('--no-use-superproject', action='store_false',
301 dest='use_superproject',
302 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400303 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400304 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700305 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400306 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400307 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900308 p.add_option('--optimized-fetch',
309 dest='optimized_fetch', action='store_true',
310 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600311 p.add_option('--retry-fetches',
312 default=0, action='store', type='int',
313 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400314 p.add_option('--prune', action='store_true',
315 help='delete refs that no longer exist on the remote (default)')
316 p.add_option('--no-prune', dest='prune', action='store_false',
317 help='do not delete refs that no longer exist on the remote')
LaMont Jones5ed8c632022-11-10 00:10:44 +0000318 p.add_option('--auto-gc', action='store_true', default=None,
LaMont Jones7efab532022-09-01 15:41:12 +0000319 help='run garbage collection on all synced projects')
320 p.add_option('--no-auto-gc', dest='auto_gc', action='store_false',
321 help='do not run garbage collection on any projects (default)')
Nico Sallembien6623b212010-05-11 12:57:01 -0700322 if show_smart:
323 p.add_option('-s', '--smart-sync',
324 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900325 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200326 p.add_option('-t', '--smart-tag',
327 dest='smart_tag', action='store',
328 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700329
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700330 g = p.add_option_group('repo Version options')
331 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500332 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700333 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700334 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800335 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700336 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700337
LaMont Jonesa46047a2022-04-07 21:57:06 +0000338 def _GetBranch(self, manifest_project):
339 """Returns the branch name for getting the approved smartsync manifest.
340
341 Args:
342 manifest_project: the manifestProject to query.
343 """
344 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800345 branch = b.merge
346 if branch.startswith(R_HEADS):
347 branch = branch[len(R_HEADS):]
348 return branch
349
LaMont Jonesa46047a2022-04-07 21:57:06 +0000350 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200351 """Returns whether current-branch or use-superproject options are enabled.
352
LaMont Jonesa46047a2022-04-07 21:57:06 +0000353 Args:
354 opt: Program options returned from optparse. See _Options().
355 manifest: The manifest to use.
356
Daniel Anderssond52ca422022-04-01 12:55:38 +0200357 Returns:
358 True if a superproject is requested, otherwise the value of the
359 current_branch option (True, False or None).
360 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000361 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700362
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000363 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
364 manifest):
365 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800366
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000367 This function updates each project's revisionId with the commit hash from
368 the superproject. It writes the updated manifest into a file and reloads
369 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800370
371 Args:
372 opt: Program options returned from optparse. See _Options().
373 args: Arguments to pass to GetProjects. See the GetProjects
374 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000375 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000376 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800377 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000378 have_superproject = manifest.superproject or any(
379 m.superproject for m in manifest.all_children)
380 if not have_superproject:
381 return
382
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000383 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000384 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700385 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000386 self._ReloadManifest(manifest_path, manifest)
387 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700388
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800389 all_projects = self.GetProjects(args,
390 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000391 submodules_ok=opt.fetch_submodules,
392 manifest=manifest,
393 all_manifests=not opt.this_manifest_only)
394
395 per_manifest = collections.defaultdict(list)
396 manifest_paths = {}
397 if opt.this_manifest_only:
398 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700399 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000400 for p in all_projects:
401 per_manifest[p.manifest.path_prefix].append(p)
402
403 superproject_logging_data = {}
404 need_unload = False
405 for m in self.ManifestList(opt):
406 if not m.path_prefix in per_manifest:
407 continue
408 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
409 if superproject_logging_data:
410 superproject_logging_data['multimanifest'] = True
411 superproject_logging_data.update(
412 superproject=use_super,
413 haslocalmanifests=bool(m.HasLocalManifests),
414 hassuperprojecttag=bool(m.superproject),
415 )
416 if use_super and (m.IsMirror or m.IsArchive):
417 # Don't use superproject, because we have no working tree.
418 use_super = False
419 superproject_logging_data['superproject'] = False
420 superproject_logging_data['noworktree'] = True
421 if opt.use_superproject is not False:
422 print(f'{m.path_prefix}: not using superproject because there is no '
423 'working tree.')
424
425 if not use_super:
426 continue
427 m.superproject.SetQuiet(opt.quiet)
428 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
429 m.superproject.SetPrintMessages(print_messages)
430 update_result = m.superproject.UpdateProjectsRevisionId(
431 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
432 manifest_path = update_result.manifest_path
433 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
434 if manifest_path:
435 m.SetManifestOverride(manifest_path)
436 need_unload = True
437 else:
438 if print_messages:
439 print(f'{m.path_prefix}: warning: Update of revisionId from '
440 'superproject has failed, repo sync will not use superproject '
441 'to fetch the source. ',
442 'Please resync with the --no-use-superproject option to avoid '
443 'this repo warning.',
444 file=sys.stderr)
445 if update_result.fatal and opt.use_superproject is not None:
446 sys.exit(1)
447 if need_unload:
448 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800449
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500450 def _FetchProjectList(self, opt, projects):
451 """Main function of the fetch worker.
452
453 The projects we're given share the same underlying git object store, so we
454 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800455
David James8d201162013-10-11 17:03:19 -0700456 Delegates most of the work to _FetchHelper.
457
458 Args:
459 opt: Program options returned from optparse. See _Options().
460 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700461 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500462 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700463
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500464 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700465 """Fetch git objects for a single project.
466
David Pursehousec1b86a22012-11-14 11:36:51 +0900467 Args:
468 opt: Program options returned from optparse. See _Options().
469 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700470
471 Returns:
472 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900473 """
David Rileye0684ad2017-04-05 00:02:59 -0700474 start = time.time()
475 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500476 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900477 try:
LaMont Jones1eddca82022-09-01 15:15:04 +0000478 sync_result = project.Sync_NetworkHalf(
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500479 quiet=opt.quiet,
480 verbose=opt.verbose,
481 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000482 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500483 force_sync=opt.force_sync,
484 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000485 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500486 optimized_fetch=opt.optimized_fetch,
487 retry_fetches=opt.retry_fetches,
488 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400489 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000490 clone_filter=project.manifest.CloneFilter,
491 partial_clone_exclude=project.manifest.PartialCloneExclude)
LaMont Jones1eddca82022-09-01 15:15:04 +0000492 success = sync_result.success
Doug Andersonfc06ced2011-03-16 15:49:18 -0700493
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500494 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400495 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500496 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700497
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500498 if not success:
499 print('error: Cannot fetch %s from %s'
500 % (project.name, project.remote.url),
501 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700502 except GitError as e:
503 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500504 except Exception as e:
505 print('error: Cannot fetch %s (%s: %s)'
506 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
507 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500508
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500509 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000510 return _FetchOneResult(success, project, start, finish,
511 sync_result.remote_fetched)
David James8d201162013-10-11 17:03:19 -0700512
Mike Frysinger339f2df2021-05-06 00:44:42 -0400513 @classmethod
514 def _FetchInitChild(cls, ssh_proxy):
515 cls.ssh_proxy = ssh_proxy
516
517 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500518 ret = True
519
Mike Frysinger355f4392022-07-20 17:15:29 -0400520 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700521 fetched = set()
LaMont Jones1eddca82022-09-01 15:15:04 +0000522 remote_fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400523 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800524
David James89ece422014-01-09 18:51:58 -0800525 objdir_project_map = dict()
526 for project in projects:
527 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500528 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700529
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500530 def _ProcessResults(results_sets):
531 ret = True
532 for results in results_sets:
LaMont Jones1eddca82022-09-01 15:15:04 +0000533 for result in results:
534 success = result.success
535 project = result.project
536 start = result.start
537 finish = result.finish
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500538 self._fetch_times.Set(project, finish - start)
539 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
540 start, finish, success)
LaMont Jones1eddca82022-09-01 15:15:04 +0000541 if result.remote_fetched:
542 remote_fetched.add(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500543 # Check for any errors before running any more tasks.
544 # ...we'll let existing jobs finish, though.
545 if not success:
546 ret = False
547 else:
548 fetched.add(project.gitdir)
549 pm.update(msg=project.name)
550 if not ret and opt.fail_fast:
551 break
552 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700553
Mike Frysinger339f2df2021-05-06 00:44:42 -0400554 # We pass the ssh proxy settings via the class. This allows multiprocessing
555 # to pickle it up when spawning children. We can't pass it as an argument
556 # to _FetchProjectList below as multiprocessing is unable to pickle those.
557 Sync.ssh_proxy = None
558
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500559 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400560 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400561 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500562 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
563 ret = False
564 else:
565 # Favor throughput over responsiveness when quiet. It seems that imap()
566 # will yield results in batches relative to chunksize, so even as the
567 # children finish a sync, we won't see the result until one child finishes
568 # ~chunksize jobs. When using a large --jobs with large chunksize, this
569 # can be jarring as there will be a large initial delay where repo looks
570 # like it isn't doing anything and sits at 0%, but then suddenly completes
571 # a lot of jobs all at once. Since this code is more network bound, we
572 # can accept a bit more CPU overhead with a smaller chunksize so that the
573 # user sees more immediate & continuous feedback.
574 if opt.quiet:
575 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800576 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500577 pm.update(inc=0, msg='warming up')
578 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800579 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
580 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500581 results = pool.imap_unordered(
582 functools.partial(self._FetchProjectList, opt),
583 projects_list,
584 chunksize=chunksize)
585 if not _ProcessResults(results):
586 ret = False
587 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800588
Mike Frysinger339f2df2021-05-06 00:44:42 -0400589 # Cleanup the reference now that we're done with it, and we're going to
590 # release any resources it points to. If we don't, later multiprocessing
591 # usage (e.g. checkouts) will try to pickle and then crash.
592 del Sync.ssh_proxy
593
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700594 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700595 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700596
LaMont Jonesa46047a2022-04-07 21:57:06 +0000597 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400598 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200599
LaMont Jones1eddca82022-09-01 15:15:04 +0000600 return _FetchResult(ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700601
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000602 def _FetchMain(self, opt, args, all_projects, err_event,
603 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400604 """The main network fetch loop.
605
606 Args:
607 opt: Program options returned from optparse. See _Options().
608 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200609 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400610 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400611 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000612 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200613
614 Returns:
615 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400616 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000617 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400618
619 to_fetch = []
620 now = time.time()
621 if _ONE_DAY_S <= (now - rp.LastFetch):
622 to_fetch.append(rp)
623 to_fetch.extend(all_projects)
624 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
625
LaMont Jones1eddca82022-09-01 15:15:04 +0000626 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
627 success = result.success
628 fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400629 if not success:
630 err_event.set()
631
632 _PostRepoFetch(rp, opt.repo_verify)
633 if opt.network_only:
634 # bail out now; the rest touches the working tree
635 if err_event.is_set():
636 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
637 sys.exit(1)
LaMont Jones1eddca82022-09-01 15:15:04 +0000638 return _FetchMainResult([])
Mike Frysingerb4429432021-05-05 20:03:26 -0400639
640 # Iteratively fetch missing and/or nested unregistered submodules
641 previously_missing_set = set()
642 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000643 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400644 all_projects = self.GetProjects(args,
645 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000646 submodules_ok=opt.fetch_submodules,
647 manifest=manifest,
648 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400649 missing = []
650 for project in all_projects:
651 if project.gitdir not in fetched:
652 missing.append(project)
653 if not missing:
654 break
655 # Stop us from non-stopped fetching actually-missing repos: If set of
656 # missing repos has not been changed from last fetch, we break.
657 missing_set = set(p.name for p in missing)
658 if previously_missing_set == missing_set:
659 break
660 previously_missing_set = missing_set
LaMont Jones1eddca82022-09-01 15:15:04 +0000661 result = self._Fetch(missing, opt, err_event, ssh_proxy)
662 success = result.success
663 new_fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400664 if not success:
665 err_event.set()
666 fetched.update(new_fetched)
667
LaMont Jones1eddca82022-09-01 15:15:04 +0000668 return _FetchMainResult(all_projects)
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200669
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500670 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700671 """Checkout work tree for one project
672
673 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500674 detach_head: Whether to leave a detached HEAD.
675 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700676 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700677
678 Returns:
679 Whether the fetch was successful.
680 """
Xin Li745be2e2019-06-03 11:24:30 -0700681 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000682 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500683 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700684 success = False
685 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500686 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500687 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700688 except GitError as e:
689 print('error.GitError: Cannot checkout %s: %s' %
690 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500691 except Exception as e:
692 print('error: Cannot checkout %s: %s: %s' %
693 (project.name, type(e).__name__, str(e)),
694 file=sys.stderr)
695 raise
Xin Li745be2e2019-06-03 11:24:30 -0700696
Mike Frysingerebf04a42021-02-23 20:48:04 -0500697 if not success:
698 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
699 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000700 return _CheckoutOneResult(success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700701
Mike Frysingerebf04a42021-02-23 20:48:04 -0500702 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700703 """Checkout projects listed in all_projects
704
705 Args:
706 all_projects: List of all projects that should be checked out.
707 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500708 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700709 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500710 # Only checkout projects with worktrees.
711 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700712
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500713 def _ProcessResults(pool, pm, results):
714 ret = True
LaMont Jones1eddca82022-09-01 15:15:04 +0000715 for result in results:
716 success = result.success
717 project = result.project
718 start = result.start
719 finish = result.finish
Mike Frysingerebf04a42021-02-23 20:48:04 -0500720 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
721 start, finish, success)
722 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500723 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500724 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500725 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500726 err_results.append(project.relpath)
727 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500728 if pool:
729 pool.close()
730 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500731 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500732 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700733
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500734 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400735 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500736 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
737 all_projects,
738 callback=_ProcessResults,
739 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500740
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000741 def _backup_cruft(self, bare_git):
742 """Save a copy of any cruft from `git gc`."""
743 # Find any cruft packs in the current gitdir, and save them.
744 # b/221065125 (repo sync complains that objects are missing). This does
745 # not prevent that state, but makes it so that the missing objects are
746 # available.
747 objdir = bare_git._project.objdir
748 pack_dir = os.path.join(objdir, 'pack')
749 bak_dir = os.path.join(objdir, '.repo', 'pack.bak')
750 if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir):
751 return
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000752 files = set(platform_utils.listdir(pack_dir))
753 to_backup = []
754 for f in files:
755 base, ext = os.path.splitext(f)
756 if base + '.mtimes' in files:
757 to_backup.append(f)
758 if to_backup:
759 os.makedirs(bak_dir, exist_ok=True)
760 for fname in to_backup:
761 bak_fname = os.path.join(bak_dir, fname)
762 if not os.path.exists(bak_fname):
Joanna Wanga6c52f52022-11-03 16:51:19 -0400763 with Trace('%s saved %s', bare_git._project.name, fname):
764 # Use a tmp file so that we are sure of a complete copy.
765 shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp')
766 shutil.move(bak_fname + '.tmp', bak_fname)
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000767
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000768 @staticmethod
769 def _GetPreciousObjectsState(project: Project, opt):
770 """Get the preciousObjects state for the project.
771
772 Args:
773 project (Project): the project to examine, and possibly correct.
774 opt (optparse.Values): options given to sync.
775
776 Returns:
777 Expected state of extensions.preciousObjects:
778 False: Should be disabled. (not present)
779 True: Should be enabled.
780 """
781 if project.use_git_worktrees:
782 return False
783 projects = project.manifest.GetProjectsWithName(project.name,
784 all_manifests=True)
785 if len(projects) == 1:
786 return False
787 relpath = project.RelPath(local=opt.this_manifest_only)
788 if len(projects) > 1:
789 # Objects are potentially shared with another project.
790 # See the logic in Project.Sync_NetworkHalf regarding UseAlternates.
791 # - When False, shared projects share (via symlink)
792 # .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects
793 # directory. All objects are precious, since there is no project with a
794 # complete set of refs.
795 # - When True, shared projects share (via info/alternates)
796 # .repo/project-objects/{PROJECT_NAME}.git as an alternate object store,
797 # which is written only on the first clone of the project, and is not
798 # written subsequently. (When Sync_NetworkHalf sees that it exists, it
799 # makes sure that the alternates file points there, and uses a
800 # project-local .git/objects directory for all syncs going forward.
801 # We do not support switching between the options. The environment
802 # variable is present for testing and migration only.
803 return not project.UseAlternates
804 print(f'\r{relpath}: project not found in manifest.', file=sys.stderr)
805 return False
806
807 def _RepairPreciousObjectsState(self, project: Project, opt):
808 """Correct the preciousObjects state for the project.
809
810 Args:
811 project (Project): the project to examine, and possibly correct.
812 opt (optparse.Values): options given to sync.
813 """
814 expected = self._GetPreciousObjectsState(project, opt)
815 actual = project.config.GetBoolean('extensions.preciousObjects') or False
816 relpath = project.RelPath(local = opt.this_manifest_only)
817
818 if (expected != actual and
819 not project.config.GetBoolean('repo.preservePreciousObjects')):
820 # If this is unexpected, log it and repair.
821 Trace(f'{relpath} expected preciousObjects={expected}, got {actual}')
822 if expected:
823 if not opt.quiet:
824 print('\r%s: Shared project %s found, disabling pruning.' %
825 (relpath, project.name))
826 if git_require((2, 7, 0)):
827 project.EnableRepositoryExtension('preciousObjects')
828 else:
829 # This isn't perfect, but it's the best we can do with old git.
830 print('\r%s: WARNING: shared projects are unreliable when using '
831 'old versions of git; please upgrade to git-2.7.0+.'
832 % (relpath,),
833 file=sys.stderr)
834 project.config.SetString('gc.pruneExpire', 'never')
835 else:
836 if not opt.quiet:
837 print(f'\r{relpath}: not shared, disabling pruning.')
838 project.config.SetString('extensions.preciousObjects', None)
839 project.config.SetString('gc.pruneExpire', None)
840
Mike Frysinger5a033082019-09-23 19:21:20 -0400841 def _GCProjects(self, projects, opt, err_event):
LaMont Jones7efab532022-09-01 15:41:12 +0000842 """Perform garbage collection.
843
844 If We are skipping garbage collection (opt.auto_gc not set), we still want
845 to potentially mark objects precious, so that `git gc` does not discard
846 shared objects.
847 """
848 pm = Progress(f'{"" if opt.auto_gc else "NOT "}Garbage collecting',
849 len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400850 pm.update(inc=0, msg='prescan')
851
Allen Webb4ee4a452021-10-07 10:42:38 -0500852 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700853 for project in projects:
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000854 self._RepairPreciousObjectsState(project, opt)
855
Allen Webb669efd02021-10-01 15:25:31 -0500856 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500857 # Only call git gc once per objdir, but call pack-refs for the remainder.
858 if project.objdir not in tidy_dirs:
859 tidy_dirs[project.objdir] = (
860 True, # Run a full gc.
861 project.bare_git,
862 )
863 elif project.gitdir not in tidy_dirs:
864 tidy_dirs[project.gitdir] = (
865 False, # Do not run a full gc; just run pack-refs.
866 project.bare_git,
867 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400868
LaMont Jones7efab532022-09-01 15:41:12 +0000869 if not opt.auto_gc:
870 pm.end()
871 return
872
Mike Frysinger355f4392022-07-20 17:15:29 -0400873 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700874
LaMont Jonesacc4c852022-09-22 19:05:01 +0000875 gc_args = ['--auto']
876 backup_cruft = False
877 if git_require((2, 37, 0)):
878 gc_args.append('--cruft')
879 backup_cruft = True
LaMont Jones891e8f72022-09-08 20:17:58 +0000880 pack_refs_args = ()
Dave Borowitz18857212012-10-23 17:02:59 -0700881 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500882 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400883 pm.update(msg=bare_git._project.name)
LaMont Jones891e8f72022-09-08 20:17:58 +0000884
Allen Webb4ee4a452021-10-07 10:42:38 -0500885 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000886 bare_git.gc(*gc_args)
Allen Webb4ee4a452021-10-07 10:42:38 -0500887 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000888 bare_git.pack_refs(*pack_refs_args)
LaMont Jonesacc4c852022-09-22 19:05:01 +0000889 if backup_cruft:
890 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400891 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700892 return
893
Mike Frysinger355f4392022-07-20 17:15:29 -0400894 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400895 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700896
897 threads = set()
898 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700899
Allen Webb4ee4a452021-10-07 10:42:38 -0500900 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400901 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700902 try:
903 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500904 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000905 bare_git.gc(*gc_args, config=config)
Allen Webb4ee4a452021-10-07 10:42:38 -0500906 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000907 bare_git.pack_refs(*pack_refs_args, config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700908 except GitError:
909 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900910 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700911 err_event.set()
912 raise
913 finally:
LaMont Jonesacc4c852022-09-22 19:05:01 +0000914 if backup_cruft:
915 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400916 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700917 sem.release()
918
Allen Webb4ee4a452021-10-07 10:42:38 -0500919 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500920 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700921 break
922 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500923 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700924 t.daemon = True
925 threads.add(t)
926 t.start()
927
928 for t in threads:
929 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400930 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700931
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000932 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700933 """Reload the manfiest from the file specified by the |manifest_name|.
934
935 It unloads the manifest if |manifest_name| is None.
936
937 Args:
938 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000939 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700940 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800941 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000942 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000943 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800944 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000945 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800946
LaMont Jonesa46047a2022-04-07 21:57:06 +0000947 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000948 """Update the cached projects list for |manifest|
949
950 In a multi-manifest checkout, each manifest has its own project.list.
951
952 Args:
953 opt: Program options returned from optparse. See _Options().
954 manifest: The manifest to use.
955
956 Returns:
957 0: success
958 1: failure
959 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700960 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000961 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
962 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700963 if project.relpath:
964 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700965 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000966 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700967 old_project_paths = []
968
969 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500970 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700971 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800972 # In reversed order, so subfolders are deleted before parent folder.
973 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700974 if not path:
975 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700976 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900977 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000978 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700979 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900980 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000981 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900982 name=path,
983 remote=RemoteSpec('origin'),
984 gitdir=gitdir,
985 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500986 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000987 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900988 relpath=path,
989 revisionExpr='HEAD',
990 revisionId=None,
991 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500992 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900993 quiet=opt.quiet,
994 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400995 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700996
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700997 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500998 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700999 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -07001000 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001001 return 0
1002
LaMont Jonesa46047a2022-04-07 21:57:06 +00001003 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +08001004 """Save all dests of copyfile and linkfile, and update them if needed.
1005
1006 Returns:
1007 Whether update was successful.
1008 """
1009 new_paths = {}
1010 new_linkfile_paths = []
1011 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001012 for project in self.GetProjects(None, missing_ok=True,
1013 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +08001014 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
1015 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
1016
1017 new_paths = {
1018 'linkfile': new_linkfile_paths,
1019 'copyfile': new_copyfile_paths,
1020 }
1021
1022 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001023 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +08001024 old_copylinkfile_paths = {}
1025
1026 if os.path.exists(copylinkfile_path):
1027 with open(copylinkfile_path, 'rb') as fp:
1028 try:
1029 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001030 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +08001031 print('error: %s is not a json formatted file.' %
1032 copylinkfile_path, file=sys.stderr)
1033 platform_utils.remove(copylinkfile_path)
1034 return False
1035
1036 need_remove_files = []
1037 need_remove_files.extend(
1038 set(old_copylinkfile_paths.get('linkfile', [])) -
1039 set(new_linkfile_paths))
1040 need_remove_files.extend(
1041 set(old_copylinkfile_paths.get('copyfile', [])) -
1042 set(new_copyfile_paths))
1043
1044 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -04001045 # Try to remove the updated copyfile or linkfile.
1046 # So, if the file is not exist, nothing need to do.
1047 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +08001048
1049 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
1050 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
1051 json.dump(new_paths, fp)
1052 return True
1053
LaMont Jonesa46047a2022-04-07 21:57:06 +00001054 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1055 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001056 print('error: cannot smart sync: no manifest server defined in '
1057 'manifest', file=sys.stderr)
1058 sys.exit(1)
1059
LaMont Jonesa46047a2022-04-07 21:57:06 +00001060 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001061 if not opt.quiet:
1062 print('Using manifest server %s' % manifest_server)
1063
David Pursehouseeeff3532020-02-12 11:24:10 +09001064 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001065 username = None
1066 password = None
1067 if opt.manifest_server_username and opt.manifest_server_password:
1068 username = opt.manifest_server_username
1069 password = opt.manifest_server_password
1070 else:
1071 try:
1072 info = netrc.netrc()
1073 except IOError:
1074 # .netrc file does not exist or could not be opened
1075 pass
1076 else:
1077 try:
1078 parse_result = urllib.parse.urlparse(manifest_server)
1079 if parse_result.hostname:
1080 auth = info.authenticators(parse_result.hostname)
1081 if auth:
1082 username, _account, password = auth
1083 else:
1084 print('No credentials found for %s in .netrc'
1085 % parse_result.hostname, file=sys.stderr)
1086 except netrc.NetrcParseError as e:
1087 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
1088
1089 if (username and password):
1090 manifest_server = manifest_server.replace('://', '://%s:%s@' %
1091 (username, password),
1092 1)
1093
1094 transport = PersistentTransport(manifest_server)
1095 if manifest_server.startswith('persistent-'):
1096 manifest_server = manifest_server[len('persistent-'):]
1097
1098 try:
1099 server = xmlrpc.client.Server(manifest_server, transport=transport)
1100 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001101 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001102
Mike Frysinger56ce3462019-12-04 19:30:48 -05001103 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -05001104 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001105 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -05001106 elif ('TARGET_PRODUCT' in os.environ and
1107 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -05001108 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
1109 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001110 [success, manifest_str] = server.GetApprovedManifest(branch, target)
1111 else:
1112 [success, manifest_str] = server.GetApprovedManifest(branch)
1113 else:
1114 assert(opt.smart_tag)
1115 [success, manifest_str] = server.GetManifest(opt.smart_tag)
1116
1117 if success:
1118 manifest_name = os.path.basename(smart_sync_manifest_path)
1119 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001120 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001121 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001122 except IOError as e:
1123 print('error: cannot write manifest to %s:\n%s'
1124 % (smart_sync_manifest_path, e),
1125 file=sys.stderr)
1126 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001127 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001128 else:
1129 print('error: manifest server RPC call failed: %s' %
1130 manifest_str, file=sys.stderr)
1131 sys.exit(1)
1132 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1133 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001134 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001135 sys.exit(1)
1136 except xmlrpc.client.ProtocolError as e:
1137 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001138 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001139 file=sys.stderr)
1140 sys.exit(1)
1141
1142 return manifest_name
1143
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001144 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
1145 """Fetch & update the local manifest project.
1146
1147 After syncing the manifest project, if the manifest has any sub manifests,
1148 those are recursively processed.
1149
1150 Args:
1151 opt: Program options returned from optparse. See _Options().
1152 mp: the manifestProject to query.
1153 manifest_name: Manifest file to be reloaded.
1154 """
1155 if not mp.standalone_manifest_url:
1156 self._UpdateManifestProject(opt, mp, manifest_name)
1157
1158 if mp.manifest.submanifests:
1159 for submanifest in mp.manifest.submanifests.values():
1160 child = submanifest.repo_client.manifest
1161 child.manifestProject.SyncWithPossibleInit(
1162 submanifest,
1163 current_branch_only=self._GetCurrentBranchOnly(opt, child),
1164 verbose=opt.verbose,
1165 tags=opt.tags,
1166 git_event_log=self.git_event_log,
1167 )
1168 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
1169
Mike Frysingerfb527e32019-08-27 02:34:32 -04001170 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001171 """Fetch & update the local manifest project.
1172
1173 Args:
1174 opt: Program options returned from optparse. See _Options().
1175 mp: the manifestProject to query.
1176 manifest_name: Manifest file to be reloaded.
1177 """
Mike Frysingerfb527e32019-08-27 02:34:32 -04001178 if not opt.local_only:
1179 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -05001180 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001181 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +02001182 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001183 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -04001184 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001185 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001186 submodules=mp.manifest.HasSubmodules,
1187 clone_filter=mp.manifest.CloneFilter,
1188 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001189 finish = time.time()
1190 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1191 start, finish, success)
1192
1193 if mp.HasChanges:
1194 syncbuf = SyncBuffer(mp.config)
1195 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001196 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001197 clean = syncbuf.Finish()
1198 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1199 start, time.time(), clean)
1200 if not clean:
1201 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001202 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001203
Mike Frysingerae6cb082019-08-27 01:10:59 -04001204 def ValidateOptions(self, opt, args):
1205 if opt.force_broken:
1206 print('warning: -f/--force-broken is now the default behavior, and the '
1207 'options are deprecated', file=sys.stderr)
1208 if opt.network_only and opt.detach_head:
1209 self.OptionParser.error('cannot combine -n and -d')
1210 if opt.network_only and opt.local_only:
1211 self.OptionParser.error('cannot combine -n and -l')
1212 if opt.manifest_name and opt.smart_sync:
1213 self.OptionParser.error('cannot combine -m and -s')
1214 if opt.manifest_name and opt.smart_tag:
1215 self.OptionParser.error('cannot combine -m and -t')
1216 if opt.manifest_server_username or opt.manifest_server_password:
1217 if not (opt.smart_sync or opt.smart_tag):
1218 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1219 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1220 self.OptionParser.error('both -u and -p must be given')
1221
Mike Frysinger0531a622021-11-05 15:22:01 -04001222 if opt.prune is None:
1223 opt.prune = True
1224
LaMont Jones5ed8c632022-11-10 00:10:44 +00001225 if opt.auto_gc is None and _AUTO_GC:
1226 print(f"Will run `git gc --auto` because {_REPO_AUTO_GC} is set.",
1227 file=sys.stderr)
1228 opt.auto_gc = True
1229
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001230 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001231 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001232 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001233 manifest = self.manifest
1234
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001235 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001236 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001237
Chirayu Desaia892b102013-06-11 14:18:46 +05301238 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001239 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001240 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301241
Xin Lid79a4bc2020-05-20 16:03:45 -07001242 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001243 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001244
Victor Boivie08c880d2011-04-19 10:32:52 +02001245 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001246 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001247 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001248 if os.path.isfile(smart_sync_manifest_path):
1249 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001250 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001251 except OSError as e:
1252 print('error: failed to remove existing smart sync override manifest: %s' %
1253 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001254
Mike Frysingerc99322a2021-05-04 15:32:43 -04001255 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001256
LaMont Jonesa46047a2022-04-07 21:57:06 +00001257 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001258 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001259 cb = rp.CurrentBranch
1260 if cb:
1261 base = rp.GetBranch(cb).merge
1262 if not base or not base.startswith('refs/heads/'):
1263 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001264 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001265 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001266
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001267 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001268 if not m.manifestProject.standalone_manifest_url:
1269 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001270
LaMont Jones4112c072022-08-24 17:32:25 +00001271 if opt.repo_upgraded:
1272 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001273
LaMont Jones4112c072022-08-24 17:32:25 +00001274 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001275 if opt.mp_update:
1276 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1277 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001278 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001279
Mike Frysinger355f4392022-07-20 17:15:29 -04001280 # Now that the manifests are up-to-date, setup the jobs value.
1281 if opt.jobs is None:
1282 # User has not made a choice, so use the manifest settings.
1283 opt.jobs = mp.default.sync_j
1284 if opt.jobs is not None:
1285 # Neither user nor manifest have made a choice.
1286 if opt.jobs_network is None:
1287 opt.jobs_network = opt.jobs
1288 if opt.jobs_checkout is None:
1289 opt.jobs_checkout = opt.jobs
1290 # Setup defaults if jobs==0.
1291 if not opt.jobs:
1292 if not opt.jobs_network:
1293 opt.jobs_network = 1
1294 if not opt.jobs_checkout:
1295 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1296 opt.jobs = os.cpu_count()
1297
1298 # Try to stay under user rlimit settings.
1299 #
1300 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1301 # that to scale down the number of jobs. Unfortunately there isn't an easy
1302 # way to determine this reliably as systems change, but it was last measured
1303 # by hand in 2011.
1304 soft_limit, _ = _rlimit_nofile()
1305 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1306 opt.jobs = min(opt.jobs, jobs_soft_limit)
1307 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1308 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1309
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001310 superproject_logging_data = {}
1311 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1312 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001313
Simran Basib9a1b732015-08-20 12:19:28 -07001314 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001315 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001316 gitc_projects = []
1317 opened_projects = []
1318 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001319 if project.relpath in self.gitc_manifest.paths and \
1320 self.gitc_manifest.paths[project.relpath].old_revision:
1321 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001322 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001323 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001324
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001325 if not args:
1326 gitc_projects = None
1327
1328 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001329 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001330 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1331 if manifest_name:
1332 manifest.Override(manifest_name)
1333 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001334 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001335 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1336 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001337 gitc_projects)
1338 print('GITC client successfully synced.')
1339
1340 # The opened projects need to be synced as normal, therefore we
1341 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001342 # TODO: make this more reliable -- if there's a project name/path overlap,
1343 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001344 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001345 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001346 if not args:
1347 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001348
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001349 all_projects = self.GetProjects(args,
1350 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001351 submodules_ok=opt.fetch_submodules,
1352 manifest=manifest,
1353 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001354
Mike Frysinger5a033082019-09-23 19:21:20 -04001355 err_network_sync = False
1356 err_update_projects = False
LaMont Jonesb6cfa092022-10-26 16:34:40 +00001357 err_update_linkfiles = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001358
LaMont Jonesa46047a2022-04-07 21:57:06 +00001359 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001360 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001361 with multiprocessing.Manager() as manager:
1362 with ssh.ProxyManager(manager) as ssh_proxy:
1363 # Initialize the socket dir once in the parent.
1364 ssh_proxy.sock()
LaMont Jones1eddca82022-09-01 15:15:04 +00001365 result = self._FetchMain(opt, args, all_projects, err_event,
1366 ssh_proxy, manifest)
1367 all_projects = result.all_projects
Mike Frysinger339f2df2021-05-06 00:44:42 -04001368
1369 if opt.network_only:
1370 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001371
1372 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001373 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001374 err_network_sync = True
1375 if opt.fail_fast:
1376 print('\nerror: Exited sync due to fetch errors.\n'
1377 'Local checkouts *not* updated. Resolve network issues & '
1378 'retry.\n'
1379 '`repo sync -l` will update some local checkouts.',
1380 file=sys.stderr)
1381 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001382
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001383 for m in self.ManifestList(opt):
1384 if m.IsMirror or m.IsArchive:
1385 # bail out now, we have no working tree
1386 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001387
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001388 if self.UpdateProjectList(opt, m):
1389 err_event.set()
1390 err_update_projects = True
1391 if opt.fail_fast:
1392 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1393 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001394
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001395 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1396 if err_update_linkfiles:
1397 err_event.set()
1398 if opt.fail_fast:
1399 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1400 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001401
Mike Frysinger5a033082019-09-23 19:21:20 -04001402 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001403 # NB: We don't exit here because this is the last step.
1404 err_checkout = not self._Checkout(all_projects, opt, err_results)
1405 if err_checkout:
1406 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001407
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001408 printed_notices = set()
1409 # If there's a notice that's supposed to print at the end of the sync,
1410 # print it now... But avoid printing duplicate messages, and preserve
1411 # order.
1412 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1413 if m.notice and m.notice not in printed_notices:
1414 print(m.notice)
1415 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001416
Mike Frysinger5a033082019-09-23 19:21:20 -04001417 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001418 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001419 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1420 if err_network_sync:
1421 print('error: Downloading network changes failed.', file=sys.stderr)
1422 if err_update_projects:
1423 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001424 if err_update_linkfiles:
1425 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001426 if err_checkout:
1427 print('error: Checking out local projects failed.', file=sys.stderr)
1428 if err_results:
1429 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1430 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1431 file=sys.stderr)
1432 sys.exit(1)
1433
Raman Tenneti7954de12021-07-28 14:36:49 -07001434 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001435 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1436 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001437
1438 # Update and log with the new sync analysis state.
1439 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001440 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1441 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001442
Mike Frysingere19d9e12020-02-12 11:23:32 -05001443 if not opt.quiet:
1444 print('repo sync has finished successfully.')
1445
David Pursehouse819827a2020-02-12 15:20:19 +09001446
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001447def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001448 # Link the docs for the internal .repo/ layout for people
1449 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1450 if not platform_utils.islink(link):
1451 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1452 try:
1453 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001454 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001455 pass
1456
Conley Owens094cdbe2014-01-30 15:09:59 -08001457 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001458 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001459 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001460 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001461 if project.Exists:
1462 project.PostRepoUpgrade()
1463
David Pursehouse819827a2020-02-12 15:20:19 +09001464
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001465def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001466 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001467 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001468 wrapper = Wrapper()
1469 try:
1470 rev = rp.bare_git.describe(rp.GetRevisionId())
1471 except GitError:
1472 rev = None
1473 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1474 # See if we're held back due to missing signed tag.
1475 current_revid = rp.bare_git.rev_parse('HEAD')
1476 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1477 if current_revid != new_revid:
1478 # We want to switch to the new rev, but also not trash any uncommitted
1479 # changes. This helps with local testing/hacking.
1480 # If a local change has been made, we will throw that away.
1481 # We also have to make sure this will switch to an older commit if that's
1482 # the latest tag in order to support release rollback.
1483 try:
1484 rp.work_git.reset('--keep', new_rev)
1485 except GitError as e:
1486 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001487 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001488 raise RepoChangedException(['--repo-upgraded'])
1489 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001490 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001491 else:
1492 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001493 print('repo version %s is current' % rp.work_git.describe(HEAD),
1494 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001495
David Pursehouse819827a2020-02-12 15:20:19 +09001496
Dave Borowitz67700e92012-10-23 15:00:54 -07001497class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001498 _ALPHA = 0.5
1499
Dave Borowitz67700e92012-10-23 15:00:54 -07001500 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001501 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001502 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001503 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001504
1505 def Get(self, project):
1506 self._Load()
1507 return self._times.get(project.name, _ONE_DAY_S)
1508
1509 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001510 self._Load()
1511 name = project.name
1512 old = self._times.get(name, t)
1513 self._seen.add(name)
1514 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001515 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001516
1517 def _Load(self):
1518 if self._times is None:
1519 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001520 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001521 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001522 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001523 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001524 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001525
1526 def Save(self):
1527 if self._times is None:
1528 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001529
1530 to_delete = []
1531 for name in self._times:
1532 if name not in self._seen:
1533 to_delete.append(name)
1534 for name in to_delete:
1535 del self._times[name]
1536
Dave Borowitz67700e92012-10-23 15:00:54 -07001537 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001538 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001539 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001540 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001541 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001542
1543# This is a replacement for xmlrpc.client.Transport using urllib2
1544# and supporting persistent-http[s]. It cannot change hosts from
1545# request to request like the normal transport, the real url
1546# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001547
1548
Dan Willemsen0745bb22015-08-17 13:41:45 -07001549class PersistentTransport(xmlrpc.client.Transport):
1550 def __init__(self, orig_host):
1551 self.orig_host = orig_host
1552
1553 def request(self, host, handler, request_body, verbose=False):
1554 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1555 # Python doesn't understand cookies with the #HttpOnly_ prefix
1556 # Since we're only using them for HTTP, copy the file temporarily,
1557 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001558 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001559 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001560 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001561 try:
1562 with open(cookiefile) as f:
1563 for line in f:
1564 if line.startswith("#HttpOnly_"):
1565 line = line[len("#HttpOnly_"):]
1566 tmpcookiefile.write(line)
1567 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001568
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001569 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001570 try:
1571 cookiejar.load()
1572 except cookielib.LoadError:
1573 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001574 finally:
1575 tmpcookiefile.close()
1576 else:
1577 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001578
1579 proxyhandler = urllib.request.ProxyHandler
1580 if proxy:
1581 proxyhandler = urllib.request.ProxyHandler({
1582 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001583 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001584
1585 opener = urllib.request.build_opener(
1586 urllib.request.HTTPCookieProcessor(cookiejar),
1587 proxyhandler)
1588
1589 url = urllib.parse.urljoin(self.orig_host, handler)
1590 parse_results = urllib.parse.urlparse(url)
1591
1592 scheme = parse_results.scheme
1593 if scheme == 'persistent-http':
1594 scheme = 'http'
1595 if scheme == 'persistent-https':
1596 # If we're proxying through persistent-https, use http. The
1597 # proxy itself will do the https.
1598 if proxy:
1599 scheme = 'http'
1600 else:
1601 scheme = 'https'
1602
1603 # Parse out any authentication information using the base class
1604 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1605
1606 url = urllib.parse.urlunparse((
1607 scheme,
1608 host,
1609 parse_results.path,
1610 parse_results.params,
1611 parse_results.query,
1612 parse_results.fragment))
1613
1614 request = urllib.request.Request(url, request_body)
1615 if extra_headers is not None:
1616 for (name, header) in extra_headers:
1617 request.add_header(name, header)
1618 request.add_header('Content-Type', 'text/xml')
1619 try:
1620 response = opener.open(request)
1621 except urllib.error.HTTPError as e:
1622 if e.code == 501:
1623 # We may have been redirected through a login process
1624 # but our POST turned into a GET. Retry.
1625 response = opener.open(request)
1626 else:
1627 raise
1628
1629 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001630 # Response should be fairly small, so read it all at once.
1631 # This way we can show it to the user in case of error (e.g. HTML).
1632 data = response.read()
1633 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001634 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001635 except xml.parsers.expat.ExpatError as e:
1636 raise IOError(
1637 f'Parsing the manifest failed: {e}\n'
1638 f'Please report this to your manifest server admin.\n'
1639 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001640 p.close()
1641 return u.close()
1642
1643 def close(self):
1644 pass