blob: d4e302ac21252f22e745686e2bf9a3484dcd7db3 [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
Mike Frysingerebf04a42021-02-23 20:48:04 -050015import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040016import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050017import io
Anthony King85b24ac2014-05-06 15:57:48 +010018import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050019import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090020import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070021from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070023import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070025import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070026import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040027import urllib.error
28import urllib.parse
29import urllib.request
30import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031
Roy Lee18afd7f2010-05-09 04:32:08 +080032try:
33 import threading as _threading
34except ImportError:
35 import dummy_threading as _threading
36
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070037try:
38 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090039
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070040 def _rlimit_nofile():
41 return resource.getrlimit(resource.RLIMIT_NOFILE)
42except ImportError:
43 def _rlimit_nofile():
44 return (256, 256)
45
David Rileye0684ad2017-04-05 00:02:59 -070046import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040047from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090048from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090049from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080050import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070051import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070052from project import Project
53from project import RemoteSpec
Mike Frysingerd41eed02021-04-20 23:21:29 -040054from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080055from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070056import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070057from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070058from progress import Progress
Mike Frysinger19e409c2021-05-05 19:44:35 -040059import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080060from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070061from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070062
Dave Borowitz67700e92012-10-23 15:00:54 -070063_ONE_DAY_S = 24 * 60 * 60
64
David Pursehouse819827a2020-02-12 15:20:19 +090065
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080066class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080067 jobs = 1
Mike Frysinger4f210542021-06-14 16:05:19 -040068 COMMON = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070069 helpSummary = "Update working tree to the latest revision"
70 helpUsage = """
71%prog [<project>...]
72"""
73 helpDescription = """
74The '%prog' command synchronizes local project directories
75with the remote repositories specified in the manifest. If a local
76project does not yet exist, it will clone a new local directory from
77the remote repository and set up tracking branches as specified in
78the manifest. If the local project already exists, '%prog'
79will update the remote branches and rebase any new local changes
80on top of the new remote changes.
81
82'%prog' will synchronize all projects listed at the command
83line. Projects can be specified either by name, or by a relative
84or absolute path to the project's local directory. If no projects
85are specified, '%prog' will synchronize all projects listed in
86the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070087
88The -d/--detach option can be used to switch specified projects
89back to the manifest revision. This option is especially helpful
90if the project is currently on a topic branch, but the manifest
91revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070092
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070093The -s/--smart-sync option can be used to sync to a known good
94build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020095manifest. The -t/--smart-tag option is similar and allows you to
96specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070097
David Pursehousecf76b1b2012-09-14 10:31:42 +090098The -u/--manifest-server-username and -p/--manifest-server-password
99options can be used to specify a username and password to authenticate
100with the manifest server when using the -s or -t option.
101
102If -u and -p are not specified when using the -s or -t option, '%prog'
103will attempt to read authentication credentials for the manifest server
104from the user's .netrc file.
105
106'%prog' will not use authentication credentials from -u/-p or .netrc
107if the manifest server specified in the manifest file already includes
108credentials.
109
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400110By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400111to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500112
Kevin Degiabaa7f32014-11-12 11:27:45 -0700113The --force-sync option can be used to overwrite existing git
114directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900115object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700116refs may be removed when overwriting.
117
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500118The --force-remove-dirty option can be used to remove previously used
119projects with uncommitted changes. WARNING: This may cause data to be
120lost since uncommitted changes may be removed with projects that no longer
121exist in the manifest.
122
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700123The --no-clone-bundle option disables any attempt to use
124$URL/clone.bundle to bootstrap a new Git repository from a
125resumeable bundle file on a content delivery network. This
126may be necessary if there are problems with the local Python
127HTTP client or proxy configuration, but the Git binary works.
128
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800129The --fetch-submodules option enables fetching Git submodules
130of a project from server.
131
David Pursehousef2fad612015-01-29 14:36:28 +0900132The -c/--current-branch option can be used to only fetch objects that
133are on the branch specified by a project's revision.
134
David Pursehouseb1553542014-09-04 21:28:09 +0900135The --optimized-fetch option can be used to only fetch projects that
136are fixed to a sha1 revision if the sha1 revision does not already
137exist locally.
138
David Pursehouse74cfd272015-10-14 10:50:15 +0900139The --prune option can be used to remove any refs that no longer
140exist on the remote.
141
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400142# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700143
144If at least one project remote URL uses an SSH connection (ssh://,
145git+ssh://, or user@host:path syntax) repo will automatically
146enable the SSH ControlMaster option when connecting to that host.
147This feature permits other projects in the same '%prog' session to
148reuse the same SSH tunnel, saving connection setup overheads.
149
150To disable this behavior on UNIX platforms, set the GIT_SSH
151environment variable to 'ssh'. For example:
152
153 export GIT_SSH=ssh
154 %prog
155
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400156# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700157
158This feature is automatically disabled on Windows, due to the lack
159of UNIX domain socket support.
160
161This feature is not compatible with url.insteadof rewrites in the
162user's ~/.gitconfig. '%prog' is currently not able to perform the
163rewrite early enough to establish the ControlMaster tunnel.
164
165If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
166later is required to fix a server side protocol bug.
167
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700168"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500169 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170
Mike Frysinger9180a072021-04-13 14:57:40 -0400171 def _CommonOptions(self, p):
Mike Frysingerc177f942021-05-04 08:06:36 -0400172 if self.manifest:
173 try:
174 self.PARALLEL_JOBS = self.manifest.default.sync_j
175 except ManifestParseError:
176 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400177 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700178
Mike Frysinger9180a072021-04-13 14:57:40 -0400179 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400180 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
181 help='number of network jobs to run in parallel (defaults to --jobs)')
182 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
183 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
184
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500185 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200186 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400187 help='obsolete option (to be deleted in the future)')
188 p.add_option('--fail-fast',
189 dest='fail_fast', action='store_true',
190 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700191 p.add_option('--force-sync',
192 dest='force_sync', action='store_true',
193 help="overwrite an existing git directory if it needs to "
194 "point to a different object directory. WARNING: this "
195 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500196 p.add_option('--force-remove-dirty',
197 dest='force_remove_dirty', action='store_true',
198 help="force remove projects with uncommitted modifications if "
199 "projects no longer exist in the manifest. "
200 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900201 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700202 dest='local_only', action='store_true',
203 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900204 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100205 dest='mp_update', action='store_false', default='true',
206 help='use the existing manifest checkout as-is. '
207 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900208 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700209 dest='network_only', action='store_true',
210 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700212 dest='detach_head', action='store_true',
213 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700215 dest='current_branch_only', action='store_true',
216 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400217 p.add_option('--no-current-branch',
218 dest='current_branch_only', action='store_false',
219 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500220 p.add_option('-m', '--manifest-name',
221 dest='manifest_name',
222 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700223 p.add_option('--clone-bundle', action='store_true',
224 help='enable use of /clone.bundle on HTTP/HTTPS')
225 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700226 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800227 p.add_option('-u', '--manifest-server-username', action='store',
228 dest='manifest_server_username',
229 help='username to authenticate with the manifest server')
230 p.add_option('-p', '--manifest-server-password', action='store',
231 dest='manifest_server_password',
232 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800233 p.add_option('--fetch-submodules',
234 dest='fetch_submodules', action='store_true',
235 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800236 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700237 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700238 p.add_option('--no-use-superproject', action='store_false',
239 dest='use_superproject',
240 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400241 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400242 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700243 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400244 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400245 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900246 p.add_option('--optimized-fetch',
247 dest='optimized_fetch', action='store_true',
248 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600249 p.add_option('--retry-fetches',
250 default=0, action='store', type='int',
251 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900252 p.add_option('--prune', dest='prune', action='store_true',
253 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700254 if show_smart:
255 p.add_option('-s', '--smart-sync',
256 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900257 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200258 p.add_option('-t', '--smart-tag',
259 dest='smart_tag', action='store',
260 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700261
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700262 g = p.add_option_group('repo Version options')
263 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500264 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700265 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700266 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800267 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700268 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700269
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800270 def _GetBranch(self):
271 """Returns the branch name for getting the approved manifest."""
272 p = self.manifest.manifestProject
273 b = p.GetBranch(p.CurrentBranch)
274 branch = b.merge
275 if branch.startswith(R_HEADS):
276 branch = branch[len(R_HEADS):]
277 return branch
278
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700279 def _GetCurrentBranchOnly(self, opt):
280 """Returns True if current-branch or use-superproject options are enabled."""
Xin Li0cb6e922021-06-16 10:19:00 -0700281 return opt.current_branch_only or git_superproject.UseSuperproject(opt, self.manifest)
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700282
Raman Tenneti7954de12021-07-28 14:36:49 -0700283 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800284 """Update revisionId of every project with the SHA from superproject.
285
286 This function updates each project's revisionId with SHA from superproject.
287 It writes the updated manifest into a file and reloads the manifest from it.
288
289 Args:
290 opt: Program options returned from optparse. See _Options().
291 args: Arguments to pass to GetProjects. See the GetProjects
292 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700293 load_local_manifests: Whether to load local manifests.
Raman Tenneti7954de12021-07-28 14:36:49 -0700294 superproject_logging_data: A dictionary of superproject data that is to be logged.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800295
296 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700297 Returns path to the overriding manifest file instead of None.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800298 """
Raman Tennetib55769a2021-08-13 11:47:24 -0700299 print_messages = git_superproject.PrintMessages(opt, self.manifest)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800300 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800301 self.repodir,
Raman Tenneti784e16f2021-06-11 17:29:45 -0700302 self.git_event_log,
Raman Tennetib55769a2021-08-13 11:47:24 -0700303 quiet=opt.quiet,
304 print_messages=print_messages)
Raman Tennetiae86a462021-07-27 08:54:59 -0700305 if opt.local_only:
306 manifest_path = superproject.manifest_path
307 if manifest_path:
308 self._ReloadManifest(manifest_path, load_local_manifests)
309 return manifest_path
310
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800311 all_projects = self.GetProjects(args,
312 missing_ok=True,
313 submodules_ok=opt.fetch_submodules)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700314 update_result = superproject.UpdateProjectsRevisionId(all_projects)
315 manifest_path = update_result.manifest_path
Raman Tenneti7954de12021-07-28 14:36:49 -0700316 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700317 if manifest_path:
318 self._ReloadManifest(manifest_path, load_local_manifests)
319 else:
Raman Tennetib55769a2021-08-13 11:47:24 -0700320 if print_messages:
321 print('warning: Update of revisionId from superproject has failed, '
322 'repo sync will not use superproject to fetch the source. ',
323 'Please resync with the --no-use-superproject option to avoid this repo warning.',
324 file=sys.stderr)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700325 if update_result.fatal and opt.use_superproject is not None:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700326 sys.exit(1)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800327 return manifest_path
328
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500329 def _FetchProjectList(self, opt, projects):
330 """Main function of the fetch worker.
331
332 The projects we're given share the same underlying git object store, so we
333 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800334
David James8d201162013-10-11 17:03:19 -0700335 Delegates most of the work to _FetchHelper.
336
337 Args:
338 opt: Program options returned from optparse. See _Options().
339 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700340 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500341 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700342
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500343 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700344 """Fetch git objects for a single project.
345
David Pursehousec1b86a22012-11-14 11:36:51 +0900346 Args:
347 opt: Program options returned from optparse. See _Options().
348 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700349
350 Returns:
351 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 """
David Rileye0684ad2017-04-05 00:02:59 -0700353 start = time.time()
354 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500355 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900356 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500357 success = project.Sync_NetworkHalf(
358 quiet=opt.quiet,
359 verbose=opt.verbose,
360 output_redir=buf,
361 current_branch_only=self._GetCurrentBranchOnly(opt),
362 force_sync=opt.force_sync,
363 clone_bundle=opt.clone_bundle,
364 tags=opt.tags, archive=self.manifest.IsArchive,
365 optimized_fetch=opt.optimized_fetch,
366 retry_fetches=opt.retry_fetches,
367 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400368 ssh_proxy=self.ssh_proxy,
Raman Tennetif32f2432021-04-12 20:57:25 -0700369 clone_filter=self.manifest.CloneFilter,
370 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700371
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500372 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400373 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500374 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700375
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500376 if not success:
377 print('error: Cannot fetch %s from %s'
378 % (project.name, project.remote.url),
379 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700380 except GitError as e:
381 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500382 except Exception as e:
383 print('error: Cannot fetch %s (%s: %s)'
384 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
385 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500386
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500387 finish = time.time()
388 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700389
Mike Frysinger339f2df2021-05-06 00:44:42 -0400390 @classmethod
391 def _FetchInitChild(cls, ssh_proxy):
392 cls.ssh_proxy = ssh_proxy
393
394 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500395 ret = True
396
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400397 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700398 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400399 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800400
David James89ece422014-01-09 18:51:58 -0800401 objdir_project_map = dict()
402 for project in projects:
403 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500404 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700405
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500406 def _ProcessResults(results_sets):
407 ret = True
408 for results in results_sets:
409 for (success, project, start, finish) in results:
410 self._fetch_times.Set(project, finish - start)
411 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
412 start, finish, success)
413 # Check for any errors before running any more tasks.
414 # ...we'll let existing jobs finish, though.
415 if not success:
416 ret = False
417 else:
418 fetched.add(project.gitdir)
419 pm.update(msg=project.name)
420 if not ret and opt.fail_fast:
421 break
422 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700423
Mike Frysinger339f2df2021-05-06 00:44:42 -0400424 # We pass the ssh proxy settings via the class. This allows multiprocessing
425 # to pickle it up when spawning children. We can't pass it as an argument
426 # to _FetchProjectList below as multiprocessing is unable to pickle those.
427 Sync.ssh_proxy = None
428
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500429 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400430 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400431 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500432 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
433 ret = False
434 else:
435 # Favor throughput over responsiveness when quiet. It seems that imap()
436 # will yield results in batches relative to chunksize, so even as the
437 # children finish a sync, we won't see the result until one child finishes
438 # ~chunksize jobs. When using a large --jobs with large chunksize, this
439 # can be jarring as there will be a large initial delay where repo looks
440 # like it isn't doing anything and sits at 0%, but then suddenly completes
441 # a lot of jobs all at once. Since this code is more network bound, we
442 # can accept a bit more CPU overhead with a smaller chunksize so that the
443 # user sees more immediate & continuous feedback.
444 if opt.quiet:
445 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800446 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500447 pm.update(inc=0, msg='warming up')
448 chunksize = 4
Mike Frysinger339f2df2021-05-06 00:44:42 -0400449 with multiprocessing.Pool(
450 jobs, initializer=self._FetchInitChild, initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500451 results = pool.imap_unordered(
452 functools.partial(self._FetchProjectList, opt),
453 projects_list,
454 chunksize=chunksize)
455 if not _ProcessResults(results):
456 ret = False
457 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800458
Mike Frysinger339f2df2021-05-06 00:44:42 -0400459 # Cleanup the reference now that we're done with it, and we're going to
460 # release any resources it points to. If we don't, later multiprocessing
461 # usage (e.g. checkouts) will try to pickle and then crash.
462 del Sync.ssh_proxy
463
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700464 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700465 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700466
Julien Campergue335f5ef2013-10-16 11:02:35 +0200467 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400468 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200469
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500470 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700471
Mike Frysingerb4429432021-05-05 20:03:26 -0400472 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400473 load_local_manifests, ssh_proxy):
Mike Frysingerb4429432021-05-05 20:03:26 -0400474 """The main network fetch loop.
475
476 Args:
477 opt: Program options returned from optparse. See _Options().
478 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200479 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400480 err_event: Whether an error was hit while processing.
481 manifest_name: Manifest file to be reloaded.
482 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400483 ssh_proxy: SSH manager for clients & masters.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200484
485 Returns:
486 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400487 """
488 rp = self.manifest.repoProject
489
490 to_fetch = []
491 now = time.time()
492 if _ONE_DAY_S <= (now - rp.LastFetch):
493 to_fetch.append(rp)
494 to_fetch.extend(all_projects)
495 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
496
Mike Frysinger339f2df2021-05-06 00:44:42 -0400497 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400498 if not success:
499 err_event.set()
500
501 _PostRepoFetch(rp, opt.repo_verify)
502 if opt.network_only:
503 # bail out now; the rest touches the working tree
504 if err_event.is_set():
505 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
506 sys.exit(1)
507 return
508
509 # Iteratively fetch missing and/or nested unregistered submodules
510 previously_missing_set = set()
511 while True:
512 self._ReloadManifest(manifest_name, load_local_manifests)
513 all_projects = self.GetProjects(args,
514 missing_ok=True,
515 submodules_ok=opt.fetch_submodules)
516 missing = []
517 for project in all_projects:
518 if project.gitdir not in fetched:
519 missing.append(project)
520 if not missing:
521 break
522 # Stop us from non-stopped fetching actually-missing repos: If set of
523 # missing repos has not been changed from last fetch, we break.
524 missing_set = set(p.name for p in missing)
525 if previously_missing_set == missing_set:
526 break
527 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400528 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400529 if not success:
530 err_event.set()
531 fetched.update(new_fetched)
532
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200533 return all_projects
534
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500535 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700536 """Checkout work tree for one project
537
538 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500539 detach_head: Whether to leave a detached HEAD.
540 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700541 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700542
543 Returns:
544 Whether the fetch was successful.
545 """
Xin Li745be2e2019-06-03 11:24:30 -0700546 start = time.time()
547 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500548 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700549 success = False
550 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500551 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500552 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700553 except GitError as e:
554 print('error.GitError: Cannot checkout %s: %s' %
555 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500556 except Exception as e:
557 print('error: Cannot checkout %s: %s: %s' %
558 (project.name, type(e).__name__, str(e)),
559 file=sys.stderr)
560 raise
Xin Li745be2e2019-06-03 11:24:30 -0700561
Mike Frysingerebf04a42021-02-23 20:48:04 -0500562 if not success:
563 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
564 finish = time.time()
565 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700566
Mike Frysingerebf04a42021-02-23 20:48:04 -0500567 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700568 """Checkout projects listed in all_projects
569
570 Args:
571 all_projects: List of all projects that should be checked out.
572 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500573 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700574 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500575 # Only checkout projects with worktrees.
576 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700577
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500578 def _ProcessResults(pool, pm, results):
579 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500580 for (success, project, start, finish) in results:
581 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
582 start, finish, success)
583 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500584 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500585 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500586 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500587 err_results.append(project.relpath)
588 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500589 if pool:
590 pool.close()
591 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500592 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500593 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700594
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500595 return self.ExecuteInParallel(
596 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
597 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
598 all_projects,
599 callback=_ProcessResults,
600 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500601
Mike Frysinger5a033082019-09-23 19:21:20 -0400602 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400603 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400604 pm.update(inc=0, msg='prescan')
605
Allen Webb4ee4a452021-10-07 10:42:38 -0500606 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700607 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500608 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500609 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900610 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100611 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400612 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100613 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500614 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500615 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500616 else:
617 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400618 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500619 'versions of git; please upgrade to git-2.7.0+.'
620 % (project.relpath,),
621 file=sys.stderr)
622 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500623 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500624 # Only call git gc once per objdir, but call pack-refs for the remainder.
625 if project.objdir not in tidy_dirs:
626 tidy_dirs[project.objdir] = (
627 True, # Run a full gc.
628 project.bare_git,
629 )
630 elif project.gitdir not in tidy_dirs:
631 tidy_dirs[project.gitdir] = (
632 False, # Do not run a full gc; just run pack-refs.
633 project.bare_git,
634 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400635
636 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700637 jobs = min(self.jobs, cpu_count)
638
639 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500640 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400641 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500642 if run_gc:
643 bare_git.gc('--auto')
644 else:
645 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400646 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700647 return
648
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400649 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700650
651 threads = set()
652 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700653
Allen Webb4ee4a452021-10-07 10:42:38 -0500654 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400655 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700656 try:
657 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500658 if run_gc:
659 bare_git.gc('--auto', config=config)
660 else:
661 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700662 except GitError:
663 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900664 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700665 err_event.set()
666 raise
667 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400668 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700669 sem.release()
670
Allen Webb4ee4a452021-10-07 10:42:38 -0500671 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500672 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700673 break
674 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500675 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700676 t.daemon = True
677 threads.add(t)
678 t.start()
679
680 for t in threads:
681 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400682 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700683
Raman Tennetifeb28912021-05-02 19:47:29 -0700684 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
685 """Reload the manfiest from the file specified by the |manifest_name|.
686
687 It unloads the manifest if |manifest_name| is None.
688
689 Args:
690 manifest_name: Manifest file to be reloaded.
691 load_local_manifests: Whether to load local manifests.
692 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800693 if manifest_name:
694 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700695 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800696 else:
697 self.manifest._Unload()
698
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500699 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700700 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700701 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700702 if project.relpath:
703 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700704 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500705 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700706 old_project_paths = []
707
708 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500709 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700710 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800711 # In reversed order, so subfolders are deleted before parent folder.
712 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700713 if not path:
714 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700715 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900716 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700717 gitdir = os.path.join(self.manifest.topdir, path, '.git')
718 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900719 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900720 manifest=self.manifest,
721 name=path,
722 remote=RemoteSpec('origin'),
723 gitdir=gitdir,
724 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500725 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900726 worktree=os.path.join(self.manifest.topdir, path),
727 relpath=path,
728 revisionExpr='HEAD',
729 revisionId=None,
730 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500731 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900732 quiet=opt.quiet,
733 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400734 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700735
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700736 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500737 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700738 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700739 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700740 return 0
741
jiajia tanga590e642021-04-25 20:02:02 +0800742 def UpdateCopyLinkfileList(self):
743 """Save all dests of copyfile and linkfile, and update them if needed.
744
745 Returns:
746 Whether update was successful.
747 """
748 new_paths = {}
749 new_linkfile_paths = []
750 new_copyfile_paths = []
751 for project in self.GetProjects(None, missing_ok=True):
752 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
753 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
754
755 new_paths = {
756 'linkfile': new_linkfile_paths,
757 'copyfile': new_copyfile_paths,
758 }
759
760 copylinkfile_name = 'copy-link-files.json'
761 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
762 old_copylinkfile_paths = {}
763
764 if os.path.exists(copylinkfile_path):
765 with open(copylinkfile_path, 'rb') as fp:
766 try:
767 old_copylinkfile_paths = json.load(fp)
768 except:
769 print('error: %s is not a json formatted file.' %
770 copylinkfile_path, file=sys.stderr)
771 platform_utils.remove(copylinkfile_path)
772 return False
773
774 need_remove_files = []
775 need_remove_files.extend(
776 set(old_copylinkfile_paths.get('linkfile', [])) -
777 set(new_linkfile_paths))
778 need_remove_files.extend(
779 set(old_copylinkfile_paths.get('copyfile', [])) -
780 set(new_copyfile_paths))
781
782 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400783 # Try to remove the updated copyfile or linkfile.
784 # So, if the file is not exist, nothing need to do.
785 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800786
787 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
788 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
789 json.dump(new_paths, fp)
790 return True
791
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400792 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
793 if not self.manifest.manifest_server:
794 print('error: cannot smart sync: no manifest server defined in '
795 'manifest', file=sys.stderr)
796 sys.exit(1)
797
798 manifest_server = self.manifest.manifest_server
799 if not opt.quiet:
800 print('Using manifest server %s' % manifest_server)
801
David Pursehouseeeff3532020-02-12 11:24:10 +0900802 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400803 username = None
804 password = None
805 if opt.manifest_server_username and opt.manifest_server_password:
806 username = opt.manifest_server_username
807 password = opt.manifest_server_password
808 else:
809 try:
810 info = netrc.netrc()
811 except IOError:
812 # .netrc file does not exist or could not be opened
813 pass
814 else:
815 try:
816 parse_result = urllib.parse.urlparse(manifest_server)
817 if parse_result.hostname:
818 auth = info.authenticators(parse_result.hostname)
819 if auth:
820 username, _account, password = auth
821 else:
822 print('No credentials found for %s in .netrc'
823 % parse_result.hostname, file=sys.stderr)
824 except netrc.NetrcParseError as e:
825 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
826
827 if (username and password):
828 manifest_server = manifest_server.replace('://', '://%s:%s@' %
829 (username, password),
830 1)
831
832 transport = PersistentTransport(manifest_server)
833 if manifest_server.startswith('persistent-'):
834 manifest_server = manifest_server[len('persistent-'):]
835
836 try:
837 server = xmlrpc.client.Server(manifest_server, transport=transport)
838 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800839 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400840
Mike Frysinger56ce3462019-12-04 19:30:48 -0500841 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500842 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400843 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500844 elif ('TARGET_PRODUCT' in os.environ and
845 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500846 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
847 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400848 [success, manifest_str] = server.GetApprovedManifest(branch, target)
849 else:
850 [success, manifest_str] = server.GetApprovedManifest(branch)
851 else:
852 assert(opt.smart_tag)
853 [success, manifest_str] = server.GetManifest(opt.smart_tag)
854
855 if success:
856 manifest_name = os.path.basename(smart_sync_manifest_path)
857 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500858 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400859 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400860 except IOError as e:
861 print('error: cannot write manifest to %s:\n%s'
862 % (smart_sync_manifest_path, e),
863 file=sys.stderr)
864 sys.exit(1)
865 self._ReloadManifest(manifest_name)
866 else:
867 print('error: manifest server RPC call failed: %s' %
868 manifest_str, file=sys.stderr)
869 sys.exit(1)
870 except (socket.error, IOError, xmlrpc.client.Fault) as e:
871 print('error: cannot connect to manifest server %s:\n%s'
872 % (self.manifest.manifest_server, e), file=sys.stderr)
873 sys.exit(1)
874 except xmlrpc.client.ProtocolError as e:
875 print('error: cannot connect to manifest server %s:\n%d %s'
876 % (self.manifest.manifest_server, e.errcode, e.errmsg),
877 file=sys.stderr)
878 sys.exit(1)
879
880 return manifest_name
881
Mike Frysingerfb527e32019-08-27 02:34:32 -0400882 def _UpdateManifestProject(self, opt, mp, manifest_name):
883 """Fetch & update the local manifest project."""
884 if not opt.local_only:
885 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500886 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700887 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200888 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500889 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400890 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600891 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400892 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700893 clone_filter=self.manifest.CloneFilter,
894 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400895 finish = time.time()
896 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
897 start, finish, success)
898
899 if mp.HasChanges:
900 syncbuf = SyncBuffer(mp.config)
901 start = time.time()
902 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
903 clean = syncbuf.Finish()
904 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
905 start, time.time(), clean)
906 if not clean:
907 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400908 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400909 if opt.jobs is None:
910 self.jobs = self.manifest.default.sync_j
911
Mike Frysingerae6cb082019-08-27 01:10:59 -0400912 def ValidateOptions(self, opt, args):
913 if opt.force_broken:
914 print('warning: -f/--force-broken is now the default behavior, and the '
915 'options are deprecated', file=sys.stderr)
916 if opt.network_only and opt.detach_head:
917 self.OptionParser.error('cannot combine -n and -d')
918 if opt.network_only and opt.local_only:
919 self.OptionParser.error('cannot combine -n and -l')
920 if opt.manifest_name and opt.smart_sync:
921 self.OptionParser.error('cannot combine -m and -s')
922 if opt.manifest_name and opt.smart_tag:
923 self.OptionParser.error('cannot combine -m and -t')
924 if opt.manifest_server_username or opt.manifest_server_password:
925 if not (opt.smart_sync or opt.smart_tag):
926 self.OptionParser.error('-u and -p may only be combined with -s or -t')
927 if None in [opt.manifest_server_username, opt.manifest_server_password]:
928 self.OptionParser.error('both -u and -p must be given')
929
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700930 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800931 if opt.jobs:
932 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700933 if self.jobs > 1:
934 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400935 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700936
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500937 if opt.manifest_name:
938 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700939
Chirayu Desaia892b102013-06-11 14:18:46 +0530940 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900941 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900942 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530943
Xin Lid79a4bc2020-05-20 16:03:45 -0700944 if opt.clone_bundle is None:
945 opt.clone_bundle = self.manifest.CloneBundle
946
Victor Boivie08c880d2011-04-19 10:32:52 +0200947 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400948 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
949 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900950 if os.path.isfile(smart_sync_manifest_path):
951 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800952 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900953 except OSError as e:
954 print('error: failed to remove existing smart sync override manifest: %s' %
955 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700956
Mike Frysingerc99322a2021-05-04 15:32:43 -0400957 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400958
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700959 rp = self.manifest.repoProject
960 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500961 cb = rp.CurrentBranch
962 if cb:
963 base = rp.GetBranch(cb).merge
964 if not base or not base.startswith('refs/heads/'):
965 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400966 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500967 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968
969 mp = self.manifest.manifestProject
Jack Neus03ff2762021-10-15 15:43:19 +0000970 is_standalone_manifest = mp.config.GetString('manifest.standalone')
971 if not is_standalone_manifest:
972 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700973
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800974 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700975 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800976
Fredrik de Grootcc960972019-11-22 09:04:31 +0100977 if not opt.mp_update:
978 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +0000979 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +0100980 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700981
Raman Tennetifeb28912021-05-02 19:47:29 -0700982 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti7954de12021-07-28 14:36:49 -0700983 use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
984 superproject_logging_data = {
985 'superproject': use_superproject,
986 'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Raman Tennetib55769a2021-08-13 11:47:24 -0700987 'hassuperprojecttag': bool(self.manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -0700988 }
989 if use_superproject:
990 manifest_name = self._UpdateProjectsRevisionId(
991 opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800992
Simran Basib9a1b732015-08-20 12:19:28 -0700993 if self.gitc_manifest:
994 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700995 missing_ok=True)
996 gitc_projects = []
997 opened_projects = []
998 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700999 if project.relpath in self.gitc_manifest.paths and \
1000 self.gitc_manifest.paths[project.relpath].old_revision:
1001 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001002 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001003 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001004
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001005 if not args:
1006 gitc_projects = None
1007
1008 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001009 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001010 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1011 if manifest_name:
1012 manifest.Override(manifest_name)
1013 else:
1014 manifest.Override(self.manifest.manifestFile)
1015 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1016 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001017 gitc_projects)
1018 print('GITC client successfully synced.')
1019
1020 # The opened projects need to be synced as normal, therefore we
1021 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001022 # TODO: make this more reliable -- if there's a project name/path overlap,
1023 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +09001024 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
1025 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001026 if not args:
1027 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001028 all_projects = self.GetProjects(args,
1029 missing_ok=True,
1030 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001031
Mike Frysinger5a033082019-09-23 19:21:20 -04001032 err_network_sync = False
1033 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001034
Dave Borowitz67700e92012-10-23 15:00:54 -07001035 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001036 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001037 with multiprocessing.Manager() as manager:
1038 with ssh.ProxyManager(manager) as ssh_proxy:
1039 # Initialize the socket dir once in the parent.
1040 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001041 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1042 manifest_name, load_local_manifests,
1043 ssh_proxy)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001044
1045 if opt.network_only:
1046 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001047
1048 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001049 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001050 err_network_sync = True
1051 if opt.fail_fast:
1052 print('\nerror: Exited sync due to fetch errors.\n'
1053 'Local checkouts *not* updated. Resolve network issues & '
1054 'retry.\n'
1055 '`repo sync -l` will update some local checkouts.',
1056 file=sys.stderr)
1057 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001058
Julien Campergue335f5ef2013-10-16 11:02:35 +02001059 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001060 # bail out now, we have no working tree
1061 return
1062
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001063 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001064 err_event.set()
1065 err_update_projects = True
1066 if opt.fail_fast:
1067 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1068 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001069
Mike Frysinger14208f42021-05-04 15:31:51 -04001070 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1071 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001072 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001073 if opt.fail_fast:
1074 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1075 sys.exit(1)
1076
Mike Frysinger5a033082019-09-23 19:21:20 -04001077 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001078 # NB: We don't exit here because this is the last step.
1079 err_checkout = not self._Checkout(all_projects, opt, err_results)
1080 if err_checkout:
1081 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001083 # If there's a notice that's supposed to print at the end of the sync, print
1084 # it now...
1085 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001086 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001087
Mike Frysinger5a033082019-09-23 19:21:20 -04001088 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001089 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001090 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1091 if err_network_sync:
1092 print('error: Downloading network changes failed.', file=sys.stderr)
1093 if err_update_projects:
1094 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001095 if err_update_linkfiles:
1096 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001097 if err_checkout:
1098 print('error: Checking out local projects failed.', file=sys.stderr)
1099 if err_results:
1100 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1101 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1102 file=sys.stderr)
1103 sys.exit(1)
1104
Raman Tenneti7954de12021-07-28 14:36:49 -07001105 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001106 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1107 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001108
1109 # Update and log with the new sync analysis state.
1110 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001111 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1112 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001113
Mike Frysingere19d9e12020-02-12 11:23:32 -05001114 if not opt.quiet:
1115 print('repo sync has finished successfully.')
1116
David Pursehouse819827a2020-02-12 15:20:19 +09001117
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001118def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001119 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001120 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001121 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001122 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001123 if project.Exists:
1124 project.PostRepoUpgrade()
1125
David Pursehouse819827a2020-02-12 15:20:19 +09001126
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001127def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001128 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001129 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001130 wrapper = Wrapper()
1131 try:
1132 rev = rp.bare_git.describe(rp.GetRevisionId())
1133 except GitError:
1134 rev = None
1135 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1136 # See if we're held back due to missing signed tag.
1137 current_revid = rp.bare_git.rev_parse('HEAD')
1138 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1139 if current_revid != new_revid:
1140 # We want to switch to the new rev, but also not trash any uncommitted
1141 # changes. This helps with local testing/hacking.
1142 # If a local change has been made, we will throw that away.
1143 # We also have to make sure this will switch to an older commit if that's
1144 # the latest tag in order to support release rollback.
1145 try:
1146 rp.work_git.reset('--keep', new_rev)
1147 except GitError as e:
1148 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001149 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001150 raise RepoChangedException(['--repo-upgraded'])
1151 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001152 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001153 else:
1154 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001155 print('repo version %s is current' % rp.work_git.describe(HEAD),
1156 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001157
David Pursehouse819827a2020-02-12 15:20:19 +09001158
Dave Borowitz67700e92012-10-23 15:00:54 -07001159class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001160 _ALPHA = 0.5
1161
Dave Borowitz67700e92012-10-23 15:00:54 -07001162 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001163 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001164 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001165 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001166
1167 def Get(self, project):
1168 self._Load()
1169 return self._times.get(project.name, _ONE_DAY_S)
1170
1171 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001172 self._Load()
1173 name = project.name
1174 old = self._times.get(name, t)
1175 self._seen.add(name)
1176 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001177 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001178
1179 def _Load(self):
1180 if self._times is None:
1181 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001182 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001183 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001184 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001185 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001186 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001187
1188 def Save(self):
1189 if self._times is None:
1190 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001191
1192 to_delete = []
1193 for name in self._times:
1194 if name not in self._seen:
1195 to_delete.append(name)
1196 for name in to_delete:
1197 del self._times[name]
1198
Dave Borowitz67700e92012-10-23 15:00:54 -07001199 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001200 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001201 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001202 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001203 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001204
1205# This is a replacement for xmlrpc.client.Transport using urllib2
1206# and supporting persistent-http[s]. It cannot change hosts from
1207# request to request like the normal transport, the real url
1208# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001209
1210
Dan Willemsen0745bb22015-08-17 13:41:45 -07001211class PersistentTransport(xmlrpc.client.Transport):
1212 def __init__(self, orig_host):
1213 self.orig_host = orig_host
1214
1215 def request(self, host, handler, request_body, verbose=False):
1216 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1217 # Python doesn't understand cookies with the #HttpOnly_ prefix
1218 # Since we're only using them for HTTP, copy the file temporarily,
1219 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001220 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001221 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001222 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001223 try:
1224 with open(cookiefile) as f:
1225 for line in f:
1226 if line.startswith("#HttpOnly_"):
1227 line = line[len("#HttpOnly_"):]
1228 tmpcookiefile.write(line)
1229 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001230
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001231 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001232 try:
1233 cookiejar.load()
1234 except cookielib.LoadError:
1235 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001236 finally:
1237 tmpcookiefile.close()
1238 else:
1239 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001240
1241 proxyhandler = urllib.request.ProxyHandler
1242 if proxy:
1243 proxyhandler = urllib.request.ProxyHandler({
1244 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001245 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001246
1247 opener = urllib.request.build_opener(
1248 urllib.request.HTTPCookieProcessor(cookiejar),
1249 proxyhandler)
1250
1251 url = urllib.parse.urljoin(self.orig_host, handler)
1252 parse_results = urllib.parse.urlparse(url)
1253
1254 scheme = parse_results.scheme
1255 if scheme == 'persistent-http':
1256 scheme = 'http'
1257 if scheme == 'persistent-https':
1258 # If we're proxying through persistent-https, use http. The
1259 # proxy itself will do the https.
1260 if proxy:
1261 scheme = 'http'
1262 else:
1263 scheme = 'https'
1264
1265 # Parse out any authentication information using the base class
1266 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1267
1268 url = urllib.parse.urlunparse((
1269 scheme,
1270 host,
1271 parse_results.path,
1272 parse_results.params,
1273 parse_results.query,
1274 parse_results.fragment))
1275
1276 request = urllib.request.Request(url, request_body)
1277 if extra_headers is not None:
1278 for (name, header) in extra_headers:
1279 request.add_header(name, header)
1280 request.add_header('Content-Type', 'text/xml')
1281 try:
1282 response = opener.open(request)
1283 except urllib.error.HTTPError as e:
1284 if e.code == 501:
1285 # We may have been redirected through a login process
1286 # but our POST turned into a GET. Retry.
1287 response = opener.open(request)
1288 else:
1289 raise
1290
1291 p, u = xmlrpc.client.getparser()
1292 while 1:
1293 data = response.read(1024)
1294 if not data:
1295 break
1296 p.feed(data)
1297 p.close()
1298 return u.close()
1299
1300 def close(self):
1301 pass