blob: 5b41046dc0a045b334b47fe1a4db38345478ee27 [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 Jonesd7935532022-12-01 20:18:46 +000073# Env var to implicitly turn auto-gc back on. This was added to allow a user to
74# revert a change in default behavior in v2.29.9, and will be removed in a
75# future release.
LaMont Jones5ed8c632022-11-10 00:10:44 +000076_REPO_AUTO_GC = 'REPO_AUTO_GC'
77_AUTO_GC = os.environ.get(_REPO_AUTO_GC) == '1'
78
David Pursehouse819827a2020-02-12 15:20:19 +090079
LaMont Jones1eddca82022-09-01 15:15:04 +000080class _FetchOneResult(NamedTuple):
81 """_FetchOne return value.
82
83 Attributes:
84 success (bool): True if successful.
85 project (Project): The fetched project.
86 start (float): The starting time.time().
87 finish (float): The ending time.time().
88 remote_fetched (bool): True if the remote was actually queried.
89 """
90 success: bool
91 project: Project
92 start: float
93 finish: float
94 remote_fetched: bool
95
96
97class _FetchResult(NamedTuple):
98 """_Fetch return value.
99
100 Attributes:
101 success (bool): True if successful.
LaMont Jones78dcd372022-10-25 22:38:07 +0000102 projects (Set[str]): The names of the git directories of fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000103 """
104 success: bool
LaMont Jones78dcd372022-10-25 22:38:07 +0000105 projects: Set[str]
LaMont Jones1eddca82022-09-01 15:15:04 +0000106
107
108class _FetchMainResult(NamedTuple):
109 """_FetchMain return value.
110
111 Attributes:
LaMont Jones78dcd372022-10-25 22:38:07 +0000112 all_projects (List[Project]): The fetched projects.
LaMont Jones1eddca82022-09-01 15:15:04 +0000113 """
LaMont Jones78dcd372022-10-25 22:38:07 +0000114 all_projects: List[Project]
LaMont Jones1eddca82022-09-01 15:15:04 +0000115
116
117class _CheckoutOneResult(NamedTuple):
118 """_CheckoutOne return value.
119
120 Attributes:
121 success (bool): True if successful.
122 project (Project): The project.
123 start (float): The starting time.time().
124 finish (float): The ending time.time().
125 """
126 success: bool
127 project: Project
128 start: float
129 finish: float
130
131
Shawn O. Pearcec95583b2009-03-03 17:47:06 -0800132class Sync(Command, MirrorSafeCommand):
Mike Frysinger4f210542021-06-14 16:05:19 -0400133 COMMON = True
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000134 MULTI_MANIFEST_SUPPORT = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135 helpSummary = "Update working tree to the latest revision"
136 helpUsage = """
137%prog [<project>...]
138"""
139 helpDescription = """
140The '%prog' command synchronizes local project directories
141with the remote repositories specified in the manifest. If a local
142project does not yet exist, it will clone a new local directory from
143the remote repository and set up tracking branches as specified in
144the manifest. If the local project already exists, '%prog'
145will update the remote branches and rebase any new local changes
146on top of the new remote changes.
147
148'%prog' will synchronize all projects listed at the command
149line. Projects can be specified either by name, or by a relative
150or absolute path to the project's local directory. If no projects
151are specified, '%prog' will synchronize all projects listed in
152the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700153
154The -d/--detach option can be used to switch specified projects
155back to the manifest revision. This option is especially helpful
156if the project is currently on a topic branch, but the manifest
157revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700158
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700159The -s/--smart-sync option can be used to sync to a known good
160build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200161manifest. The -t/--smart-tag option is similar and allows you to
162specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700163
David Pursehousecf76b1b2012-09-14 10:31:42 +0900164The -u/--manifest-server-username and -p/--manifest-server-password
165options can be used to specify a username and password to authenticate
166with the manifest server when using the -s or -t option.
167
168If -u and -p are not specified when using the -s or -t option, '%prog'
169will attempt to read authentication credentials for the manifest server
170from the user's .netrc file.
171
172'%prog' will not use authentication credentials from -u/-p or .netrc
173if the manifest server specified in the manifest file already includes
174credentials.
175
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400176By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400177to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500178
Kevin Degiabaa7f32014-11-12 11:27:45 -0700179The --force-sync option can be used to overwrite existing git
180directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900181object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700182refs may be removed when overwriting.
183
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500184The --force-remove-dirty option can be used to remove previously used
185projects with uncommitted changes. WARNING: This may cause data to be
186lost since uncommitted changes may be removed with projects that no longer
187exist in the manifest.
188
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700189The --no-clone-bundle option disables any attempt to use
190$URL/clone.bundle to bootstrap a new Git repository from a
191resumeable bundle file on a content delivery network. This
192may be necessary if there are problems with the local Python
193HTTP client or proxy configuration, but the Git binary works.
194
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800195The --fetch-submodules option enables fetching Git submodules
196of a project from server.
197
David Pursehousef2fad612015-01-29 14:36:28 +0900198The -c/--current-branch option can be used to only fetch objects that
199are on the branch specified by a project's revision.
200
David Pursehouseb1553542014-09-04 21:28:09 +0900201The --optimized-fetch option can be used to only fetch projects that
202are fixed to a sha1 revision if the sha1 revision does not already
203exist locally.
204
David Pursehouse74cfd272015-10-14 10:50:15 +0900205The --prune option can be used to remove any refs that no longer
206exist on the remote.
207
LaMont Jones7efab532022-09-01 15:41:12 +0000208The --auto-gc option can be used to trigger garbage collection on all
209projects. By default, repo does not run garbage collection.
210
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400211# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700212
213If at least one project remote URL uses an SSH connection (ssh://,
214git+ssh://, or user@host:path syntax) repo will automatically
215enable the SSH ControlMaster option when connecting to that host.
216This feature permits other projects in the same '%prog' session to
217reuse the same SSH tunnel, saving connection setup overheads.
218
219To disable this behavior on UNIX platforms, set the GIT_SSH
220environment variable to 'ssh'. For example:
221
222 export GIT_SSH=ssh
223 %prog
224
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400225# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700226
227This feature is automatically disabled on Windows, due to the lack
228of UNIX domain socket support.
229
230This feature is not compatible with url.insteadof rewrites in the
231user's ~/.gitconfig. '%prog' is currently not able to perform the
232rewrite early enough to establish the ControlMaster tunnel.
233
234If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
235later is required to fix a server side protocol bug.
236
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237"""
Mike Frysinger355f4392022-07-20 17:15:29 -0400238 # A value of 0 means we want parallel jobs, but we'll determine the default
239 # value later on.
240 PARALLEL_JOBS = 0
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700241
Mike Frysinger9180a072021-04-13 14:57:40 -0400242 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400243 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400244 help='number of network jobs to run in parallel (defaults to --jobs or 1)')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400245 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
Mike Frysinger355f4392022-07-20 17:15:29 -0400246 help='number of local checkout jobs to run in parallel (defaults to --jobs or '
247 f'{DEFAULT_LOCAL_JOBS})')
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400248
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500249 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200250 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400251 help='obsolete option (to be deleted in the future)')
252 p.add_option('--fail-fast',
253 dest='fail_fast', action='store_true',
254 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700255 p.add_option('--force-sync',
256 dest='force_sync', action='store_true',
257 help="overwrite an existing git directory if it needs to "
258 "point to a different object directory. WARNING: this "
259 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500260 p.add_option('--force-remove-dirty',
261 dest='force_remove_dirty', action='store_true',
262 help="force remove projects with uncommitted modifications if "
263 "projects no longer exist in the manifest. "
264 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900265 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700266 dest='local_only', action='store_true',
267 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900268 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100269 dest='mp_update', action='store_false', default='true',
270 help='use the existing manifest checkout as-is. '
271 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900272 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700273 dest='network_only', action='store_true',
274 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900275 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700276 dest='detach_head', action='store_true',
277 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900278 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700279 dest='current_branch_only', action='store_true',
280 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400281 p.add_option('--no-current-branch',
282 dest='current_branch_only', action='store_false',
283 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500284 p.add_option('-m', '--manifest-name',
285 dest='manifest_name',
286 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700287 p.add_option('--clone-bundle', action='store_true',
288 help='enable use of /clone.bundle on HTTP/HTTPS')
289 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700290 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800291 p.add_option('-u', '--manifest-server-username', action='store',
292 dest='manifest_server_username',
293 help='username to authenticate with the manifest server')
294 p.add_option('-p', '--manifest-server-password', action='store',
295 dest='manifest_server_password',
296 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800297 p.add_option('--fetch-submodules',
298 dest='fetch_submodules', action='store_true',
299 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800300 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700301 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700302 p.add_option('--no-use-superproject', action='store_false',
303 dest='use_superproject',
304 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400305 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400306 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700307 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400308 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400309 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900310 p.add_option('--optimized-fetch',
311 dest='optimized_fetch', action='store_true',
312 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600313 p.add_option('--retry-fetches',
314 default=0, action='store', type='int',
315 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400316 p.add_option('--prune', action='store_true',
317 help='delete refs that no longer exist on the remote (default)')
318 p.add_option('--no-prune', dest='prune', action='store_false',
319 help='do not delete refs that no longer exist on the remote')
LaMont Jones5ed8c632022-11-10 00:10:44 +0000320 p.add_option('--auto-gc', action='store_true', default=None,
LaMont Jones7efab532022-09-01 15:41:12 +0000321 help='run garbage collection on all synced projects')
322 p.add_option('--no-auto-gc', dest='auto_gc', action='store_false',
323 help='do not run garbage collection on any projects (default)')
Nico Sallembien6623b212010-05-11 12:57:01 -0700324 if show_smart:
325 p.add_option('-s', '--smart-sync',
326 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900327 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200328 p.add_option('-t', '--smart-tag',
329 dest='smart_tag', action='store',
330 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700331
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700332 g = p.add_option_group('repo Version options')
333 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500334 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700335 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700336 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800337 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700338 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700339
LaMont Jonesa46047a2022-04-07 21:57:06 +0000340 def _GetBranch(self, manifest_project):
341 """Returns the branch name for getting the approved smartsync manifest.
342
343 Args:
344 manifest_project: the manifestProject to query.
345 """
346 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800347 branch = b.merge
348 if branch.startswith(R_HEADS):
349 branch = branch[len(R_HEADS):]
350 return branch
351
LaMont Jonesa46047a2022-04-07 21:57:06 +0000352 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200353 """Returns whether current-branch or use-superproject options are enabled.
354
LaMont Jonesa46047a2022-04-07 21:57:06 +0000355 Args:
356 opt: Program options returned from optparse. See _Options().
357 manifest: The manifest to use.
358
Daniel Anderssond52ca422022-04-01 12:55:38 +0200359 Returns:
360 True if a superproject is requested, otherwise the value of the
361 current_branch option (True, False or None).
362 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000363 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700364
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000365 def _UpdateProjectsRevisionId(self, opt, args, superproject_logging_data,
366 manifest):
367 """Update revisionId of projects with the commit hash from the superproject.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800368
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000369 This function updates each project's revisionId with the commit hash from
370 the superproject. It writes the updated manifest into a file and reloads
371 the manifest from it. When appropriate, sub manifests are also processed.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800372
373 Args:
374 opt: Program options returned from optparse. See _Options().
375 args: Arguments to pass to GetProjects. See the GetProjects
376 docstring for details.
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000377 superproject_logging_data: A dictionary of superproject data to log.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000378 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800379 """
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000380 have_superproject = manifest.superproject or any(
381 m.superproject for m in manifest.all_children)
382 if not have_superproject:
383 return
384
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000385 if opt.local_only and manifest.superproject:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000386 manifest_path = manifest.superproject.manifest_path
Raman Tennetiae86a462021-07-27 08:54:59 -0700387 if manifest_path:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000388 self._ReloadManifest(manifest_path, manifest)
389 return
Raman Tennetiae86a462021-07-27 08:54:59 -0700390
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800391 all_projects = self.GetProjects(args,
392 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000393 submodules_ok=opt.fetch_submodules,
394 manifest=manifest,
395 all_manifests=not opt.this_manifest_only)
396
397 per_manifest = collections.defaultdict(list)
398 manifest_paths = {}
399 if opt.this_manifest_only:
400 per_manifest[manifest.path_prefix] = all_projects
Raman Tenneti784e16f2021-06-11 17:29:45 -0700401 else:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000402 for p in all_projects:
403 per_manifest[p.manifest.path_prefix].append(p)
404
405 superproject_logging_data = {}
406 need_unload = False
407 for m in self.ManifestList(opt):
408 if not m.path_prefix in per_manifest:
409 continue
410 use_super = git_superproject.UseSuperproject(opt.use_superproject, m)
411 if superproject_logging_data:
412 superproject_logging_data['multimanifest'] = True
413 superproject_logging_data.update(
414 superproject=use_super,
415 haslocalmanifests=bool(m.HasLocalManifests),
416 hassuperprojecttag=bool(m.superproject),
417 )
418 if use_super and (m.IsMirror or m.IsArchive):
419 # Don't use superproject, because we have no working tree.
420 use_super = False
421 superproject_logging_data['superproject'] = False
422 superproject_logging_data['noworktree'] = True
423 if opt.use_superproject is not False:
424 print(f'{m.path_prefix}: not using superproject because there is no '
425 'working tree.')
426
427 if not use_super:
428 continue
429 m.superproject.SetQuiet(opt.quiet)
430 print_messages = git_superproject.PrintMessages(opt.use_superproject, m)
431 m.superproject.SetPrintMessages(print_messages)
432 update_result = m.superproject.UpdateProjectsRevisionId(
433 per_manifest[m.path_prefix], git_event_log=self.git_event_log)
434 manifest_path = update_result.manifest_path
435 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
436 if manifest_path:
437 m.SetManifestOverride(manifest_path)
438 need_unload = True
439 else:
440 if print_messages:
441 print(f'{m.path_prefix}: warning: Update of revisionId from '
442 'superproject has failed, repo sync will not use superproject '
443 'to fetch the source. ',
444 'Please resync with the --no-use-superproject option to avoid '
445 'this repo warning.',
446 file=sys.stderr)
447 if update_result.fatal and opt.use_superproject is not None:
448 sys.exit(1)
449 if need_unload:
450 m.outer_client.manifest.Unload()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800451
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500452 def _FetchProjectList(self, opt, projects):
453 """Main function of the fetch worker.
454
455 The projects we're given share the same underlying git object store, so we
456 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800457
David James8d201162013-10-11 17:03:19 -0700458 Delegates most of the work to _FetchHelper.
459
460 Args:
461 opt: Program options returned from optparse. See _Options().
462 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700463 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500464 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700465
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500466 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700467 """Fetch git objects for a single project.
468
David Pursehousec1b86a22012-11-14 11:36:51 +0900469 Args:
470 opt: Program options returned from optparse. See _Options().
471 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700472
473 Returns:
474 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900475 """
David Rileye0684ad2017-04-05 00:02:59 -0700476 start = time.time()
477 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500478 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900479 try:
LaMont Jones1eddca82022-09-01 15:15:04 +0000480 sync_result = project.Sync_NetworkHalf(
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500481 quiet=opt.quiet,
482 verbose=opt.verbose,
483 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000484 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500485 force_sync=opt.force_sync,
486 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000487 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500488 optimized_fetch=opt.optimized_fetch,
489 retry_fetches=opt.retry_fetches,
490 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400491 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000492 clone_filter=project.manifest.CloneFilter,
493 partial_clone_exclude=project.manifest.PartialCloneExclude)
LaMont Jones1eddca82022-09-01 15:15:04 +0000494 success = sync_result.success
Doug Andersonfc06ced2011-03-16 15:49:18 -0700495
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500496 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400497 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500498 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700499
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500500 if not success:
501 print('error: Cannot fetch %s from %s'
502 % (project.name, project.remote.url),
503 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700504 except GitError as e:
505 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500506 except Exception as e:
507 print('error: Cannot fetch %s (%s: %s)'
508 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
509 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500510
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500511 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000512 return _FetchOneResult(success, project, start, finish,
513 sync_result.remote_fetched)
David James8d201162013-10-11 17:03:19 -0700514
Mike Frysinger339f2df2021-05-06 00:44:42 -0400515 @classmethod
516 def _FetchInitChild(cls, ssh_proxy):
517 cls.ssh_proxy = ssh_proxy
518
519 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500520 ret = True
521
Mike Frysinger355f4392022-07-20 17:15:29 -0400522 jobs = opt.jobs_network
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700523 fetched = set()
LaMont Jones1eddca82022-09-01 15:15:04 +0000524 remote_fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400525 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800526
David James89ece422014-01-09 18:51:58 -0800527 objdir_project_map = dict()
528 for project in projects:
529 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500530 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700531
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500532 def _ProcessResults(results_sets):
533 ret = True
534 for results in results_sets:
LaMont Jones1eddca82022-09-01 15:15:04 +0000535 for result in results:
536 success = result.success
537 project = result.project
538 start = result.start
539 finish = result.finish
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500540 self._fetch_times.Set(project, finish - start)
541 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
542 start, finish, success)
LaMont Jones1eddca82022-09-01 15:15:04 +0000543 if result.remote_fetched:
544 remote_fetched.add(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500545 # Check for any errors before running any more tasks.
546 # ...we'll let existing jobs finish, though.
547 if not success:
548 ret = False
549 else:
550 fetched.add(project.gitdir)
551 pm.update(msg=project.name)
552 if not ret and opt.fail_fast:
553 break
554 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700555
Mike Frysinger339f2df2021-05-06 00:44:42 -0400556 # We pass the ssh proxy settings via the class. This allows multiprocessing
557 # to pickle it up when spawning children. We can't pass it as an argument
558 # to _FetchProjectList below as multiprocessing is unable to pickle those.
559 Sync.ssh_proxy = None
560
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500561 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400562 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400563 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500564 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
565 ret = False
566 else:
567 # Favor throughput over responsiveness when quiet. It seems that imap()
568 # will yield results in batches relative to chunksize, so even as the
569 # children finish a sync, we won't see the result until one child finishes
570 # ~chunksize jobs. When using a large --jobs with large chunksize, this
571 # can be jarring as there will be a large initial delay where repo looks
572 # like it isn't doing anything and sits at 0%, but then suddenly completes
573 # a lot of jobs all at once. Since this code is more network bound, we
574 # can accept a bit more CPU overhead with a smaller chunksize so that the
575 # user sees more immediate & continuous feedback.
576 if opt.quiet:
577 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800578 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500579 pm.update(inc=0, msg='warming up')
580 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800581 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
582 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500583 results = pool.imap_unordered(
584 functools.partial(self._FetchProjectList, opt),
585 projects_list,
586 chunksize=chunksize)
587 if not _ProcessResults(results):
588 ret = False
589 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800590
Mike Frysinger339f2df2021-05-06 00:44:42 -0400591 # Cleanup the reference now that we're done with it, and we're going to
592 # release any resources it points to. If we don't, later multiprocessing
593 # usage (e.g. checkouts) will try to pickle and then crash.
594 del Sync.ssh_proxy
595
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700596 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700597 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700598
LaMont Jonesa46047a2022-04-07 21:57:06 +0000599 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400600 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200601
LaMont Jones1eddca82022-09-01 15:15:04 +0000602 return _FetchResult(ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700603
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000604 def _FetchMain(self, opt, args, all_projects, err_event,
605 ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400606 """The main network fetch loop.
607
608 Args:
609 opt: Program options returned from optparse. See _Options().
610 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200611 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400612 err_event: Whether an error was hit while processing.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400613 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000614 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200615
616 Returns:
617 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400618 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000619 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400620
621 to_fetch = []
622 now = time.time()
623 if _ONE_DAY_S <= (now - rp.LastFetch):
624 to_fetch.append(rp)
625 to_fetch.extend(all_projects)
626 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
627
LaMont Jones1eddca82022-09-01 15:15:04 +0000628 result = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
629 success = result.success
630 fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400631 if not success:
632 err_event.set()
633
634 _PostRepoFetch(rp, opt.repo_verify)
635 if opt.network_only:
636 # bail out now; the rest touches the working tree
637 if err_event.is_set():
638 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
639 sys.exit(1)
LaMont Jones1eddca82022-09-01 15:15:04 +0000640 return _FetchMainResult([])
Mike Frysingerb4429432021-05-05 20:03:26 -0400641
642 # Iteratively fetch missing and/or nested unregistered submodules
643 previously_missing_set = set()
644 while True:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000645 self._ReloadManifest(None, manifest)
Mike Frysingerb4429432021-05-05 20:03:26 -0400646 all_projects = self.GetProjects(args,
647 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000648 submodules_ok=opt.fetch_submodules,
649 manifest=manifest,
650 all_manifests=not opt.this_manifest_only)
Mike Frysingerb4429432021-05-05 20:03:26 -0400651 missing = []
652 for project in all_projects:
653 if project.gitdir not in fetched:
654 missing.append(project)
655 if not missing:
656 break
657 # Stop us from non-stopped fetching actually-missing repos: If set of
658 # missing repos has not been changed from last fetch, we break.
659 missing_set = set(p.name for p in missing)
660 if previously_missing_set == missing_set:
661 break
662 previously_missing_set = missing_set
LaMont Jones1eddca82022-09-01 15:15:04 +0000663 result = self._Fetch(missing, opt, err_event, ssh_proxy)
664 success = result.success
665 new_fetched = result.projects
Mike Frysingerb4429432021-05-05 20:03:26 -0400666 if not success:
667 err_event.set()
668 fetched.update(new_fetched)
669
LaMont Jones1eddca82022-09-01 15:15:04 +0000670 return _FetchMainResult(all_projects)
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200671
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500672 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700673 """Checkout work tree for one project
674
675 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500676 detach_head: Whether to leave a detached HEAD.
677 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700678 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700679
680 Returns:
681 Whether the fetch was successful.
682 """
Xin Li745be2e2019-06-03 11:24:30 -0700683 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000684 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500685 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700686 success = False
687 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500688 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500689 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700690 except GitError as e:
691 print('error.GitError: Cannot checkout %s: %s' %
692 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500693 except Exception as e:
694 print('error: Cannot checkout %s: %s: %s' %
695 (project.name, type(e).__name__, str(e)),
696 file=sys.stderr)
697 raise
Xin Li745be2e2019-06-03 11:24:30 -0700698
Mike Frysingerebf04a42021-02-23 20:48:04 -0500699 if not success:
700 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
701 finish = time.time()
LaMont Jones1eddca82022-09-01 15:15:04 +0000702 return _CheckoutOneResult(success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700703
Mike Frysingerebf04a42021-02-23 20:48:04 -0500704 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700705 """Checkout projects listed in all_projects
706
707 Args:
708 all_projects: List of all projects that should be checked out.
709 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500710 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700711 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500712 # Only checkout projects with worktrees.
713 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700714
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500715 def _ProcessResults(pool, pm, results):
716 ret = True
LaMont Jones1eddca82022-09-01 15:15:04 +0000717 for result in results:
718 success = result.success
719 project = result.project
720 start = result.start
721 finish = result.finish
Mike Frysingerebf04a42021-02-23 20:48:04 -0500722 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
723 start, finish, success)
724 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500725 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500726 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500727 ret = False
LaMont Jonesbee4efb2022-09-30 17:46:52 +0000728 err_results.append(project.RelPath(local=opt.this_manifest_only))
Mike Frysingerebf04a42021-02-23 20:48:04 -0500729 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500730 if pool:
731 pool.close()
732 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500733 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500734 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700735
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500736 return self.ExecuteInParallel(
Mike Frysinger355f4392022-07-20 17:15:29 -0400737 opt.jobs_checkout,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500738 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
739 all_projects,
740 callback=_ProcessResults,
741 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500742
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000743 def _backup_cruft(self, bare_git):
744 """Save a copy of any cruft from `git gc`."""
745 # Find any cruft packs in the current gitdir, and save them.
746 # b/221065125 (repo sync complains that objects are missing). This does
747 # not prevent that state, but makes it so that the missing objects are
748 # available.
749 objdir = bare_git._project.objdir
750 pack_dir = os.path.join(objdir, 'pack')
751 bak_dir = os.path.join(objdir, '.repo', 'pack.bak')
752 if not _BACKUP_OBJECTS or not platform_utils.isdir(pack_dir):
753 return
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000754 files = set(platform_utils.listdir(pack_dir))
755 to_backup = []
756 for f in files:
757 base, ext = os.path.splitext(f)
758 if base + '.mtimes' in files:
759 to_backup.append(f)
760 if to_backup:
761 os.makedirs(bak_dir, exist_ok=True)
762 for fname in to_backup:
763 bak_fname = os.path.join(bak_dir, fname)
764 if not os.path.exists(bak_fname):
Joanna Wanga6c52f52022-11-03 16:51:19 -0400765 with Trace('%s saved %s', bare_git._project.name, fname):
766 # Use a tmp file so that we are sure of a complete copy.
767 shutil.copy(os.path.join(pack_dir, fname), bak_fname + '.tmp')
768 shutil.move(bak_fname + '.tmp', bak_fname)
LaMont Jonesaefa4d32022-09-15 18:24:56 +0000769
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000770 @staticmethod
771 def _GetPreciousObjectsState(project: Project, opt):
772 """Get the preciousObjects state for the project.
773
774 Args:
775 project (Project): the project to examine, and possibly correct.
776 opt (optparse.Values): options given to sync.
777
778 Returns:
779 Expected state of extensions.preciousObjects:
780 False: Should be disabled. (not present)
781 True: Should be enabled.
782 """
783 if project.use_git_worktrees:
784 return False
785 projects = project.manifest.GetProjectsWithName(project.name,
786 all_manifests=True)
787 if len(projects) == 1:
788 return False
789 relpath = project.RelPath(local=opt.this_manifest_only)
790 if len(projects) > 1:
791 # Objects are potentially shared with another project.
792 # See the logic in Project.Sync_NetworkHalf regarding UseAlternates.
793 # - When False, shared projects share (via symlink)
794 # .repo/project-objects/{PROJECT_NAME}.git as the one-and-only objects
795 # directory. All objects are precious, since there is no project with a
796 # complete set of refs.
797 # - When True, shared projects share (via info/alternates)
798 # .repo/project-objects/{PROJECT_NAME}.git as an alternate object store,
799 # which is written only on the first clone of the project, and is not
800 # written subsequently. (When Sync_NetworkHalf sees that it exists, it
801 # makes sure that the alternates file points there, and uses a
802 # project-local .git/objects directory for all syncs going forward.
803 # We do not support switching between the options. The environment
804 # variable is present for testing and migration only.
805 return not project.UseAlternates
806 print(f'\r{relpath}: project not found in manifest.', file=sys.stderr)
807 return False
808
809 def _RepairPreciousObjectsState(self, project: Project, opt):
810 """Correct the preciousObjects state for the project.
811
812 Args:
813 project (Project): the project to examine, and possibly correct.
814 opt (optparse.Values): options given to sync.
815 """
816 expected = self._GetPreciousObjectsState(project, opt)
817 actual = project.config.GetBoolean('extensions.preciousObjects') or False
818 relpath = project.RelPath(local = opt.this_manifest_only)
819
820 if (expected != actual and
821 not project.config.GetBoolean('repo.preservePreciousObjects')):
822 # If this is unexpected, log it and repair.
823 Trace(f'{relpath} expected preciousObjects={expected}, got {actual}')
824 if expected:
825 if not opt.quiet:
826 print('\r%s: Shared project %s found, disabling pruning.' %
827 (relpath, project.name))
828 if git_require((2, 7, 0)):
829 project.EnableRepositoryExtension('preciousObjects')
830 else:
831 # This isn't perfect, but it's the best we can do with old git.
832 print('\r%s: WARNING: shared projects are unreliable when using '
833 'old versions of git; please upgrade to git-2.7.0+.'
834 % (relpath,),
835 file=sys.stderr)
836 project.config.SetString('gc.pruneExpire', 'never')
837 else:
838 if not opt.quiet:
839 print(f'\r{relpath}: not shared, disabling pruning.')
840 project.config.SetString('extensions.preciousObjects', None)
841 project.config.SetString('gc.pruneExpire', None)
842
Mike Frysinger5a033082019-09-23 19:21:20 -0400843 def _GCProjects(self, projects, opt, err_event):
LaMont Jones7efab532022-09-01 15:41:12 +0000844 """Perform garbage collection.
845
846 If We are skipping garbage collection (opt.auto_gc not set), we still want
847 to potentially mark objects precious, so that `git gc` does not discard
848 shared objects.
849 """
850 pm = Progress(f'{"" if opt.auto_gc else "NOT "}Garbage collecting',
851 len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400852 pm.update(inc=0, msg='prescan')
853
Allen Webb4ee4a452021-10-07 10:42:38 -0500854 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700855 for project in projects:
LaMont Jonesfa8d9392022-11-02 22:01:29 +0000856 self._RepairPreciousObjectsState(project, opt)
857
Allen Webb669efd02021-10-01 15:25:31 -0500858 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500859 # Only call git gc once per objdir, but call pack-refs for the remainder.
860 if project.objdir not in tidy_dirs:
861 tidy_dirs[project.objdir] = (
862 True, # Run a full gc.
863 project.bare_git,
864 )
865 elif project.gitdir not in tidy_dirs:
866 tidy_dirs[project.gitdir] = (
867 False, # Do not run a full gc; just run pack-refs.
868 project.bare_git,
869 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400870
LaMont Jones7efab532022-09-01 15:41:12 +0000871 if not opt.auto_gc:
872 pm.end()
873 return
874
Mike Frysinger355f4392022-07-20 17:15:29 -0400875 jobs = opt.jobs
Dave Borowitz18857212012-10-23 17:02:59 -0700876
LaMont Jonesacc4c852022-09-22 19:05:01 +0000877 gc_args = ['--auto']
878 backup_cruft = False
879 if git_require((2, 37, 0)):
880 gc_args.append('--cruft')
881 backup_cruft = True
LaMont Jones891e8f72022-09-08 20:17:58 +0000882 pack_refs_args = ()
Dave Borowitz18857212012-10-23 17:02:59 -0700883 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500884 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400885 pm.update(msg=bare_git._project.name)
LaMont Jones891e8f72022-09-08 20:17:58 +0000886
Allen Webb4ee4a452021-10-07 10:42:38 -0500887 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000888 bare_git.gc(*gc_args)
Allen Webb4ee4a452021-10-07 10:42:38 -0500889 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000890 bare_git.pack_refs(*pack_refs_args)
LaMont Jonesacc4c852022-09-22 19:05:01 +0000891 if backup_cruft:
892 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400893 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700894 return
895
Mike Frysinger355f4392022-07-20 17:15:29 -0400896 cpu_count = os.cpu_count()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400897 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700898
899 threads = set()
900 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700901
Allen Webb4ee4a452021-10-07 10:42:38 -0500902 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400903 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700904 try:
905 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500906 if run_gc:
LaMont Jones891e8f72022-09-08 20:17:58 +0000907 bare_git.gc(*gc_args, config=config)
Allen Webb4ee4a452021-10-07 10:42:38 -0500908 else:
LaMont Jones891e8f72022-09-08 20:17:58 +0000909 bare_git.pack_refs(*pack_refs_args, config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700910 except GitError:
911 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900912 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700913 err_event.set()
914 raise
915 finally:
LaMont Jonesacc4c852022-09-22 19:05:01 +0000916 if backup_cruft:
917 self._backup_cruft(bare_git)
Mike Frysinger65af2602021-04-08 22:47:44 -0400918 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700919 sem.release()
920
Allen Webb4ee4a452021-10-07 10:42:38 -0500921 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500922 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700923 break
924 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500925 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700926 t.daemon = True
927 threads.add(t)
928 t.start()
929
930 for t in threads:
931 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400932 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700933
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000934 def _ReloadManifest(self, manifest_name, manifest):
Raman Tennetifeb28912021-05-02 19:47:29 -0700935 """Reload the manfiest from the file specified by the |manifest_name|.
936
937 It unloads the manifest if |manifest_name| is None.
938
939 Args:
940 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000941 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700942 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800943 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000944 # Override calls Unload already
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000945 manifest.Override(manifest_name)
Tim Kilbourn07669002013-03-08 15:02:49 -0800946 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000947 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800948
LaMont Jonesa46047a2022-04-07 21:57:06 +0000949 def UpdateProjectList(self, opt, manifest):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000950 """Update the cached projects list for |manifest|
951
952 In a multi-manifest checkout, each manifest has its own project.list.
953
954 Args:
955 opt: Program options returned from optparse. See _Options().
956 manifest: The manifest to use.
957
958 Returns:
959 0: success
960 1: failure
961 """
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700962 new_project_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +0000963 for project in self.GetProjects(None, missing_ok=True, manifest=manifest,
964 all_manifests=False):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700965 if project.relpath:
966 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700967 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000968 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700969 old_project_paths = []
970
971 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500972 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700973 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800974 # In reversed order, so subfolders are deleted before parent folder.
975 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700976 if not path:
977 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700978 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900979 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000980 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700981 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900982 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000983 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900984 name=path,
985 remote=RemoteSpec('origin'),
986 gitdir=gitdir,
987 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500988 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000989 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900990 relpath=path,
991 revisionExpr='HEAD',
992 revisionId=None,
993 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500994 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900995 quiet=opt.quiet,
996 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400997 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700998
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700999 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -05001000 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001001 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -07001002 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001003 return 0
1004
LaMont Jonesa46047a2022-04-07 21:57:06 +00001005 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +08001006 """Save all dests of copyfile and linkfile, and update them if needed.
1007
1008 Returns:
1009 Whether update was successful.
1010 """
1011 new_paths = {}
1012 new_linkfile_paths = []
1013 new_copyfile_paths = []
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001014 for project in self.GetProjects(None, missing_ok=True,
1015 manifest=manifest, all_manifests=False):
jiajia tanga590e642021-04-25 20:02:02 +08001016 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
1017 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
1018
1019 new_paths = {
1020 'linkfile': new_linkfile_paths,
1021 'copyfile': new_copyfile_paths,
1022 }
1023
1024 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001025 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +08001026 old_copylinkfile_paths = {}
1027
1028 if os.path.exists(copylinkfile_path):
1029 with open(copylinkfile_path, 'rb') as fp:
1030 try:
1031 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001032 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +08001033 print('error: %s is not a json formatted file.' %
1034 copylinkfile_path, file=sys.stderr)
1035 platform_utils.remove(copylinkfile_path)
1036 return False
1037
1038 need_remove_files = []
1039 need_remove_files.extend(
1040 set(old_copylinkfile_paths.get('linkfile', [])) -
1041 set(new_linkfile_paths))
1042 need_remove_files.extend(
1043 set(old_copylinkfile_paths.get('copyfile', [])) -
1044 set(new_copyfile_paths))
1045
1046 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -04001047 # Try to remove the updated copyfile or linkfile.
1048 # So, if the file is not exist, nothing need to do.
1049 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +08001050
1051 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
1052 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
1053 json.dump(new_paths, fp)
1054 return True
1055
LaMont Jonesa46047a2022-04-07 21:57:06 +00001056 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
1057 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001058 print('error: cannot smart sync: no manifest server defined in '
1059 'manifest', file=sys.stderr)
1060 sys.exit(1)
1061
LaMont Jonesa46047a2022-04-07 21:57:06 +00001062 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001063 if not opt.quiet:
1064 print('Using manifest server %s' % manifest_server)
1065
David Pursehouseeeff3532020-02-12 11:24:10 +09001066 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001067 username = None
1068 password = None
1069 if opt.manifest_server_username and opt.manifest_server_password:
1070 username = opt.manifest_server_username
1071 password = opt.manifest_server_password
1072 else:
1073 try:
1074 info = netrc.netrc()
1075 except IOError:
1076 # .netrc file does not exist or could not be opened
1077 pass
1078 else:
1079 try:
1080 parse_result = urllib.parse.urlparse(manifest_server)
1081 if parse_result.hostname:
1082 auth = info.authenticators(parse_result.hostname)
1083 if auth:
1084 username, _account, password = auth
1085 else:
1086 print('No credentials found for %s in .netrc'
1087 % parse_result.hostname, file=sys.stderr)
1088 except netrc.NetrcParseError as e:
1089 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
1090
1091 if (username and password):
1092 manifest_server = manifest_server.replace('://', '://%s:%s@' %
1093 (username, password),
1094 1)
1095
1096 transport = PersistentTransport(manifest_server)
1097 if manifest_server.startswith('persistent-'):
1098 manifest_server = manifest_server[len('persistent-'):]
1099
1100 try:
1101 server = xmlrpc.client.Server(manifest_server, transport=transport)
1102 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001103 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001104
Mike Frysinger56ce3462019-12-04 19:30:48 -05001105 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -05001106 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001107 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -05001108 elif ('TARGET_PRODUCT' in os.environ and
1109 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -05001110 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
1111 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001112 [success, manifest_str] = server.GetApprovedManifest(branch, target)
1113 else:
1114 [success, manifest_str] = server.GetApprovedManifest(branch)
1115 else:
1116 assert(opt.smart_tag)
1117 [success, manifest_str] = server.GetManifest(opt.smart_tag)
1118
1119 if success:
1120 manifest_name = os.path.basename(smart_sync_manifest_path)
1121 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001122 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001123 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001124 except IOError as e:
1125 print('error: cannot write manifest to %s:\n%s'
1126 % (smart_sync_manifest_path, e),
1127 file=sys.stderr)
1128 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001129 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001130 else:
1131 print('error: manifest server RPC call failed: %s' %
1132 manifest_str, file=sys.stderr)
1133 sys.exit(1)
1134 except (socket.error, IOError, xmlrpc.client.Fault) as e:
1135 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001136 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001137 sys.exit(1)
1138 except xmlrpc.client.ProtocolError as e:
1139 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +00001140 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001141 file=sys.stderr)
1142 sys.exit(1)
1143
1144 return manifest_name
1145
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001146 def _UpdateAllManifestProjects(self, opt, mp, manifest_name):
1147 """Fetch & update the local manifest project.
1148
1149 After syncing the manifest project, if the manifest has any sub manifests,
1150 those are recursively processed.
1151
1152 Args:
1153 opt: Program options returned from optparse. See _Options().
1154 mp: the manifestProject to query.
1155 manifest_name: Manifest file to be reloaded.
1156 """
1157 if not mp.standalone_manifest_url:
1158 self._UpdateManifestProject(opt, mp, manifest_name)
1159
1160 if mp.manifest.submanifests:
1161 for submanifest in mp.manifest.submanifests.values():
1162 child = submanifest.repo_client.manifest
1163 child.manifestProject.SyncWithPossibleInit(
1164 submanifest,
1165 current_branch_only=self._GetCurrentBranchOnly(opt, child),
1166 verbose=opt.verbose,
1167 tags=opt.tags,
1168 git_event_log=self.git_event_log,
1169 )
1170 self._UpdateAllManifestProjects(opt, child.manifestProject, None)
1171
Mike Frysingerfb527e32019-08-27 02:34:32 -04001172 def _UpdateManifestProject(self, opt, mp, manifest_name):
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001173 """Fetch & update the local manifest project.
1174
1175 Args:
1176 opt: Program options returned from optparse. See _Options().
1177 mp: the manifestProject to query.
1178 manifest_name: Manifest file to be reloaded.
1179 """
Mike Frysingerfb527e32019-08-27 02:34:32 -04001180 if not opt.local_only:
1181 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -05001182 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001183 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +02001184 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001185 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -04001186 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001187 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001188 submodules=mp.manifest.HasSubmodules,
1189 clone_filter=mp.manifest.CloneFilter,
1190 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001191 finish = time.time()
1192 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
1193 start, finish, success)
1194
1195 if mp.HasChanges:
1196 syncbuf = SyncBuffer(mp.config)
1197 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +00001198 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -04001199 clean = syncbuf.Finish()
1200 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
1201 start, time.time(), clean)
1202 if not clean:
1203 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +00001204 self._ReloadManifest(manifest_name, mp.manifest)
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001205
Mike Frysingerae6cb082019-08-27 01:10:59 -04001206 def ValidateOptions(self, opt, args):
1207 if opt.force_broken:
1208 print('warning: -f/--force-broken is now the default behavior, and the '
1209 'options are deprecated', file=sys.stderr)
1210 if opt.network_only and opt.detach_head:
1211 self.OptionParser.error('cannot combine -n and -d')
1212 if opt.network_only and opt.local_only:
1213 self.OptionParser.error('cannot combine -n and -l')
1214 if opt.manifest_name and opt.smart_sync:
1215 self.OptionParser.error('cannot combine -m and -s')
1216 if opt.manifest_name and opt.smart_tag:
1217 self.OptionParser.error('cannot combine -m and -t')
1218 if opt.manifest_server_username or opt.manifest_server_password:
1219 if not (opt.smart_sync or opt.smart_tag):
1220 self.OptionParser.error('-u and -p may only be combined with -s or -t')
1221 if None in [opt.manifest_server_username, opt.manifest_server_password]:
1222 self.OptionParser.error('both -u and -p must be given')
1223
Mike Frysinger0531a622021-11-05 15:22:01 -04001224 if opt.prune is None:
1225 opt.prune = True
1226
LaMont Jones5ed8c632022-11-10 00:10:44 +00001227 if opt.auto_gc is None and _AUTO_GC:
1228 print(f"Will run `git gc --auto` because {_REPO_AUTO_GC} is set.",
1229 file=sys.stderr)
1230 opt.auto_gc = True
LaMont Jonesd7935532022-12-01 20:18:46 +00001231 print(f'{_REPO_AUTO_GC} is deprecated and will be removed in a future'
1232 'release. Use `--auto-gc` instead.', file=sys.stderr)
LaMont Jones5ed8c632022-11-10 00:10:44 +00001233
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001234 def Execute(self, opt, args):
LaMont Jonesa46047a2022-04-07 21:57:06 +00001235 manifest = self.outer_manifest
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001236 if not opt.outer_manifest:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001237 manifest = self.manifest
1238
Chris Wolfee9dc3b32012-01-26 11:36:18 -05001239 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001240 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -07001241
Chirayu Desaia892b102013-06-11 14:18:46 +05301242 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +09001243 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001244 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +05301245
Xin Lid79a4bc2020-05-20 16:03:45 -07001246 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001247 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -07001248
Victor Boivie08c880d2011-04-19 10:32:52 +02001249 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001250 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -04001251 else:
David Pursehouse59b41742015-05-07 14:36:09 +09001252 if os.path.isfile(smart_sync_manifest_path):
1253 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001254 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +09001255 except OSError as e:
1256 print('error: failed to remove existing smart sync override manifest: %s' %
1257 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -07001258
Mike Frysingerc99322a2021-05-04 15:32:43 -04001259 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -04001260
LaMont Jonesa46047a2022-04-07 21:57:06 +00001261 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001262 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -05001263 cb = rp.CurrentBranch
1264 if cb:
1265 base = rp.GetBranch(cb).merge
1266 if not base or not base.startswith('refs/heads/'):
1267 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -04001268 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -05001269 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001270
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001271 for m in self.ManifestList(opt):
LaMont Jones4112c072022-08-24 17:32:25 +00001272 if not m.manifestProject.standalone_manifest_url:
1273 m.manifestProject.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001274
LaMont Jones4112c072022-08-24 17:32:25 +00001275 if opt.repo_upgraded:
1276 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001277
LaMont Jones4112c072022-08-24 17:32:25 +00001278 mp = manifest.manifestProject
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001279 if opt.mp_update:
1280 self._UpdateAllManifestProjects(opt, mp, manifest_name)
1281 else:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001282 print('Skipping update of local manifest project.')
Simran Basib9a1b732015-08-20 12:19:28 -07001283
Mike Frysinger355f4392022-07-20 17:15:29 -04001284 # Now that the manifests are up-to-date, setup the jobs value.
1285 if opt.jobs is None:
1286 # User has not made a choice, so use the manifest settings.
1287 opt.jobs = mp.default.sync_j
1288 if opt.jobs is not None:
1289 # Neither user nor manifest have made a choice.
1290 if opt.jobs_network is None:
1291 opt.jobs_network = opt.jobs
1292 if opt.jobs_checkout is None:
1293 opt.jobs_checkout = opt.jobs
1294 # Setup defaults if jobs==0.
1295 if not opt.jobs:
1296 if not opt.jobs_network:
1297 opt.jobs_network = 1
1298 if not opt.jobs_checkout:
1299 opt.jobs_checkout = DEFAULT_LOCAL_JOBS
1300 opt.jobs = os.cpu_count()
1301
1302 # Try to stay under user rlimit settings.
1303 #
1304 # Since each worker requires at 3 file descriptors to run `git fetch`, use
1305 # that to scale down the number of jobs. Unfortunately there isn't an easy
1306 # way to determine this reliably as systems change, but it was last measured
1307 # by hand in 2011.
1308 soft_limit, _ = _rlimit_nofile()
1309 jobs_soft_limit = max(1, (soft_limit - 5) // 3)
1310 opt.jobs = min(opt.jobs, jobs_soft_limit)
1311 opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
1312 opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
1313
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001314 superproject_logging_data = {}
1315 self._UpdateProjectsRevisionId(opt, args, superproject_logging_data,
1316 manifest)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001317
Simran Basib9a1b732015-08-20 12:19:28 -07001318 if self.gitc_manifest:
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001319 gitc_manifest_projects = self.GetProjects(args, missing_ok=True)
Simran Basib9a1b732015-08-20 12:19:28 -07001320 gitc_projects = []
1321 opened_projects = []
1322 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001323 if project.relpath in self.gitc_manifest.paths and \
1324 self.gitc_manifest.paths[project.relpath].old_revision:
1325 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001326 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001327 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001328
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001329 if not args:
1330 gitc_projects = None
1331
1332 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001333 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001334 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1335 if manifest_name:
1336 manifest.Override(manifest_name)
1337 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001338 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001339 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1340 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001341 gitc_projects)
1342 print('GITC client successfully synced.')
1343
1344 # The opened projects need to be synced as normal, therefore we
1345 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001346 # TODO: make this more reliable -- if there's a project name/path overlap,
1347 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001348 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001349 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001350 if not args:
1351 return
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001352
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001353 all_projects = self.GetProjects(args,
1354 missing_ok=True,
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001355 submodules_ok=opt.fetch_submodules,
1356 manifest=manifest,
1357 all_manifests=not opt.this_manifest_only)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001358
Mike Frysinger5a033082019-09-23 19:21:20 -04001359 err_network_sync = False
1360 err_update_projects = False
LaMont Jonesb6cfa092022-10-26 16:34:40 +00001361 err_update_linkfiles = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001362
LaMont Jonesa46047a2022-04-07 21:57:06 +00001363 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001364 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001365 with multiprocessing.Manager() as manager:
1366 with ssh.ProxyManager(manager) as ssh_proxy:
1367 # Initialize the socket dir once in the parent.
1368 ssh_proxy.sock()
LaMont Jones1eddca82022-09-01 15:15:04 +00001369 result = self._FetchMain(opt, args, all_projects, err_event,
1370 ssh_proxy, manifest)
1371 all_projects = result.all_projects
Mike Frysinger339f2df2021-05-06 00:44:42 -04001372
1373 if opt.network_only:
1374 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001375
1376 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001377 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001378 err_network_sync = True
1379 if opt.fail_fast:
1380 print('\nerror: Exited sync due to fetch errors.\n'
1381 'Local checkouts *not* updated. Resolve network issues & '
1382 'retry.\n'
1383 '`repo sync -l` will update some local checkouts.',
1384 file=sys.stderr)
1385 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001386
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001387 for m in self.ManifestList(opt):
1388 if m.IsMirror or m.IsArchive:
1389 # bail out now, we have no working tree
1390 continue
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001391
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001392 if self.UpdateProjectList(opt, m):
1393 err_event.set()
1394 err_update_projects = True
1395 if opt.fail_fast:
1396 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1397 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001398
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001399 err_update_linkfiles = not self.UpdateCopyLinkfileList(m)
1400 if err_update_linkfiles:
1401 err_event.set()
1402 if opt.fail_fast:
1403 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1404 sys.exit(1)
jiajia tanga590e642021-04-25 20:02:02 +08001405
Mike Frysinger5a033082019-09-23 19:21:20 -04001406 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001407 # NB: We don't exit here because this is the last step.
1408 err_checkout = not self._Checkout(all_projects, opt, err_results)
1409 if err_checkout:
1410 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001411
LaMont Jonesbdcba7d2022-04-11 22:50:11 +00001412 printed_notices = set()
1413 # If there's a notice that's supposed to print at the end of the sync,
1414 # print it now... But avoid printing duplicate messages, and preserve
1415 # order.
1416 for m in sorted(self.ManifestList(opt), key=lambda x: x.path_prefix):
1417 if m.notice and m.notice not in printed_notices:
1418 print(m.notice)
1419 printed_notices.add(m.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001420
Mike Frysinger5a033082019-09-23 19:21:20 -04001421 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001422 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001423 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1424 if err_network_sync:
1425 print('error: Downloading network changes failed.', file=sys.stderr)
1426 if err_update_projects:
1427 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001428 if err_update_linkfiles:
1429 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001430 if err_checkout:
1431 print('error: Checking out local projects failed.', file=sys.stderr)
1432 if err_results:
1433 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1434 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1435 file=sys.stderr)
1436 sys.exit(1)
1437
Raman Tenneti7954de12021-07-28 14:36:49 -07001438 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001439 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1440 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001441
1442 # Update and log with the new sync analysis state.
1443 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001444 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1445 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001446
Mike Frysingere19d9e12020-02-12 11:23:32 -05001447 if not opt.quiet:
1448 print('repo sync has finished successfully.')
1449
David Pursehouse819827a2020-02-12 15:20:19 +09001450
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001451def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001452 # Link the docs for the internal .repo/ layout for people
1453 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1454 if not platform_utils.islink(link):
1455 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1456 try:
1457 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001458 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001459 pass
1460
Conley Owens094cdbe2014-01-30 15:09:59 -08001461 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001462 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001463 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001464 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001465 if project.Exists:
1466 project.PostRepoUpgrade()
1467
David Pursehouse819827a2020-02-12 15:20:19 +09001468
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001469def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001470 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001471 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001472 wrapper = Wrapper()
1473 try:
1474 rev = rp.bare_git.describe(rp.GetRevisionId())
1475 except GitError:
1476 rev = None
1477 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1478 # See if we're held back due to missing signed tag.
1479 current_revid = rp.bare_git.rev_parse('HEAD')
1480 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1481 if current_revid != new_revid:
1482 # We want to switch to the new rev, but also not trash any uncommitted
1483 # changes. This helps with local testing/hacking.
1484 # If a local change has been made, we will throw that away.
1485 # We also have to make sure this will switch to an older commit if that's
1486 # the latest tag in order to support release rollback.
1487 try:
1488 rp.work_git.reset('--keep', new_rev)
1489 except GitError as e:
1490 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001491 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001492 raise RepoChangedException(['--repo-upgraded'])
1493 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001494 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001495 else:
1496 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001497 print('repo version %s is current' % rp.work_git.describe(HEAD),
1498 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001499
David Pursehouse819827a2020-02-12 15:20:19 +09001500
Dave Borowitz67700e92012-10-23 15:00:54 -07001501class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001502 _ALPHA = 0.5
1503
Dave Borowitz67700e92012-10-23 15:00:54 -07001504 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001505 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001506 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001507 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001508
1509 def Get(self, project):
1510 self._Load()
1511 return self._times.get(project.name, _ONE_DAY_S)
1512
1513 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001514 self._Load()
1515 name = project.name
1516 old = self._times.get(name, t)
1517 self._seen.add(name)
1518 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001519 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001520
1521 def _Load(self):
1522 if self._times is None:
1523 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001524 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001525 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001526 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001527 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001528 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001529
1530 def Save(self):
1531 if self._times is None:
1532 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001533
1534 to_delete = []
1535 for name in self._times:
1536 if name not in self._seen:
1537 to_delete.append(name)
1538 for name in to_delete:
1539 del self._times[name]
1540
Dave Borowitz67700e92012-10-23 15:00:54 -07001541 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001542 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001543 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001544 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001545 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001546
1547# This is a replacement for xmlrpc.client.Transport using urllib2
1548# and supporting persistent-http[s]. It cannot change hosts from
1549# request to request like the normal transport, the real url
1550# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001551
1552
Dan Willemsen0745bb22015-08-17 13:41:45 -07001553class PersistentTransport(xmlrpc.client.Transport):
1554 def __init__(self, orig_host):
1555 self.orig_host = orig_host
1556
1557 def request(self, host, handler, request_body, verbose=False):
1558 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1559 # Python doesn't understand cookies with the #HttpOnly_ prefix
1560 # Since we're only using them for HTTP, copy the file temporarily,
1561 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001562 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001563 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001564 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001565 try:
1566 with open(cookiefile) as f:
1567 for line in f:
1568 if line.startswith("#HttpOnly_"):
1569 line = line[len("#HttpOnly_"):]
1570 tmpcookiefile.write(line)
1571 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001572
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001573 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001574 try:
1575 cookiejar.load()
1576 except cookielib.LoadError:
1577 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001578 finally:
1579 tmpcookiefile.close()
1580 else:
1581 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001582
1583 proxyhandler = urllib.request.ProxyHandler
1584 if proxy:
1585 proxyhandler = urllib.request.ProxyHandler({
1586 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001587 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001588
1589 opener = urllib.request.build_opener(
1590 urllib.request.HTTPCookieProcessor(cookiejar),
1591 proxyhandler)
1592
1593 url = urllib.parse.urljoin(self.orig_host, handler)
1594 parse_results = urllib.parse.urlparse(url)
1595
1596 scheme = parse_results.scheme
1597 if scheme == 'persistent-http':
1598 scheme = 'http'
1599 if scheme == 'persistent-https':
1600 # If we're proxying through persistent-https, use http. The
1601 # proxy itself will do the https.
1602 if proxy:
1603 scheme = 'http'
1604 else:
1605 scheme = 'https'
1606
1607 # Parse out any authentication information using the base class
1608 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1609
1610 url = urllib.parse.urlunparse((
1611 scheme,
1612 host,
1613 parse_results.path,
1614 parse_results.params,
1615 parse_results.query,
1616 parse_results.fragment))
1617
1618 request = urllib.request.Request(url, request_body)
1619 if extra_headers is not None:
1620 for (name, header) in extra_headers:
1621 request.add_header(name, header)
1622 request.add_header('Content-Type', 'text/xml')
1623 try:
1624 response = opener.open(request)
1625 except urllib.error.HTTPError as e:
1626 if e.code == 501:
1627 # We may have been redirected through a login process
1628 # but our POST turned into a GET. Retry.
1629 response = opener.open(request)
1630 else:
1631 raise
1632
1633 p, u = xmlrpc.client.getparser()
Mike Frysinger5951e302022-05-20 23:34:44 -04001634 # Response should be fairly small, so read it all at once.
1635 # This way we can show it to the user in case of error (e.g. HTML).
1636 data = response.read()
1637 try:
Dan Willemsen0745bb22015-08-17 13:41:45 -07001638 p.feed(data)
Mike Frysinger5951e302022-05-20 23:34:44 -04001639 except xml.parsers.expat.ExpatError as e:
1640 raise IOError(
1641 f'Parsing the manifest failed: {e}\n'
1642 f'Please report this to your manifest server admin.\n'
1643 f'Here is the full response:\n{data.decode("utf-8")}')
Dan Willemsen0745bb22015-08-17 13:41:45 -07001644 p.close()
1645 return u.close()
1646
1647 def close(self):
1648 pass