blob: fb25c221f21f7347f3827093c16a4634681b783b [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
jiajia tanga590e642021-04-25 20:02:02 +080015import errno
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.error
29import urllib.parse
30import urllib.request
31import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032
Roy Lee18afd7f2010-05-09 04:32:08 +080033try:
34 import threading as _threading
35except ImportError:
36 import dummy_threading as _threading
37
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070038try:
39 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090040
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070041 def _rlimit_nofile():
42 return resource.getrlimit(resource.RLIMIT_NOFILE)
43except ImportError:
44 def _rlimit_nofile():
45 return (256, 256)
46
David Rileye0684ad2017-04-05 00:02:59 -070047import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040048from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090049from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090050from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080051import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070052import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070053from project import Project
54from project import RemoteSpec
Mike Frysingerd41eed02021-04-20 23:21:29 -040055from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080056from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070057import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070058from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070059from progress import Progress
Mike Frysinger19e409c2021-05-05 19:44:35 -040060import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080061from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070062from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070063
Dave Borowitz67700e92012-10-23 15:00:54 -070064_ONE_DAY_S = 24 * 60 * 60
65
David Pursehouse819827a2020-02-12 15:20:19 +090066
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080067class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080068 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070069 common = True
70 helpSummary = "Update working tree to the latest revision"
71 helpUsage = """
72%prog [...]
73"""
74 helpDescription = """
75The '%prog' command synchronizes local project directories
76with the remote repositories specified in the manifest. If a local
77project does not yet exist, it will clone a new local directory from
78the remote repository and set up tracking branches as specified in
79the manifest. If the local project already exists, '%prog'
80will update the remote branches and rebase any new local changes
81on top of the new remote changes.
82
83'%prog' will synchronize all projects listed at the command
84line. Projects can be specified either by name, or by a relative
85or absolute path to the project's local directory. If no projects
86are specified, '%prog' will synchronize all projects listed in
87the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070088
89The -d/--detach option can be used to switch specified projects
90back to the manifest revision. This option is especially helpful
91if the project is currently on a topic branch, but the manifest
92revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070093
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070094The -s/--smart-sync option can be used to sync to a known good
95build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020096manifest. The -t/--smart-tag option is similar and allows you to
97specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070098
David Pursehousecf76b1b2012-09-14 10:31:42 +090099The -u/--manifest-server-username and -p/--manifest-server-password
100options can be used to specify a username and password to authenticate
101with the manifest server when using the -s or -t option.
102
103If -u and -p are not specified when using the -s or -t option, '%prog'
104will attempt to read authentication credentials for the manifest server
105from the user's .netrc file.
106
107'%prog' will not use authentication credentials from -u/-p or .netrc
108if the manifest server specified in the manifest file already includes
109credentials.
110
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400111By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400112to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500113
Kevin Degiabaa7f32014-11-12 11:27:45 -0700114The --force-sync option can be used to overwrite existing git
115directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900116object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700117refs may be removed when overwriting.
118
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500119The --force-remove-dirty option can be used to remove previously used
120projects with uncommitted changes. WARNING: This may cause data to be
121lost since uncommitted changes may be removed with projects that no longer
122exist in the manifest.
123
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700124The --no-clone-bundle option disables any attempt to use
125$URL/clone.bundle to bootstrap a new Git repository from a
126resumeable bundle file on a content delivery network. This
127may be necessary if there are problems with the local Python
128HTTP client or proxy configuration, but the Git binary works.
129
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800130The --fetch-submodules option enables fetching Git submodules
131of a project from server.
132
David Pursehousef2fad612015-01-29 14:36:28 +0900133The -c/--current-branch option can be used to only fetch objects that
134are on the branch specified by a project's revision.
135
David Pursehouseb1553542014-09-04 21:28:09 +0900136The --optimized-fetch option can be used to only fetch projects that
137are fixed to a sha1 revision if the sha1 revision does not already
138exist locally.
139
David Pursehouse74cfd272015-10-14 10:50:15 +0900140The --prune option can be used to remove any refs that no longer
141exist on the remote.
142
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400143# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700144
145If at least one project remote URL uses an SSH connection (ssh://,
146git+ssh://, or user@host:path syntax) repo will automatically
147enable the SSH ControlMaster option when connecting to that host.
148This feature permits other projects in the same '%prog' session to
149reuse the same SSH tunnel, saving connection setup overheads.
150
151To disable this behavior on UNIX platforms, set the GIT_SSH
152environment variable to 'ssh'. For example:
153
154 export GIT_SSH=ssh
155 %prog
156
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400157# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700158
159This feature is automatically disabled on Windows, due to the lack
160of UNIX domain socket support.
161
162This feature is not compatible with url.insteadof rewrites in the
163user's ~/.gitconfig. '%prog' is currently not able to perform the
164rewrite early enough to establish the ControlMaster tunnel.
165
166If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
167later is required to fix a server side protocol bug.
168
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500170 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171
Mike Frysinger9180a072021-04-13 14:57:40 -0400172 def _CommonOptions(self, p):
Mike Frysingerc177f942021-05-04 08:06:36 -0400173 if self.manifest:
174 try:
175 self.PARALLEL_JOBS = self.manifest.default.sync_j
176 except ManifestParseError:
177 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400178 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700179
Mike Frysinger9180a072021-04-13 14:57:40 -0400180 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400181 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
182 help='number of network jobs to run in parallel (defaults to --jobs)')
183 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
184 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
185
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500186 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200187 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400188 help='obsolete option (to be deleted in the future)')
189 p.add_option('--fail-fast',
190 dest='fail_fast', action='store_true',
191 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700192 p.add_option('--force-sync',
193 dest='force_sync', action='store_true',
194 help="overwrite an existing git directory if it needs to "
195 "point to a different object directory. WARNING: this "
196 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500197 p.add_option('--force-remove-dirty',
198 dest='force_remove_dirty', action='store_true',
199 help="force remove projects with uncommitted modifications if "
200 "projects no longer exist in the manifest. "
201 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900202 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700203 dest='local_only', action='store_true',
204 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900205 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100206 dest='mp_update', action='store_false', default='true',
207 help='use the existing manifest checkout as-is. '
208 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700210 dest='network_only', action='store_true',
211 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700213 dest='detach_head', action='store_true',
214 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700216 dest='current_branch_only', action='store_true',
217 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400218 p.add_option('--no-current-branch',
219 dest='current_branch_only', action='store_false',
220 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500221 p.add_option('-m', '--manifest-name',
222 dest='manifest_name',
223 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700224 p.add_option('--clone-bundle', action='store_true',
225 help='enable use of /clone.bundle on HTTP/HTTPS')
226 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700227 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800228 p.add_option('-u', '--manifest-server-username', action='store',
229 dest='manifest_server_username',
230 help='username to authenticate with the manifest server')
231 p.add_option('-p', '--manifest-server-password', action='store',
232 dest='manifest_server_password',
233 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800234 p.add_option('--fetch-submodules',
235 dest='fetch_submodules', action='store_true',
236 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800237 p.add_option('--use-superproject', action='store_true',
238 help='use the manifest superproject to sync projects')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700239 p.add_option('--no-use-superproject', action='store_false',
240 dest='use_superproject',
241 help='disable use of manifest superprojects')
Mike Frysingerd68ed632021-05-03 01:21:35 -0400242 p.add_option('--tags',
243 action='store_false',
244 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700245 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400246 dest='tags', action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700247 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900248 p.add_option('--optimized-fetch',
249 dest='optimized_fetch', action='store_true',
250 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600251 p.add_option('--retry-fetches',
252 default=0, action='store', type='int',
253 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900254 p.add_option('--prune', dest='prune', action='store_true',
255 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700256 if show_smart:
257 p.add_option('-s', '--smart-sync',
258 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900259 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200260 p.add_option('-t', '--smart-tag',
261 dest='smart_tag', action='store',
262 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700263
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700264 g = p.add_option_group('repo Version options')
265 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500266 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700268 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800269 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700270 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800272 def _GetBranch(self):
273 """Returns the branch name for getting the approved manifest."""
274 p = self.manifest.manifestProject
275 b = p.GetBranch(p.CurrentBranch)
276 branch = b.merge
277 if branch.startswith(R_HEADS):
278 branch = branch[len(R_HEADS):]
279 return branch
280
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700281 def _UseSuperproject(self, opt):
282 """Returns True if use-superproject option is enabled"""
Raman Tenneti23ea7542021-05-07 14:01:54 -0700283 if opt.use_superproject is not None:
284 return opt.use_superproject
285 else:
286 return self.manifest.manifestProject.config.GetBoolean('repo.superproject')
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700287
288 def _GetCurrentBranchOnly(self, opt):
289 """Returns True if current-branch or use-superproject options are enabled."""
290 return opt.current_branch_only or self._UseSuperproject(opt)
291
Raman Tennetifeb28912021-05-02 19:47:29 -0700292 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800293 """Update revisionId of every project with the SHA from superproject.
294
295 This function updates each project's revisionId with SHA from superproject.
296 It writes the updated manifest into a file and reloads the manifest from it.
297
298 Args:
299 opt: Program options returned from optparse. See _Options().
300 args: Arguments to pass to GetProjects. See the GetProjects
301 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700302 load_local_manifests: Whether to load local manifests.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800303
304 Returns:
305 Returns path to the overriding manifest file.
306 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800307 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800308 self.repodir,
309 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800310 all_projects = self.GetProjects(args,
311 missing_ok=True,
312 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800313 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800314 if not manifest_path:
Raman Tennetif907ced2021-05-07 10:22:16 -0700315 print('error: Update of revsionId from superproject has failed. '
316 'Please resync with --no-use-superproject option',
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800317 file=sys.stderr)
318 sys.exit(1)
Raman Tennetifeb28912021-05-02 19:47:29 -0700319 self._ReloadManifest(manifest_path, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800320 return manifest_path
321
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500322 def _FetchProjectList(self, opt, projects):
323 """Main function of the fetch worker.
324
325 The projects we're given share the same underlying git object store, so we
326 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800327
David James8d201162013-10-11 17:03:19 -0700328 Delegates most of the work to _FetchHelper.
329
330 Args:
331 opt: Program options returned from optparse. See _Options().
332 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700333 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500334 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700335
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500336 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700337 """Fetch git objects for a single project.
338
David Pursehousec1b86a22012-11-14 11:36:51 +0900339 Args:
340 opt: Program options returned from optparse. See _Options().
341 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700342
343 Returns:
344 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900345 """
David Rileye0684ad2017-04-05 00:02:59 -0700346 start = time.time()
347 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500348 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500350 success = project.Sync_NetworkHalf(
351 quiet=opt.quiet,
352 verbose=opt.verbose,
353 output_redir=buf,
354 current_branch_only=self._GetCurrentBranchOnly(opt),
355 force_sync=opt.force_sync,
356 clone_bundle=opt.clone_bundle,
357 tags=opt.tags, archive=self.manifest.IsArchive,
358 optimized_fetch=opt.optimized_fetch,
359 retry_fetches=opt.retry_fetches,
360 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400361 ssh_proxy=self.ssh_proxy,
Raman Tennetif32f2432021-04-12 20:57:25 -0700362 clone_filter=self.manifest.CloneFilter,
363 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700364
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500365 output = buf.getvalue()
366 if opt.verbose and output:
367 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700368
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500369 if not success:
370 print('error: Cannot fetch %s from %s'
371 % (project.name, project.remote.url),
372 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700373 except GitError as e:
374 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500375 except Exception as e:
376 print('error: Cannot fetch %s (%s: %s)'
377 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
378 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500379
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500380 finish = time.time()
381 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700382
Mike Frysinger339f2df2021-05-06 00:44:42 -0400383 @classmethod
384 def _FetchInitChild(cls, ssh_proxy):
385 cls.ssh_proxy = ssh_proxy
386
387 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500388 ret = True
389
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400390 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700391 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400392 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800393
David James89ece422014-01-09 18:51:58 -0800394 objdir_project_map = dict()
395 for project in projects:
396 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500397 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700398
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500399 def _ProcessResults(results_sets):
400 ret = True
401 for results in results_sets:
402 for (success, project, start, finish) in results:
403 self._fetch_times.Set(project, finish - start)
404 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
405 start, finish, success)
406 # Check for any errors before running any more tasks.
407 # ...we'll let existing jobs finish, though.
408 if not success:
409 ret = False
410 else:
411 fetched.add(project.gitdir)
412 pm.update(msg=project.name)
413 if not ret and opt.fail_fast:
414 break
415 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700416
Mike Frysinger339f2df2021-05-06 00:44:42 -0400417 # We pass the ssh proxy settings via the class. This allows multiprocessing
418 # to pickle it up when spawning children. We can't pass it as an argument
419 # to _FetchProjectList below as multiprocessing is unable to pickle those.
420 Sync.ssh_proxy = None
421
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500422 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400423 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400424 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500425 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
426 ret = False
427 else:
428 # Favor throughput over responsiveness when quiet. It seems that imap()
429 # will yield results in batches relative to chunksize, so even as the
430 # children finish a sync, we won't see the result until one child finishes
431 # ~chunksize jobs. When using a large --jobs with large chunksize, this
432 # can be jarring as there will be a large initial delay where repo looks
433 # like it isn't doing anything and sits at 0%, but then suddenly completes
434 # a lot of jobs all at once. Since this code is more network bound, we
435 # can accept a bit more CPU overhead with a smaller chunksize so that the
436 # user sees more immediate & continuous feedback.
437 if opt.quiet:
438 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800439 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500440 pm.update(inc=0, msg='warming up')
441 chunksize = 4
Mike Frysinger339f2df2021-05-06 00:44:42 -0400442 with multiprocessing.Pool(
443 jobs, initializer=self._FetchInitChild, initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500444 results = pool.imap_unordered(
445 functools.partial(self._FetchProjectList, opt),
446 projects_list,
447 chunksize=chunksize)
448 if not _ProcessResults(results):
449 ret = False
450 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800451
Mike Frysinger339f2df2021-05-06 00:44:42 -0400452 # Cleanup the reference now that we're done with it, and we're going to
453 # release any resources it points to. If we don't, later multiprocessing
454 # usage (e.g. checkouts) will try to pickle and then crash.
455 del Sync.ssh_proxy
456
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700457 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700458 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700459
Julien Campergue335f5ef2013-10-16 11:02:35 +0200460 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400461 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200462
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500463 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700464
Mike Frysingerb4429432021-05-05 20:03:26 -0400465 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400466 load_local_manifests, ssh_proxy):
Mike Frysingerb4429432021-05-05 20:03:26 -0400467 """The main network fetch loop.
468
469 Args:
470 opt: Program options returned from optparse. See _Options().
471 args: Command line args used to filter out projects.
472 all_projects: List of all projects that should be checked out.
473 err_event: Whether an error was hit while processing.
474 manifest_name: Manifest file to be reloaded.
475 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400476 ssh_proxy: SSH manager for clients & masters.
Mike Frysingerb4429432021-05-05 20:03:26 -0400477 """
478 rp = self.manifest.repoProject
479
480 to_fetch = []
481 now = time.time()
482 if _ONE_DAY_S <= (now - rp.LastFetch):
483 to_fetch.append(rp)
484 to_fetch.extend(all_projects)
485 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
486
Mike Frysinger339f2df2021-05-06 00:44:42 -0400487 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400488 if not success:
489 err_event.set()
490
491 _PostRepoFetch(rp, opt.repo_verify)
492 if opt.network_only:
493 # bail out now; the rest touches the working tree
494 if err_event.is_set():
495 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
496 sys.exit(1)
497 return
498
499 # Iteratively fetch missing and/or nested unregistered submodules
500 previously_missing_set = set()
501 while True:
502 self._ReloadManifest(manifest_name, load_local_manifests)
503 all_projects = self.GetProjects(args,
504 missing_ok=True,
505 submodules_ok=opt.fetch_submodules)
506 missing = []
507 for project in all_projects:
508 if project.gitdir not in fetched:
509 missing.append(project)
510 if not missing:
511 break
512 # Stop us from non-stopped fetching actually-missing repos: If set of
513 # missing repos has not been changed from last fetch, we break.
514 missing_set = set(p.name for p in missing)
515 if previously_missing_set == missing_set:
516 break
517 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400518 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400519 if not success:
520 err_event.set()
521 fetched.update(new_fetched)
522
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500523 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700524 """Checkout work tree for one project
525
526 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500527 detach_head: Whether to leave a detached HEAD.
528 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700529 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700530
531 Returns:
532 Whether the fetch was successful.
533 """
Xin Li745be2e2019-06-03 11:24:30 -0700534 start = time.time()
535 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500536 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700537 success = False
538 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500539 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500540 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700541 except GitError as e:
542 print('error.GitError: Cannot checkout %s: %s' %
543 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500544 except Exception as e:
545 print('error: Cannot checkout %s: %s: %s' %
546 (project.name, type(e).__name__, str(e)),
547 file=sys.stderr)
548 raise
Xin Li745be2e2019-06-03 11:24:30 -0700549
Mike Frysingerebf04a42021-02-23 20:48:04 -0500550 if not success:
551 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
552 finish = time.time()
553 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700554
Mike Frysingerebf04a42021-02-23 20:48:04 -0500555 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700556 """Checkout projects listed in all_projects
557
558 Args:
559 all_projects: List of all projects that should be checked out.
560 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500561 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700562 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500563 # Only checkout projects with worktrees.
564 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700565
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500566 def _ProcessResults(pool, pm, results):
567 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500568 for (success, project, start, finish) in results:
569 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
570 start, finish, success)
571 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500572 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500573 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500574 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500575 err_results.append(project.relpath)
576 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500577 if pool:
578 pool.close()
579 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500580 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500581 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700582
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500583 return self.ExecuteInParallel(
584 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
585 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
586 all_projects,
587 callback=_ProcessResults,
588 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500589
Mike Frysinger5a033082019-09-23 19:21:20 -0400590 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400591 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400592 pm.update(inc=0, msg='prescan')
593
Gabe Black2ff30292014-10-09 17:54:35 -0700594 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700595 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500596 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500597 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900598 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100599 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400600 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100601 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500602 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500603 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500604 else:
605 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400606 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500607 'versions of git; please upgrade to git-2.7.0+.'
608 % (project.relpath,),
609 file=sys.stderr)
610 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700611 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700612
Mike Frysinger65af2602021-04-08 22:47:44 -0400613 pm.update(inc=len(projects) - len(gc_gitdirs), msg='warming up')
614
615 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700616 jobs = min(self.jobs, cpu_count)
617
618 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700619 for bare_git in gc_gitdirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400620 pm.update(msg=bare_git._project.name)
David James8d201162013-10-11 17:03:19 -0700621 bare_git.gc('--auto')
Mike Frysinger65af2602021-04-08 22:47:44 -0400622 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700623 return
624
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400625 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700626
627 threads = set()
628 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700629
David James8d201162013-10-11 17:03:19 -0700630 def GC(bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400631 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700632 try:
633 try:
David James8d201162013-10-11 17:03:19 -0700634 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700635 except GitError:
636 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900637 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700638 err_event.set()
639 raise
640 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400641 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700642 sem.release()
643
Gabe Black2ff30292014-10-09 17:54:35 -0700644 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500645 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700646 break
647 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700648 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700649 t.daemon = True
650 threads.add(t)
651 t.start()
652
653 for t in threads:
654 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400655 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700656
Raman Tennetifeb28912021-05-02 19:47:29 -0700657 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
658 """Reload the manfiest from the file specified by the |manifest_name|.
659
660 It unloads the manifest if |manifest_name| is None.
661
662 Args:
663 manifest_name: Manifest file to be reloaded.
664 load_local_manifests: Whether to load local manifests.
665 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800666 if manifest_name:
667 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700668 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800669 else:
670 self.manifest._Unload()
671
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500672 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700673 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700674 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700675 if project.relpath:
676 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700677 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500678 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700679 old_project_paths = []
680
681 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500682 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700683 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800684 # In reversed order, so subfolders are deleted before parent folder.
685 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700686 if not path:
687 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700688 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900689 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700690 gitdir = os.path.join(self.manifest.topdir, path, '.git')
691 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900692 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900693 manifest=self.manifest,
694 name=path,
695 remote=RemoteSpec('origin'),
696 gitdir=gitdir,
697 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500698 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900699 worktree=os.path.join(self.manifest.topdir, path),
700 relpath=path,
701 revisionExpr='HEAD',
702 revisionId=None,
703 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500704 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900705 quiet=opt.quiet,
706 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400707 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700708
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700709 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500710 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700711 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700712 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700713 return 0
714
jiajia tanga590e642021-04-25 20:02:02 +0800715 def UpdateCopyLinkfileList(self):
716 """Save all dests of copyfile and linkfile, and update them if needed.
717
718 Returns:
719 Whether update was successful.
720 """
721 new_paths = {}
722 new_linkfile_paths = []
723 new_copyfile_paths = []
724 for project in self.GetProjects(None, missing_ok=True):
725 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
726 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
727
728 new_paths = {
729 'linkfile': new_linkfile_paths,
730 'copyfile': new_copyfile_paths,
731 }
732
733 copylinkfile_name = 'copy-link-files.json'
734 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
735 old_copylinkfile_paths = {}
736
737 if os.path.exists(copylinkfile_path):
738 with open(copylinkfile_path, 'rb') as fp:
739 try:
740 old_copylinkfile_paths = json.load(fp)
741 except:
742 print('error: %s is not a json formatted file.' %
743 copylinkfile_path, file=sys.stderr)
744 platform_utils.remove(copylinkfile_path)
745 return False
746
747 need_remove_files = []
748 need_remove_files.extend(
749 set(old_copylinkfile_paths.get('linkfile', [])) -
750 set(new_linkfile_paths))
751 need_remove_files.extend(
752 set(old_copylinkfile_paths.get('copyfile', [])) -
753 set(new_copyfile_paths))
754
755 for need_remove_file in need_remove_files:
756 try:
757 platform_utils.remove(need_remove_file)
758 except OSError as e:
759 if e.errno == errno.ENOENT:
760 # Try to remove the updated copyfile or linkfile.
761 # So, if the file is not exist, nothing need to do.
762 pass
763
764 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
765 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
766 json.dump(new_paths, fp)
767 return True
768
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400769 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
770 if not self.manifest.manifest_server:
771 print('error: cannot smart sync: no manifest server defined in '
772 'manifest', file=sys.stderr)
773 sys.exit(1)
774
775 manifest_server = self.manifest.manifest_server
776 if not opt.quiet:
777 print('Using manifest server %s' % manifest_server)
778
David Pursehouseeeff3532020-02-12 11:24:10 +0900779 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400780 username = None
781 password = None
782 if opt.manifest_server_username and opt.manifest_server_password:
783 username = opt.manifest_server_username
784 password = opt.manifest_server_password
785 else:
786 try:
787 info = netrc.netrc()
788 except IOError:
789 # .netrc file does not exist or could not be opened
790 pass
791 else:
792 try:
793 parse_result = urllib.parse.urlparse(manifest_server)
794 if parse_result.hostname:
795 auth = info.authenticators(parse_result.hostname)
796 if auth:
797 username, _account, password = auth
798 else:
799 print('No credentials found for %s in .netrc'
800 % parse_result.hostname, file=sys.stderr)
801 except netrc.NetrcParseError as e:
802 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
803
804 if (username and password):
805 manifest_server = manifest_server.replace('://', '://%s:%s@' %
806 (username, password),
807 1)
808
809 transport = PersistentTransport(manifest_server)
810 if manifest_server.startswith('persistent-'):
811 manifest_server = manifest_server[len('persistent-'):]
812
813 try:
814 server = xmlrpc.client.Server(manifest_server, transport=transport)
815 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800816 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400817
Mike Frysinger56ce3462019-12-04 19:30:48 -0500818 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500819 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400820 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500821 elif ('TARGET_PRODUCT' in os.environ and
822 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500823 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
824 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400825 [success, manifest_str] = server.GetApprovedManifest(branch, target)
826 else:
827 [success, manifest_str] = server.GetApprovedManifest(branch)
828 else:
829 assert(opt.smart_tag)
830 [success, manifest_str] = server.GetManifest(opt.smart_tag)
831
832 if success:
833 manifest_name = os.path.basename(smart_sync_manifest_path)
834 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500835 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400836 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400837 except IOError as e:
838 print('error: cannot write manifest to %s:\n%s'
839 % (smart_sync_manifest_path, e),
840 file=sys.stderr)
841 sys.exit(1)
842 self._ReloadManifest(manifest_name)
843 else:
844 print('error: manifest server RPC call failed: %s' %
845 manifest_str, file=sys.stderr)
846 sys.exit(1)
847 except (socket.error, IOError, xmlrpc.client.Fault) as e:
848 print('error: cannot connect to manifest server %s:\n%s'
849 % (self.manifest.manifest_server, e), file=sys.stderr)
850 sys.exit(1)
851 except xmlrpc.client.ProtocolError as e:
852 print('error: cannot connect to manifest server %s:\n%d %s'
853 % (self.manifest.manifest_server, e.errcode, e.errmsg),
854 file=sys.stderr)
855 sys.exit(1)
856
857 return manifest_name
858
Mike Frysingerfb527e32019-08-27 02:34:32 -0400859 def _UpdateManifestProject(self, opt, mp, manifest_name):
860 """Fetch & update the local manifest project."""
861 if not opt.local_only:
862 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500863 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700864 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200865 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500866 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400867 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600868 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400869 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700870 clone_filter=self.manifest.CloneFilter,
871 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400872 finish = time.time()
873 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
874 start, finish, success)
875
876 if mp.HasChanges:
877 syncbuf = SyncBuffer(mp.config)
878 start = time.time()
879 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
880 clean = syncbuf.Finish()
881 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
882 start, time.time(), clean)
883 if not clean:
884 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400885 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400886 if opt.jobs is None:
887 self.jobs = self.manifest.default.sync_j
888
Mike Frysingerae6cb082019-08-27 01:10:59 -0400889 def ValidateOptions(self, opt, args):
890 if opt.force_broken:
891 print('warning: -f/--force-broken is now the default behavior, and the '
892 'options are deprecated', file=sys.stderr)
893 if opt.network_only and opt.detach_head:
894 self.OptionParser.error('cannot combine -n and -d')
895 if opt.network_only and opt.local_only:
896 self.OptionParser.error('cannot combine -n and -l')
897 if opt.manifest_name and opt.smart_sync:
898 self.OptionParser.error('cannot combine -m and -s')
899 if opt.manifest_name and opt.smart_tag:
900 self.OptionParser.error('cannot combine -m and -t')
901 if opt.manifest_server_username or opt.manifest_server_password:
902 if not (opt.smart_sync or opt.smart_tag):
903 self.OptionParser.error('-u and -p may only be combined with -s or -t')
904 if None in [opt.manifest_server_username, opt.manifest_server_password]:
905 self.OptionParser.error('both -u and -p must be given')
906
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700907 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800908 if opt.jobs:
909 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700910 if self.jobs > 1:
911 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400912 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700913
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500914 if opt.manifest_name:
915 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700916
Chirayu Desaia892b102013-06-11 14:18:46 +0530917 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900918 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900919 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530920
Xin Lid79a4bc2020-05-20 16:03:45 -0700921 if opt.clone_bundle is None:
922 opt.clone_bundle = self.manifest.CloneBundle
923
Victor Boivie08c880d2011-04-19 10:32:52 +0200924 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400925 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
926 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900927 if os.path.isfile(smart_sync_manifest_path):
928 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800929 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900930 except OSError as e:
931 print('error: failed to remove existing smart sync override manifest: %s' %
932 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700933
Mike Frysingerc99322a2021-05-04 15:32:43 -0400934 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400935
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700936 rp = self.manifest.repoProject
937 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500938 cb = rp.CurrentBranch
939 if cb:
940 base = rp.GetBranch(cb).merge
941 if not base or not base.startswith('refs/heads/'):
942 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400943 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500944 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700945
946 mp = self.manifest.manifestProject
947 mp.PreSync()
948
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800949 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700950 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800951
Fredrik de Grootcc960972019-11-22 09:04:31 +0100952 if not opt.mp_update:
953 print('Skipping update of local manifest project.')
954 else:
955 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700956
Raman Tennetifeb28912021-05-02 19:47:29 -0700957 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700958 if self._UseSuperproject(opt):
Raman Tennetifeb28912021-05-02 19:47:29 -0700959 manifest_name = self._UpdateProjectsRevisionId(opt, args, load_local_manifests)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800960
Simran Basib9a1b732015-08-20 12:19:28 -0700961 if self.gitc_manifest:
962 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700963 missing_ok=True)
964 gitc_projects = []
965 opened_projects = []
966 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700967 if project.relpath in self.gitc_manifest.paths and \
968 self.gitc_manifest.paths[project.relpath].old_revision:
969 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700970 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700971 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700972
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700973 if not args:
974 gitc_projects = None
975
976 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700977 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700978 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
979 if manifest_name:
980 manifest.Override(manifest_name)
981 else:
982 manifest.Override(self.manifest.manifestFile)
983 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
984 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700985 gitc_projects)
986 print('GITC client successfully synced.')
987
988 # The opened projects need to be synced as normal, therefore we
989 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700990 # TODO: make this more reliable -- if there's a project name/path overlap,
991 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900992 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
993 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700994 if not args:
995 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800996 all_projects = self.GetProjects(args,
997 missing_ok=True,
998 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700999
Mike Frysinger5a033082019-09-23 19:21:20 -04001000 err_network_sync = False
1001 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001002
Dave Borowitz67700e92012-10-23 15:00:54 -07001003 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001004 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001005 with multiprocessing.Manager() as manager:
1006 with ssh.ProxyManager(manager) as ssh_proxy:
1007 # Initialize the socket dir once in the parent.
1008 ssh_proxy.sock()
1009 self._FetchMain(opt, args, all_projects, err_event, manifest_name,
1010 load_local_manifests, ssh_proxy)
1011
1012 if opt.network_only:
1013 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001014
1015 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001016 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001017 err_network_sync = True
1018 if opt.fail_fast:
1019 print('\nerror: Exited sync due to fetch errors.\n'
1020 'Local checkouts *not* updated. Resolve network issues & '
1021 'retry.\n'
1022 '`repo sync -l` will update some local checkouts.',
1023 file=sys.stderr)
1024 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001025
Julien Campergue335f5ef2013-10-16 11:02:35 +02001026 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001027 # bail out now, we have no working tree
1028 return
1029
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001030 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001031 err_event.set()
1032 err_update_projects = True
1033 if opt.fail_fast:
1034 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1035 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001036
Mike Frysinger14208f42021-05-04 15:31:51 -04001037 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1038 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001039 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001040 if opt.fail_fast:
1041 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1042 sys.exit(1)
1043
Mike Frysinger5a033082019-09-23 19:21:20 -04001044 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001045 # NB: We don't exit here because this is the last step.
1046 err_checkout = not self._Checkout(all_projects, opt, err_results)
1047 if err_checkout:
1048 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001050 # If there's a notice that's supposed to print at the end of the sync, print
1051 # it now...
1052 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001053 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001054
Mike Frysinger5a033082019-09-23 19:21:20 -04001055 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001056 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001057 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1058 if err_network_sync:
1059 print('error: Downloading network changes failed.', file=sys.stderr)
1060 if err_update_projects:
1061 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001062 if err_update_linkfiles:
1063 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001064 if err_checkout:
1065 print('error: Checking out local projects failed.', file=sys.stderr)
1066 if err_results:
1067 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1068 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1069 file=sys.stderr)
1070 sys.exit(1)
1071
Mike Frysingere19d9e12020-02-12 11:23:32 -05001072 if not opt.quiet:
1073 print('repo sync has finished successfully.')
1074
David Pursehouse819827a2020-02-12 15:20:19 +09001075
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001076def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001077 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001078 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001079 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001080 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001081 if project.Exists:
1082 project.PostRepoUpgrade()
1083
David Pursehouse819827a2020-02-12 15:20:19 +09001084
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001085def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001086 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001087 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001088 wrapper = Wrapper()
1089 try:
1090 rev = rp.bare_git.describe(rp.GetRevisionId())
1091 except GitError:
1092 rev = None
1093 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1094 # See if we're held back due to missing signed tag.
1095 current_revid = rp.bare_git.rev_parse('HEAD')
1096 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1097 if current_revid != new_revid:
1098 # We want to switch to the new rev, but also not trash any uncommitted
1099 # changes. This helps with local testing/hacking.
1100 # If a local change has been made, we will throw that away.
1101 # We also have to make sure this will switch to an older commit if that's
1102 # the latest tag in order to support release rollback.
1103 try:
1104 rp.work_git.reset('--keep', new_rev)
1105 except GitError as e:
1106 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001107 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001108 raise RepoChangedException(['--repo-upgraded'])
1109 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001110 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001111 else:
1112 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001113 print('repo version %s is current' % rp.work_git.describe(HEAD),
1114 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001115
David Pursehouse819827a2020-02-12 15:20:19 +09001116
Dave Borowitz67700e92012-10-23 15:00:54 -07001117class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001118 _ALPHA = 0.5
1119
Dave Borowitz67700e92012-10-23 15:00:54 -07001120 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001121 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001122 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001123 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001124
1125 def Get(self, project):
1126 self._Load()
1127 return self._times.get(project.name, _ONE_DAY_S)
1128
1129 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001130 self._Load()
1131 name = project.name
1132 old = self._times.get(name, t)
1133 self._seen.add(name)
1134 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001135 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001136
1137 def _Load(self):
1138 if self._times is None:
1139 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001140 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001141 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001142 except (IOError, ValueError):
1143 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001144 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001145 except OSError:
1146 pass
1147 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001148
1149 def Save(self):
1150 if self._times is None:
1151 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001152
1153 to_delete = []
1154 for name in self._times:
1155 if name not in self._seen:
1156 to_delete.append(name)
1157 for name in to_delete:
1158 del self._times[name]
1159
Dave Borowitz67700e92012-10-23 15:00:54 -07001160 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001161 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001162 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001163 except (IOError, TypeError):
1164 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001165 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001166 except OSError:
1167 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001168
1169# This is a replacement for xmlrpc.client.Transport using urllib2
1170# and supporting persistent-http[s]. It cannot change hosts from
1171# request to request like the normal transport, the real url
1172# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001173
1174
Dan Willemsen0745bb22015-08-17 13:41:45 -07001175class PersistentTransport(xmlrpc.client.Transport):
1176 def __init__(self, orig_host):
1177 self.orig_host = orig_host
1178
1179 def request(self, host, handler, request_body, verbose=False):
1180 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1181 # Python doesn't understand cookies with the #HttpOnly_ prefix
1182 # Since we're only using them for HTTP, copy the file temporarily,
1183 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001184 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001185 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001186 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001187 try:
1188 with open(cookiefile) as f:
1189 for line in f:
1190 if line.startswith("#HttpOnly_"):
1191 line = line[len("#HttpOnly_"):]
1192 tmpcookiefile.write(line)
1193 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001194
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001195 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001196 try:
1197 cookiejar.load()
1198 except cookielib.LoadError:
1199 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001200 finally:
1201 tmpcookiefile.close()
1202 else:
1203 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001204
1205 proxyhandler = urllib.request.ProxyHandler
1206 if proxy:
1207 proxyhandler = urllib.request.ProxyHandler({
1208 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001209 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001210
1211 opener = urllib.request.build_opener(
1212 urllib.request.HTTPCookieProcessor(cookiejar),
1213 proxyhandler)
1214
1215 url = urllib.parse.urljoin(self.orig_host, handler)
1216 parse_results = urllib.parse.urlparse(url)
1217
1218 scheme = parse_results.scheme
1219 if scheme == 'persistent-http':
1220 scheme = 'http'
1221 if scheme == 'persistent-https':
1222 # If we're proxying through persistent-https, use http. The
1223 # proxy itself will do the https.
1224 if proxy:
1225 scheme = 'http'
1226 else:
1227 scheme = 'https'
1228
1229 # Parse out any authentication information using the base class
1230 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1231
1232 url = urllib.parse.urlunparse((
1233 scheme,
1234 host,
1235 parse_results.path,
1236 parse_results.params,
1237 parse_results.query,
1238 parse_results.fragment))
1239
1240 request = urllib.request.Request(url, request_body)
1241 if extra_headers is not None:
1242 for (name, header) in extra_headers:
1243 request.add_header(name, header)
1244 request.add_header('Content-Type', 'text/xml')
1245 try:
1246 response = opener.open(request)
1247 except urllib.error.HTTPError as e:
1248 if e.code == 501:
1249 # We may have been redirected through a login process
1250 # but our POST turned into a GET. Retry.
1251 response = opener.open(request)
1252 else:
1253 raise
1254
1255 p, u = xmlrpc.client.getparser()
1256 while 1:
1257 data = response.read(1024)
1258 if not data:
1259 break
1260 p.feed(data)
1261 p.close()
1262 return u.close()
1263
1264 def close(self):
1265 pass