blob: c2303232c46fde4a2f74bdb8a6e9e29bc0fafd51 [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 Frysingerd68ed632021-05-03 01:21:35 -0400241 p.add_option('--tags',
242 action='store_false',
243 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400245 dest='tags', action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900247 p.add_option('--optimized-fetch',
248 dest='optimized_fetch', action='store_true',
249 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600250 p.add_option('--retry-fetches',
251 default=0, action='store', type='int',
252 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900253 p.add_option('--prune', dest='prune', action='store_true',
254 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700255 if show_smart:
256 p.add_option('-s', '--smart-sync',
257 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900258 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200259 p.add_option('-t', '--smart-tag',
260 dest='smart_tag', action='store',
261 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700262
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g = p.add_option_group('repo Version options')
264 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500265 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800268 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700269 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800271 def _GetBranch(self):
272 """Returns the branch name for getting the approved manifest."""
273 p = self.manifest.manifestProject
274 b = p.GetBranch(p.CurrentBranch)
275 branch = b.merge
276 if branch.startswith(R_HEADS):
277 branch = branch[len(R_HEADS):]
278 return branch
279
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700280 def _GetCurrentBranchOnly(self, opt):
281 """Returns True if current-branch or use-superproject options are enabled."""
Xin Li0cb6e922021-06-16 10:19:00 -0700282 return opt.current_branch_only or git_superproject.UseSuperproject(opt, self.manifest)
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700283
Raman Tenneti7954de12021-07-28 14:36:49 -0700284 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800285 """Update revisionId of every project with the SHA from superproject.
286
287 This function updates each project's revisionId with SHA from superproject.
288 It writes the updated manifest into a file and reloads the manifest from it.
289
290 Args:
291 opt: Program options returned from optparse. See _Options().
292 args: Arguments to pass to GetProjects. See the GetProjects
293 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700294 load_local_manifests: Whether to load local manifests.
Raman Tenneti7954de12021-07-28 14:36:49 -0700295 superproject_logging_data: A dictionary of superproject data that is to be logged.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800296
297 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700298 Returns path to the overriding manifest file instead of None.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800299 """
Raman Tennetib55769a2021-08-13 11:47:24 -0700300 print_messages = git_superproject.PrintMessages(opt, self.manifest)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800301 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800302 self.repodir,
Raman Tenneti784e16f2021-06-11 17:29:45 -0700303 self.git_event_log,
Raman Tennetib55769a2021-08-13 11:47:24 -0700304 quiet=opt.quiet,
305 print_messages=print_messages)
Raman Tennetiae86a462021-07-27 08:54:59 -0700306 if opt.local_only:
307 manifest_path = superproject.manifest_path
308 if manifest_path:
309 self._ReloadManifest(manifest_path, load_local_manifests)
310 return manifest_path
311
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800312 all_projects = self.GetProjects(args,
313 missing_ok=True,
314 submodules_ok=opt.fetch_submodules)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700315 update_result = superproject.UpdateProjectsRevisionId(all_projects)
316 manifest_path = update_result.manifest_path
Raman Tenneti7954de12021-07-28 14:36:49 -0700317 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700318 if manifest_path:
319 self._ReloadManifest(manifest_path, load_local_manifests)
320 else:
Raman Tennetib55769a2021-08-13 11:47:24 -0700321 if print_messages:
322 print('warning: Update of revisionId from superproject has failed, '
323 'repo sync will not use superproject to fetch the source. ',
324 'Please resync with the --no-use-superproject option to avoid this repo warning.',
325 file=sys.stderr)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700326 if update_result.fatal and opt.use_superproject is not None:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700327 sys.exit(1)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800328 return manifest_path
329
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500330 def _FetchProjectList(self, opt, projects):
331 """Main function of the fetch worker.
332
333 The projects we're given share the same underlying git object store, so we
334 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800335
David James8d201162013-10-11 17:03:19 -0700336 Delegates most of the work to _FetchHelper.
337
338 Args:
339 opt: Program options returned from optparse. See _Options().
340 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700341 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500342 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700343
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500344 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700345 """Fetch git objects for a single project.
346
David Pursehousec1b86a22012-11-14 11:36:51 +0900347 Args:
348 opt: Program options returned from optparse. See _Options().
349 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700350
351 Returns:
352 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 """
David Rileye0684ad2017-04-05 00:02:59 -0700354 start = time.time()
355 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500356 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900357 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500358 success = project.Sync_NetworkHalf(
359 quiet=opt.quiet,
360 verbose=opt.verbose,
361 output_redir=buf,
362 current_branch_only=self._GetCurrentBranchOnly(opt),
363 force_sync=opt.force_sync,
364 clone_bundle=opt.clone_bundle,
365 tags=opt.tags, archive=self.manifest.IsArchive,
366 optimized_fetch=opt.optimized_fetch,
367 retry_fetches=opt.retry_fetches,
368 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400369 ssh_proxy=self.ssh_proxy,
Raman Tennetif32f2432021-04-12 20:57:25 -0700370 clone_filter=self.manifest.CloneFilter,
371 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700372
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500373 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400374 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500375 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700376
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500377 if not success:
378 print('error: Cannot fetch %s from %s'
379 % (project.name, project.remote.url),
380 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700381 except GitError as e:
382 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500383 except Exception as e:
384 print('error: Cannot fetch %s (%s: %s)'
385 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
386 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500387
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500388 finish = time.time()
389 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700390
Mike Frysinger339f2df2021-05-06 00:44:42 -0400391 @classmethod
392 def _FetchInitChild(cls, ssh_proxy):
393 cls.ssh_proxy = ssh_proxy
394
395 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500396 ret = True
397
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400398 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700399 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400400 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800401
David James89ece422014-01-09 18:51:58 -0800402 objdir_project_map = dict()
403 for project in projects:
404 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500405 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700406
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500407 def _ProcessResults(results_sets):
408 ret = True
409 for results in results_sets:
410 for (success, project, start, finish) in results:
411 self._fetch_times.Set(project, finish - start)
412 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
413 start, finish, success)
414 # Check for any errors before running any more tasks.
415 # ...we'll let existing jobs finish, though.
416 if not success:
417 ret = False
418 else:
419 fetched.add(project.gitdir)
420 pm.update(msg=project.name)
421 if not ret and opt.fail_fast:
422 break
423 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700424
Mike Frysinger339f2df2021-05-06 00:44:42 -0400425 # We pass the ssh proxy settings via the class. This allows multiprocessing
426 # to pickle it up when spawning children. We can't pass it as an argument
427 # to _FetchProjectList below as multiprocessing is unable to pickle those.
428 Sync.ssh_proxy = None
429
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500430 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400431 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400432 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500433 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
434 ret = False
435 else:
436 # Favor throughput over responsiveness when quiet. It seems that imap()
437 # will yield results in batches relative to chunksize, so even as the
438 # children finish a sync, we won't see the result until one child finishes
439 # ~chunksize jobs. When using a large --jobs with large chunksize, this
440 # can be jarring as there will be a large initial delay where repo looks
441 # like it isn't doing anything and sits at 0%, but then suddenly completes
442 # a lot of jobs all at once. Since this code is more network bound, we
443 # can accept a bit more CPU overhead with a smaller chunksize so that the
444 # user sees more immediate & continuous feedback.
445 if opt.quiet:
446 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800447 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500448 pm.update(inc=0, msg='warming up')
449 chunksize = 4
Mike Frysinger339f2df2021-05-06 00:44:42 -0400450 with multiprocessing.Pool(
451 jobs, initializer=self._FetchInitChild, initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500452 results = pool.imap_unordered(
453 functools.partial(self._FetchProjectList, opt),
454 projects_list,
455 chunksize=chunksize)
456 if not _ProcessResults(results):
457 ret = False
458 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800459
Mike Frysinger339f2df2021-05-06 00:44:42 -0400460 # Cleanup the reference now that we're done with it, and we're going to
461 # release any resources it points to. If we don't, later multiprocessing
462 # usage (e.g. checkouts) will try to pickle and then crash.
463 del Sync.ssh_proxy
464
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700465 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700466 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700467
Julien Campergue335f5ef2013-10-16 11:02:35 +0200468 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400469 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200470
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500471 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700472
Mike Frysingerb4429432021-05-05 20:03:26 -0400473 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400474 load_local_manifests, ssh_proxy):
Mike Frysingerb4429432021-05-05 20:03:26 -0400475 """The main network fetch loop.
476
477 Args:
478 opt: Program options returned from optparse. See _Options().
479 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200480 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400481 err_event: Whether an error was hit while processing.
482 manifest_name: Manifest file to be reloaded.
483 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400484 ssh_proxy: SSH manager for clients & masters.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200485
486 Returns:
487 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400488 """
489 rp = self.manifest.repoProject
490
491 to_fetch = []
492 now = time.time()
493 if _ONE_DAY_S <= (now - rp.LastFetch):
494 to_fetch.append(rp)
495 to_fetch.extend(all_projects)
496 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
497
Mike Frysinger339f2df2021-05-06 00:44:42 -0400498 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400499 if not success:
500 err_event.set()
501
502 _PostRepoFetch(rp, opt.repo_verify)
503 if opt.network_only:
504 # bail out now; the rest touches the working tree
505 if err_event.is_set():
506 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
507 sys.exit(1)
508 return
509
510 # Iteratively fetch missing and/or nested unregistered submodules
511 previously_missing_set = set()
512 while True:
513 self._ReloadManifest(manifest_name, load_local_manifests)
514 all_projects = self.GetProjects(args,
515 missing_ok=True,
516 submodules_ok=opt.fetch_submodules)
517 missing = []
518 for project in all_projects:
519 if project.gitdir not in fetched:
520 missing.append(project)
521 if not missing:
522 break
523 # Stop us from non-stopped fetching actually-missing repos: If set of
524 # missing repos has not been changed from last fetch, we break.
525 missing_set = set(p.name for p in missing)
526 if previously_missing_set == missing_set:
527 break
528 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400529 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400530 if not success:
531 err_event.set()
532 fetched.update(new_fetched)
533
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200534 return all_projects
535
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500536 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700537 """Checkout work tree for one project
538
539 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500540 detach_head: Whether to leave a detached HEAD.
541 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700542 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700543
544 Returns:
545 Whether the fetch was successful.
546 """
Xin Li745be2e2019-06-03 11:24:30 -0700547 start = time.time()
548 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500549 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700550 success = False
551 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500552 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500553 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700554 except GitError as e:
555 print('error.GitError: Cannot checkout %s: %s' %
556 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500557 except Exception as e:
558 print('error: Cannot checkout %s: %s: %s' %
559 (project.name, type(e).__name__, str(e)),
560 file=sys.stderr)
561 raise
Xin Li745be2e2019-06-03 11:24:30 -0700562
Mike Frysingerebf04a42021-02-23 20:48:04 -0500563 if not success:
564 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
565 finish = time.time()
566 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700567
Mike Frysingerebf04a42021-02-23 20:48:04 -0500568 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700569 """Checkout projects listed in all_projects
570
571 Args:
572 all_projects: List of all projects that should be checked out.
573 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500574 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700575 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500576 # Only checkout projects with worktrees.
577 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700578
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500579 def _ProcessResults(pool, pm, results):
580 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500581 for (success, project, start, finish) in results:
582 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
583 start, finish, success)
584 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500585 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500586 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500587 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500588 err_results.append(project.relpath)
589 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500590 if pool:
591 pool.close()
592 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500593 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500594 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700595
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500596 return self.ExecuteInParallel(
597 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
598 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
599 all_projects,
600 callback=_ProcessResults,
601 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500602
Mike Frysinger5a033082019-09-23 19:21:20 -0400603 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400604 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400605 pm.update(inc=0, msg='prescan')
606
Allen Webb4ee4a452021-10-07 10:42:38 -0500607 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700608 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500609 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500610 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900611 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100612 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400613 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100614 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500615 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500616 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500617 else:
618 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400619 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500620 'versions of git; please upgrade to git-2.7.0+.'
621 % (project.relpath,),
622 file=sys.stderr)
623 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500624 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500625 # Only call git gc once per objdir, but call pack-refs for the remainder.
626 if project.objdir not in tidy_dirs:
627 tidy_dirs[project.objdir] = (
628 True, # Run a full gc.
629 project.bare_git,
630 )
631 elif project.gitdir not in tidy_dirs:
632 tidy_dirs[project.gitdir] = (
633 False, # Do not run a full gc; just run pack-refs.
634 project.bare_git,
635 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400636
637 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700638 jobs = min(self.jobs, cpu_count)
639
640 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500641 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400642 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500643 if run_gc:
644 bare_git.gc('--auto')
645 else:
646 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400647 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700648 return
649
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400650 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700651
652 threads = set()
653 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700654
Allen Webb4ee4a452021-10-07 10:42:38 -0500655 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400656 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700657 try:
658 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500659 if run_gc:
660 bare_git.gc('--auto', config=config)
661 else:
662 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700663 except GitError:
664 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900665 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700666 err_event.set()
667 raise
668 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400669 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700670 sem.release()
671
Allen Webb4ee4a452021-10-07 10:42:38 -0500672 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500673 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700674 break
675 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500676 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700677 t.daemon = True
678 threads.add(t)
679 t.start()
680
681 for t in threads:
682 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400683 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700684
Raman Tennetifeb28912021-05-02 19:47:29 -0700685 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
686 """Reload the manfiest from the file specified by the |manifest_name|.
687
688 It unloads the manifest if |manifest_name| is None.
689
690 Args:
691 manifest_name: Manifest file to be reloaded.
692 load_local_manifests: Whether to load local manifests.
693 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800694 if manifest_name:
695 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700696 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800697 else:
698 self.manifest._Unload()
699
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500700 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700701 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700702 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700703 if project.relpath:
704 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700705 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500706 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700707 old_project_paths = []
708
709 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500710 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700711 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800712 # In reversed order, so subfolders are deleted before parent folder.
713 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700714 if not path:
715 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700716 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900717 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700718 gitdir = os.path.join(self.manifest.topdir, path, '.git')
719 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900720 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900721 manifest=self.manifest,
722 name=path,
723 remote=RemoteSpec('origin'),
724 gitdir=gitdir,
725 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500726 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900727 worktree=os.path.join(self.manifest.topdir, path),
728 relpath=path,
729 revisionExpr='HEAD',
730 revisionId=None,
731 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500732 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900733 quiet=opt.quiet,
734 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400735 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700736
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700737 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500738 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700739 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700740 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700741 return 0
742
jiajia tanga590e642021-04-25 20:02:02 +0800743 def UpdateCopyLinkfileList(self):
744 """Save all dests of copyfile and linkfile, and update them if needed.
745
746 Returns:
747 Whether update was successful.
748 """
749 new_paths = {}
750 new_linkfile_paths = []
751 new_copyfile_paths = []
752 for project in self.GetProjects(None, missing_ok=True):
753 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
754 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
755
756 new_paths = {
757 'linkfile': new_linkfile_paths,
758 'copyfile': new_copyfile_paths,
759 }
760
761 copylinkfile_name = 'copy-link-files.json'
762 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
763 old_copylinkfile_paths = {}
764
765 if os.path.exists(copylinkfile_path):
766 with open(copylinkfile_path, 'rb') as fp:
767 try:
768 old_copylinkfile_paths = json.load(fp)
769 except:
770 print('error: %s is not a json formatted file.' %
771 copylinkfile_path, file=sys.stderr)
772 platform_utils.remove(copylinkfile_path)
773 return False
774
775 need_remove_files = []
776 need_remove_files.extend(
777 set(old_copylinkfile_paths.get('linkfile', [])) -
778 set(new_linkfile_paths))
779 need_remove_files.extend(
780 set(old_copylinkfile_paths.get('copyfile', [])) -
781 set(new_copyfile_paths))
782
783 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400784 # Try to remove the updated copyfile or linkfile.
785 # So, if the file is not exist, nothing need to do.
786 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800787
788 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
789 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
790 json.dump(new_paths, fp)
791 return True
792
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400793 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
794 if not self.manifest.manifest_server:
795 print('error: cannot smart sync: no manifest server defined in '
796 'manifest', file=sys.stderr)
797 sys.exit(1)
798
799 manifest_server = self.manifest.manifest_server
800 if not opt.quiet:
801 print('Using manifest server %s' % manifest_server)
802
David Pursehouseeeff3532020-02-12 11:24:10 +0900803 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400804 username = None
805 password = None
806 if opt.manifest_server_username and opt.manifest_server_password:
807 username = opt.manifest_server_username
808 password = opt.manifest_server_password
809 else:
810 try:
811 info = netrc.netrc()
812 except IOError:
813 # .netrc file does not exist or could not be opened
814 pass
815 else:
816 try:
817 parse_result = urllib.parse.urlparse(manifest_server)
818 if parse_result.hostname:
819 auth = info.authenticators(parse_result.hostname)
820 if auth:
821 username, _account, password = auth
822 else:
823 print('No credentials found for %s in .netrc'
824 % parse_result.hostname, file=sys.stderr)
825 except netrc.NetrcParseError as e:
826 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
827
828 if (username and password):
829 manifest_server = manifest_server.replace('://', '://%s:%s@' %
830 (username, password),
831 1)
832
833 transport = PersistentTransport(manifest_server)
834 if manifest_server.startswith('persistent-'):
835 manifest_server = manifest_server[len('persistent-'):]
836
837 try:
838 server = xmlrpc.client.Server(manifest_server, transport=transport)
839 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800840 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400841
Mike Frysinger56ce3462019-12-04 19:30:48 -0500842 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500843 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400844 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500845 elif ('TARGET_PRODUCT' in os.environ and
846 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500847 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
848 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400849 [success, manifest_str] = server.GetApprovedManifest(branch, target)
850 else:
851 [success, manifest_str] = server.GetApprovedManifest(branch)
852 else:
853 assert(opt.smart_tag)
854 [success, manifest_str] = server.GetManifest(opt.smart_tag)
855
856 if success:
857 manifest_name = os.path.basename(smart_sync_manifest_path)
858 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500859 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400860 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400861 except IOError as e:
862 print('error: cannot write manifest to %s:\n%s'
863 % (smart_sync_manifest_path, e),
864 file=sys.stderr)
865 sys.exit(1)
866 self._ReloadManifest(manifest_name)
867 else:
868 print('error: manifest server RPC call failed: %s' %
869 manifest_str, file=sys.stderr)
870 sys.exit(1)
871 except (socket.error, IOError, xmlrpc.client.Fault) as e:
872 print('error: cannot connect to manifest server %s:\n%s'
873 % (self.manifest.manifest_server, e), file=sys.stderr)
874 sys.exit(1)
875 except xmlrpc.client.ProtocolError as e:
876 print('error: cannot connect to manifest server %s:\n%d %s'
877 % (self.manifest.manifest_server, e.errcode, e.errmsg),
878 file=sys.stderr)
879 sys.exit(1)
880
881 return manifest_name
882
Mike Frysingerfb527e32019-08-27 02:34:32 -0400883 def _UpdateManifestProject(self, opt, mp, manifest_name):
884 """Fetch & update the local manifest project."""
885 if not opt.local_only:
886 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500887 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700888 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200889 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500890 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400891 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600892 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400893 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700894 clone_filter=self.manifest.CloneFilter,
895 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400896 finish = time.time()
897 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
898 start, finish, success)
899
900 if mp.HasChanges:
901 syncbuf = SyncBuffer(mp.config)
902 start = time.time()
903 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
904 clean = syncbuf.Finish()
905 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
906 start, time.time(), clean)
907 if not clean:
908 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400909 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400910 if opt.jobs is None:
911 self.jobs = self.manifest.default.sync_j
912
Mike Frysingerae6cb082019-08-27 01:10:59 -0400913 def ValidateOptions(self, opt, args):
914 if opt.force_broken:
915 print('warning: -f/--force-broken is now the default behavior, and the '
916 'options are deprecated', file=sys.stderr)
917 if opt.network_only and opt.detach_head:
918 self.OptionParser.error('cannot combine -n and -d')
919 if opt.network_only and opt.local_only:
920 self.OptionParser.error('cannot combine -n and -l')
921 if opt.manifest_name and opt.smart_sync:
922 self.OptionParser.error('cannot combine -m and -s')
923 if opt.manifest_name and opt.smart_tag:
924 self.OptionParser.error('cannot combine -m and -t')
925 if opt.manifest_server_username or opt.manifest_server_password:
926 if not (opt.smart_sync or opt.smart_tag):
927 self.OptionParser.error('-u and -p may only be combined with -s or -t')
928 if None in [opt.manifest_server_username, opt.manifest_server_password]:
929 self.OptionParser.error('both -u and -p must be given')
930
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700931 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800932 if opt.jobs:
933 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700934 if self.jobs > 1:
935 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400936 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700937
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500938 if opt.manifest_name:
939 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700940
Chirayu Desaia892b102013-06-11 14:18:46 +0530941 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900942 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900943 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530944
Xin Lid79a4bc2020-05-20 16:03:45 -0700945 if opt.clone_bundle is None:
946 opt.clone_bundle = self.manifest.CloneBundle
947
Victor Boivie08c880d2011-04-19 10:32:52 +0200948 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400949 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
950 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900951 if os.path.isfile(smart_sync_manifest_path):
952 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800953 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900954 except OSError as e:
955 print('error: failed to remove existing smart sync override manifest: %s' %
956 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700957
Mike Frysingerc99322a2021-05-04 15:32:43 -0400958 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400959
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700960 rp = self.manifest.repoProject
961 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500962 cb = rp.CurrentBranch
963 if cb:
964 base = rp.GetBranch(cb).merge
965 if not base or not base.startswith('refs/heads/'):
966 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400967 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500968 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700969
970 mp = self.manifest.manifestProject
Jack Neus03ff2762021-10-15 15:43:19 +0000971 is_standalone_manifest = mp.config.GetString('manifest.standalone')
972 if not is_standalone_manifest:
973 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700974
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800975 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700976 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800977
Fredrik de Grootcc960972019-11-22 09:04:31 +0100978 if not opt.mp_update:
979 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +0000980 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +0100981 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700982
Raman Tennetifeb28912021-05-02 19:47:29 -0700983 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti7954de12021-07-28 14:36:49 -0700984 use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
985 superproject_logging_data = {
986 'superproject': use_superproject,
987 'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Raman Tennetib55769a2021-08-13 11:47:24 -0700988 'hassuperprojecttag': bool(self.manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -0700989 }
990 if use_superproject:
991 manifest_name = self._UpdateProjectsRevisionId(
992 opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800993
Simran Basib9a1b732015-08-20 12:19:28 -0700994 if self.gitc_manifest:
995 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700996 missing_ok=True)
997 gitc_projects = []
998 opened_projects = []
999 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001000 if project.relpath in self.gitc_manifest.paths and \
1001 self.gitc_manifest.paths[project.relpath].old_revision:
1002 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001003 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001004 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001005
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001006 if not args:
1007 gitc_projects = None
1008
1009 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001010 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001011 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1012 if manifest_name:
1013 manifest.Override(manifest_name)
1014 else:
1015 manifest.Override(self.manifest.manifestFile)
1016 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1017 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001018 gitc_projects)
1019 print('GITC client successfully synced.')
1020
1021 # The opened projects need to be synced as normal, therefore we
1022 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001023 # TODO: make this more reliable -- if there's a project name/path overlap,
1024 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +09001025 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
1026 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001027 if not args:
1028 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001029 all_projects = self.GetProjects(args,
1030 missing_ok=True,
1031 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001032
Mike Frysinger5a033082019-09-23 19:21:20 -04001033 err_network_sync = False
1034 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001035
Dave Borowitz67700e92012-10-23 15:00:54 -07001036 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001037 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001038 with multiprocessing.Manager() as manager:
1039 with ssh.ProxyManager(manager) as ssh_proxy:
1040 # Initialize the socket dir once in the parent.
1041 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001042 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1043 manifest_name, load_local_manifests,
1044 ssh_proxy)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001045
1046 if opt.network_only:
1047 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001048
1049 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001050 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001051 err_network_sync = True
1052 if opt.fail_fast:
1053 print('\nerror: Exited sync due to fetch errors.\n'
1054 'Local checkouts *not* updated. Resolve network issues & '
1055 'retry.\n'
1056 '`repo sync -l` will update some local checkouts.',
1057 file=sys.stderr)
1058 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001059
Julien Campergue335f5ef2013-10-16 11:02:35 +02001060 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001061 # bail out now, we have no working tree
1062 return
1063
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001064 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001065 err_event.set()
1066 err_update_projects = True
1067 if opt.fail_fast:
1068 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1069 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001070
Mike Frysinger14208f42021-05-04 15:31:51 -04001071 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1072 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001073 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001074 if opt.fail_fast:
1075 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1076 sys.exit(1)
1077
Mike Frysinger5a033082019-09-23 19:21:20 -04001078 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001079 # NB: We don't exit here because this is the last step.
1080 err_checkout = not self._Checkout(all_projects, opt, err_results)
1081 if err_checkout:
1082 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001084 # If there's a notice that's supposed to print at the end of the sync, print
1085 # it now...
1086 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001087 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001088
Mike Frysinger5a033082019-09-23 19:21:20 -04001089 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001090 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001091 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1092 if err_network_sync:
1093 print('error: Downloading network changes failed.', file=sys.stderr)
1094 if err_update_projects:
1095 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001096 if err_update_linkfiles:
1097 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001098 if err_checkout:
1099 print('error: Checking out local projects failed.', file=sys.stderr)
1100 if err_results:
1101 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1102 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1103 file=sys.stderr)
1104 sys.exit(1)
1105
Raman Tenneti7954de12021-07-28 14:36:49 -07001106 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001107 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1108 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001109
1110 # Update and log with the new sync analysis state.
1111 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001112 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1113 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001114
Mike Frysingere19d9e12020-02-12 11:23:32 -05001115 if not opt.quiet:
1116 print('repo sync has finished successfully.')
1117
David Pursehouse819827a2020-02-12 15:20:19 +09001118
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001119def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001120 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001121 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001122 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001123 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001124 if project.Exists:
1125 project.PostRepoUpgrade()
1126
David Pursehouse819827a2020-02-12 15:20:19 +09001127
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001128def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001129 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001130 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001131 wrapper = Wrapper()
1132 try:
1133 rev = rp.bare_git.describe(rp.GetRevisionId())
1134 except GitError:
1135 rev = None
1136 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1137 # See if we're held back due to missing signed tag.
1138 current_revid = rp.bare_git.rev_parse('HEAD')
1139 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1140 if current_revid != new_revid:
1141 # We want to switch to the new rev, but also not trash any uncommitted
1142 # changes. This helps with local testing/hacking.
1143 # If a local change has been made, we will throw that away.
1144 # We also have to make sure this will switch to an older commit if that's
1145 # the latest tag in order to support release rollback.
1146 try:
1147 rp.work_git.reset('--keep', new_rev)
1148 except GitError as e:
1149 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001150 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001151 raise RepoChangedException(['--repo-upgraded'])
1152 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001153 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001154 else:
1155 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001156 print('repo version %s is current' % rp.work_git.describe(HEAD),
1157 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001158
David Pursehouse819827a2020-02-12 15:20:19 +09001159
Dave Borowitz67700e92012-10-23 15:00:54 -07001160class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001161 _ALPHA = 0.5
1162
Dave Borowitz67700e92012-10-23 15:00:54 -07001163 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001164 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001165 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001166 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001167
1168 def Get(self, project):
1169 self._Load()
1170 return self._times.get(project.name, _ONE_DAY_S)
1171
1172 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001173 self._Load()
1174 name = project.name
1175 old = self._times.get(name, t)
1176 self._seen.add(name)
1177 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001178 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001179
1180 def _Load(self):
1181 if self._times is None:
1182 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001183 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001184 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001185 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001186 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001187 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001188
1189 def Save(self):
1190 if self._times is None:
1191 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001192
1193 to_delete = []
1194 for name in self._times:
1195 if name not in self._seen:
1196 to_delete.append(name)
1197 for name in to_delete:
1198 del self._times[name]
1199
Dave Borowitz67700e92012-10-23 15:00:54 -07001200 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001201 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001202 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001203 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001204 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001205
1206# This is a replacement for xmlrpc.client.Transport using urllib2
1207# and supporting persistent-http[s]. It cannot change hosts from
1208# request to request like the normal transport, the real url
1209# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001210
1211
Dan Willemsen0745bb22015-08-17 13:41:45 -07001212class PersistentTransport(xmlrpc.client.Transport):
1213 def __init__(self, orig_host):
1214 self.orig_host = orig_host
1215
1216 def request(self, host, handler, request_body, verbose=False):
1217 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1218 # Python doesn't understand cookies with the #HttpOnly_ prefix
1219 # Since we're only using them for HTTP, copy the file temporarily,
1220 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001221 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001222 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001223 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001224 try:
1225 with open(cookiefile) as f:
1226 for line in f:
1227 if line.startswith("#HttpOnly_"):
1228 line = line[len("#HttpOnly_"):]
1229 tmpcookiefile.write(line)
1230 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001231
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001232 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001233 try:
1234 cookiejar.load()
1235 except cookielib.LoadError:
1236 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001237 finally:
1238 tmpcookiefile.close()
1239 else:
1240 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001241
1242 proxyhandler = urllib.request.ProxyHandler
1243 if proxy:
1244 proxyhandler = urllib.request.ProxyHandler({
1245 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001246 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001247
1248 opener = urllib.request.build_opener(
1249 urllib.request.HTTPCookieProcessor(cookiejar),
1250 proxyhandler)
1251
1252 url = urllib.parse.urljoin(self.orig_host, handler)
1253 parse_results = urllib.parse.urlparse(url)
1254
1255 scheme = parse_results.scheme
1256 if scheme == 'persistent-http':
1257 scheme = 'http'
1258 if scheme == 'persistent-https':
1259 # If we're proxying through persistent-https, use http. The
1260 # proxy itself will do the https.
1261 if proxy:
1262 scheme = 'http'
1263 else:
1264 scheme = 'https'
1265
1266 # Parse out any authentication information using the base class
1267 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1268
1269 url = urllib.parse.urlunparse((
1270 scheme,
1271 host,
1272 parse_results.path,
1273 parse_results.params,
1274 parse_results.query,
1275 parse_results.fragment))
1276
1277 request = urllib.request.Request(url, request_body)
1278 if extra_headers is not None:
1279 for (name, header) in extra_headers:
1280 request.add_header(name, header)
1281 request.add_header('Content-Type', 'text/xml')
1282 try:
1283 response = opener.open(request)
1284 except urllib.error.HTTPError as e:
1285 if e.code == 501:
1286 # We may have been redirected through a login process
1287 # but our POST turned into a GET. Retry.
1288 response = opener.open(request)
1289 else:
1290 raise
1291
1292 p, u = xmlrpc.client.getparser()
1293 while 1:
1294 data = response.read(1024)
1295 if not data:
1296 break
1297 p.feed(data)
1298 p.close()
1299 return u.close()
1300
1301 def close(self):
1302 pass