blob: 0170409589190fc891c9c2c817a0f91ca04c3865 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
jiajia tanga590e642021-04-25 20:02:02 +080015import errno
Mike Frysingerebf04a42021-02-23 20:48:04 -050016import functools
Mike Frysingeracf63b22019-06-13 02:24:21 -040017import http.cookiejar as cookielib
Mike Frysinger7b586f22021-02-23 18:38:39 -050018import io
Anthony King85b24ac2014-05-06 15:57:48 +010019import json
Mike Frysingerebf04a42021-02-23 20:48:04 -050020import multiprocessing
David Pursehouse86d973d2012-08-24 10:21:02 +090021import netrc
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -070022from optparse import SUPPRESS_HELP
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import os
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070026import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.error
29import urllib.parse
30import urllib.request
31import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032
Roy Lee18afd7f2010-05-09 04:32:08 +080033try:
34 import threading as _threading
35except ImportError:
36 import dummy_threading as _threading
37
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070038try:
39 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090040
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070041 def _rlimit_nofile():
42 return resource.getrlimit(resource.RLIMIT_NOFILE)
43except ImportError:
44 def _rlimit_nofile():
45 return (256, 256)
46
David Rileye0684ad2017-04-05 00:02:59 -070047import event_log
Mike Frysinger347f9ed2021-03-15 14:58:52 -040048from git_command import git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090049from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090050from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080051import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070052import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070053from project import Project
54from project import RemoteSpec
Mike Frysingerd41eed02021-04-20 23:21:29 -040055from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080056from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070057import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070058from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070059from progress import Progress
Mike Frysinger19e409c2021-05-05 19:44:35 -040060import ssh
Conley Owens094cdbe2014-01-30 15:09:59 -080061from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070062from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070063
Dave Borowitz67700e92012-10-23 15:00:54 -070064_ONE_DAY_S = 24 * 60 * 60
65
David Pursehouse819827a2020-02-12 15:20:19 +090066
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080067class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080068 jobs = 1
Mike Frysinger4f210542021-06-14 16:05:19 -040069 COMMON = True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070070 helpSummary = "Update working tree to the latest revision"
71 helpUsage = """
72%prog [<project>...]
73"""
74 helpDescription = """
75The '%prog' command synchronizes local project directories
76with the remote repositories specified in the manifest. If a local
77project does not yet exist, it will clone a new local directory from
78the remote repository and set up tracking branches as specified in
79the manifest. If the local project already exists, '%prog'
80will update the remote branches and rebase any new local changes
81on top of the new remote changes.
82
83'%prog' will synchronize all projects listed at the command
84line. Projects can be specified either by name, or by a relative
85or absolute path to the project's local directory. If no projects
86are specified, '%prog' will synchronize all projects listed in
87the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070088
89The -d/--detach option can be used to switch specified projects
90back to the manifest revision. This option is especially helpful
91if the project is currently on a topic branch, but the manifest
92revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070093
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070094The -s/--smart-sync option can be used to sync to a known good
95build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020096manifest. The -t/--smart-tag option is similar and allows you to
97specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070098
David Pursehousecf76b1b2012-09-14 10:31:42 +090099The -u/--manifest-server-username and -p/--manifest-server-password
100options can be used to specify a username and password to authenticate
101with the manifest server when using the -s or -t option.
102
103If -u and -p are not specified when using the -s or -t option, '%prog'
104will attempt to read authentication credentials for the manifest server
105from the user's .netrc file.
106
107'%prog' will not use authentication credentials from -u/-p or .netrc
108if the manifest server specified in the manifest file already includes
109credentials.
110
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400111By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400112to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500113
Kevin Degiabaa7f32014-11-12 11:27:45 -0700114The --force-sync option can be used to overwrite existing git
115directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900116object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700117refs may be removed when overwriting.
118
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500119The --force-remove-dirty option can be used to remove previously used
120projects with uncommitted changes. WARNING: This may cause data to be
121lost since uncommitted changes may be removed with projects that no longer
122exist in the manifest.
123
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700124The --no-clone-bundle option disables any attempt to use
125$URL/clone.bundle to bootstrap a new Git repository from a
126resumeable bundle file on a content delivery network. This
127may be necessary if there are problems with the local Python
128HTTP client or proxy configuration, but the Git binary works.
129
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800130The --fetch-submodules option enables fetching Git submodules
131of a project from server.
132
David Pursehousef2fad612015-01-29 14:36:28 +0900133The -c/--current-branch option can be used to only fetch objects that
134are on the branch specified by a project's revision.
135
David Pursehouseb1553542014-09-04 21:28:09 +0900136The --optimized-fetch option can be used to only fetch projects that
137are fixed to a sha1 revision if the sha1 revision does not already
138exist locally.
139
David Pursehouse74cfd272015-10-14 10:50:15 +0900140The --prune option can be used to remove any refs that no longer
141exist on the remote.
142
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400143# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700144
145If at least one project remote URL uses an SSH connection (ssh://,
146git+ssh://, or user@host:path syntax) repo will automatically
147enable the SSH ControlMaster option when connecting to that host.
148This feature permits other projects in the same '%prog' session to
149reuse the same SSH tunnel, saving connection setup overheads.
150
151To disable this behavior on UNIX platforms, set the GIT_SSH
152environment variable to 'ssh'. For example:
153
154 export GIT_SSH=ssh
155 %prog
156
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400157# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700158
159This feature is automatically disabled on Windows, due to the lack
160of UNIX domain socket support.
161
162This feature is not compatible with url.insteadof rewrites in the
163user's ~/.gitconfig. '%prog' is currently not able to perform the
164rewrite early enough to establish the ControlMaster tunnel.
165
166If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
167later is required to fix a server side protocol bug.
168
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500170 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171
Mike Frysinger9180a072021-04-13 14:57:40 -0400172 def _CommonOptions(self, p):
Mike Frysingerc177f942021-05-04 08:06:36 -0400173 if self.manifest:
174 try:
175 self.PARALLEL_JOBS = self.manifest.default.sync_j
176 except ManifestParseError:
177 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400178 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700179
Mike Frysinger9180a072021-04-13 14:57:40 -0400180 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400181 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
182 help='number of network jobs to run in parallel (defaults to --jobs)')
183 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
184 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
185
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500186 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200187 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400188 help='obsolete option (to be deleted in the future)')
189 p.add_option('--fail-fast',
190 dest='fail_fast', action='store_true',
191 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700192 p.add_option('--force-sync',
193 dest='force_sync', action='store_true',
194 help="overwrite an existing git directory if it needs to "
195 "point to a different object directory. WARNING: this "
196 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500197 p.add_option('--force-remove-dirty',
198 dest='force_remove_dirty', action='store_true',
199 help="force remove projects with uncommitted modifications if "
200 "projects no longer exist in the manifest. "
201 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900202 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700203 dest='local_only', action='store_true',
204 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900205 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100206 dest='mp_update', action='store_false', default='true',
207 help='use the existing manifest checkout as-is. '
208 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700210 dest='network_only', action='store_true',
211 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700213 dest='detach_head', action='store_true',
214 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700216 dest='current_branch_only', action='store_true',
217 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400218 p.add_option('--no-current-branch',
219 dest='current_branch_only', action='store_false',
220 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500221 p.add_option('-m', '--manifest-name',
222 dest='manifest_name',
223 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700224 p.add_option('--clone-bundle', action='store_true',
225 help='enable use of /clone.bundle on HTTP/HTTPS')
226 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700227 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800228 p.add_option('-u', '--manifest-server-username', action='store',
229 dest='manifest_server_username',
230 help='username to authenticate with the manifest server')
231 p.add_option('-p', '--manifest-server-password', action='store',
232 dest='manifest_server_password',
233 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800234 p.add_option('--fetch-submodules',
235 dest='fetch_submodules', action='store_true',
236 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800237 p.add_option('--use-superproject', action='store_true',
238 help='use the manifest superproject to sync projects')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700239 p.add_option('--no-use-superproject', action='store_false',
240 dest='use_superproject',
241 help='disable use of manifest superprojects')
Mike Frysingerd68ed632021-05-03 01:21:35 -0400242 p.add_option('--tags',
243 action='store_false',
244 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700245 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400246 dest='tags', action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700247 help="don't fetch tags")
David Pursehouseb1553542014-09-04 21:28:09 +0900248 p.add_option('--optimized-fetch',
249 dest='optimized_fetch', action='store_true',
250 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600251 p.add_option('--retry-fetches',
252 default=0, action='store', type='int',
253 help='number of times to retry fetches on transient errors')
David Pursehouse74cfd272015-10-14 10:50:15 +0900254 p.add_option('--prune', dest='prune', action='store_true',
255 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700256 if show_smart:
257 p.add_option('-s', '--smart-sync',
258 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900259 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200260 p.add_option('-t', '--smart-tag',
261 dest='smart_tag', action='store',
262 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700263
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700264 g = p.add_option_group('repo Version options')
265 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500266 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700268 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800269 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700270 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800272 def _GetBranch(self):
273 """Returns the branch name for getting the approved manifest."""
274 p = self.manifest.manifestProject
275 b = p.GetBranch(p.CurrentBranch)
276 branch = b.merge
277 if branch.startswith(R_HEADS):
278 branch = branch[len(R_HEADS):]
279 return branch
280
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700281 def _GetCurrentBranchOnly(self, opt):
282 """Returns True if current-branch or use-superproject options are enabled."""
Xin Li0cb6e922021-06-16 10:19:00 -0700283 return opt.current_branch_only or git_superproject.UseSuperproject(opt, self.manifest)
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700284
Raman Tenneti7954de12021-07-28 14:36:49 -0700285 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800286 """Update revisionId of every project with the SHA from superproject.
287
288 This function updates each project's revisionId with SHA from superproject.
289 It writes the updated manifest into a file and reloads the manifest from it.
290
291 Args:
292 opt: Program options returned from optparse. See _Options().
293 args: Arguments to pass to GetProjects. See the GetProjects
294 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700295 load_local_manifests: Whether to load local manifests.
Raman Tenneti7954de12021-07-28 14:36:49 -0700296 superproject_logging_data: A dictionary of superproject data that is to be logged.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800297
298 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700299 Returns path to the overriding manifest file instead of None.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800300 """
Raman Tennetib55769a2021-08-13 11:47:24 -0700301 print_messages = git_superproject.PrintMessages(opt, self.manifest)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800302 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800303 self.repodir,
Raman Tenneti784e16f2021-06-11 17:29:45 -0700304 self.git_event_log,
Raman Tennetib55769a2021-08-13 11:47:24 -0700305 quiet=opt.quiet,
306 print_messages=print_messages)
Raman Tennetiae86a462021-07-27 08:54:59 -0700307 if opt.local_only:
308 manifest_path = superproject.manifest_path
309 if manifest_path:
310 self._ReloadManifest(manifest_path, load_local_manifests)
311 return manifest_path
312
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800313 all_projects = self.GetProjects(args,
314 missing_ok=True,
315 submodules_ok=opt.fetch_submodules)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700316 update_result = superproject.UpdateProjectsRevisionId(all_projects)
317 manifest_path = update_result.manifest_path
Raman Tenneti7954de12021-07-28 14:36:49 -0700318 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700319 if manifest_path:
320 self._ReloadManifest(manifest_path, load_local_manifests)
321 else:
Raman Tennetib55769a2021-08-13 11:47:24 -0700322 if print_messages:
323 print('warning: Update of revisionId from superproject has failed, '
324 'repo sync will not use superproject to fetch the source. ',
325 'Please resync with the --no-use-superproject option to avoid this repo warning.',
326 file=sys.stderr)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700327 if update_result.fatal and opt.use_superproject is not None:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700328 sys.exit(1)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800329 return manifest_path
330
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500331 def _FetchProjectList(self, opt, projects):
332 """Main function of the fetch worker.
333
334 The projects we're given share the same underlying git object store, so we
335 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800336
David James8d201162013-10-11 17:03:19 -0700337 Delegates most of the work to _FetchHelper.
338
339 Args:
340 opt: Program options returned from optparse. See _Options().
341 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700342 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500343 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700344
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500345 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700346 """Fetch git objects for a single project.
347
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 Args:
349 opt: Program options returned from optparse. See _Options().
350 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700351
352 Returns:
353 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 """
David Rileye0684ad2017-04-05 00:02:59 -0700355 start = time.time()
356 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500357 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900358 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500359 success = project.Sync_NetworkHalf(
360 quiet=opt.quiet,
361 verbose=opt.verbose,
362 output_redir=buf,
363 current_branch_only=self._GetCurrentBranchOnly(opt),
364 force_sync=opt.force_sync,
365 clone_bundle=opt.clone_bundle,
366 tags=opt.tags, archive=self.manifest.IsArchive,
367 optimized_fetch=opt.optimized_fetch,
368 retry_fetches=opt.retry_fetches,
369 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400370 ssh_proxy=self.ssh_proxy,
Raman Tennetif32f2432021-04-12 20:57:25 -0700371 clone_filter=self.manifest.CloneFilter,
372 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700373
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500374 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400375 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500376 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700377
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500378 if not success:
379 print('error: Cannot fetch %s from %s'
380 % (project.name, project.remote.url),
381 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700382 except GitError as e:
383 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500384 except Exception as e:
385 print('error: Cannot fetch %s (%s: %s)'
386 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
387 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500388
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500389 finish = time.time()
390 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700391
Mike Frysinger339f2df2021-05-06 00:44:42 -0400392 @classmethod
393 def _FetchInitChild(cls, ssh_proxy):
394 cls.ssh_proxy = ssh_proxy
395
396 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500397 ret = True
398
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400399 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700400 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400401 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800402
David James89ece422014-01-09 18:51:58 -0800403 objdir_project_map = dict()
404 for project in projects:
405 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500406 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700407
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500408 def _ProcessResults(results_sets):
409 ret = True
410 for results in results_sets:
411 for (success, project, start, finish) in results:
412 self._fetch_times.Set(project, finish - start)
413 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
414 start, finish, success)
415 # Check for any errors before running any more tasks.
416 # ...we'll let existing jobs finish, though.
417 if not success:
418 ret = False
419 else:
420 fetched.add(project.gitdir)
421 pm.update(msg=project.name)
422 if not ret and opt.fail_fast:
423 break
424 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700425
Mike Frysinger339f2df2021-05-06 00:44:42 -0400426 # We pass the ssh proxy settings via the class. This allows multiprocessing
427 # to pickle it up when spawning children. We can't pass it as an argument
428 # to _FetchProjectList below as multiprocessing is unable to pickle those.
429 Sync.ssh_proxy = None
430
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500431 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400432 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400433 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500434 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
435 ret = False
436 else:
437 # Favor throughput over responsiveness when quiet. It seems that imap()
438 # will yield results in batches relative to chunksize, so even as the
439 # children finish a sync, we won't see the result until one child finishes
440 # ~chunksize jobs. When using a large --jobs with large chunksize, this
441 # can be jarring as there will be a large initial delay where repo looks
442 # like it isn't doing anything and sits at 0%, but then suddenly completes
443 # a lot of jobs all at once. Since this code is more network bound, we
444 # can accept a bit more CPU overhead with a smaller chunksize so that the
445 # user sees more immediate & continuous feedback.
446 if opt.quiet:
447 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800448 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500449 pm.update(inc=0, msg='warming up')
450 chunksize = 4
Mike Frysinger339f2df2021-05-06 00:44:42 -0400451 with multiprocessing.Pool(
452 jobs, initializer=self._FetchInitChild, initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500453 results = pool.imap_unordered(
454 functools.partial(self._FetchProjectList, opt),
455 projects_list,
456 chunksize=chunksize)
457 if not _ProcessResults(results):
458 ret = False
459 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800460
Mike Frysinger339f2df2021-05-06 00:44:42 -0400461 # Cleanup the reference now that we're done with it, and we're going to
462 # release any resources it points to. If we don't, later multiprocessing
463 # usage (e.g. checkouts) will try to pickle and then crash.
464 del Sync.ssh_proxy
465
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700466 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700467 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700468
Julien Campergue335f5ef2013-10-16 11:02:35 +0200469 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400470 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200471
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500472 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700473
Mike Frysingerb4429432021-05-05 20:03:26 -0400474 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400475 load_local_manifests, ssh_proxy):
Mike Frysingerb4429432021-05-05 20:03:26 -0400476 """The main network fetch loop.
477
478 Args:
479 opt: Program options returned from optparse. See _Options().
480 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200481 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400482 err_event: Whether an error was hit while processing.
483 manifest_name: Manifest file to be reloaded.
484 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400485 ssh_proxy: SSH manager for clients & masters.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200486
487 Returns:
488 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400489 """
490 rp = self.manifest.repoProject
491
492 to_fetch = []
493 now = time.time()
494 if _ONE_DAY_S <= (now - rp.LastFetch):
495 to_fetch.append(rp)
496 to_fetch.extend(all_projects)
497 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
498
Mike Frysinger339f2df2021-05-06 00:44:42 -0400499 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400500 if not success:
501 err_event.set()
502
503 _PostRepoFetch(rp, opt.repo_verify)
504 if opt.network_only:
505 # bail out now; the rest touches the working tree
506 if err_event.is_set():
507 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
508 sys.exit(1)
509 return
510
511 # Iteratively fetch missing and/or nested unregistered submodules
512 previously_missing_set = set()
513 while True:
514 self._ReloadManifest(manifest_name, load_local_manifests)
515 all_projects = self.GetProjects(args,
516 missing_ok=True,
517 submodules_ok=opt.fetch_submodules)
518 missing = []
519 for project in all_projects:
520 if project.gitdir not in fetched:
521 missing.append(project)
522 if not missing:
523 break
524 # Stop us from non-stopped fetching actually-missing repos: If set of
525 # missing repos has not been changed from last fetch, we break.
526 missing_set = set(p.name for p in missing)
527 if previously_missing_set == missing_set:
528 break
529 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400530 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400531 if not success:
532 err_event.set()
533 fetched.update(new_fetched)
534
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200535 return all_projects
536
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500537 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700538 """Checkout work tree for one project
539
540 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500541 detach_head: Whether to leave a detached HEAD.
542 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700543 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700544
545 Returns:
546 Whether the fetch was successful.
547 """
Xin Li745be2e2019-06-03 11:24:30 -0700548 start = time.time()
549 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500550 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700551 success = False
552 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500553 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500554 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700555 except GitError as e:
556 print('error.GitError: Cannot checkout %s: %s' %
557 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500558 except Exception as e:
559 print('error: Cannot checkout %s: %s: %s' %
560 (project.name, type(e).__name__, str(e)),
561 file=sys.stderr)
562 raise
Xin Li745be2e2019-06-03 11:24:30 -0700563
Mike Frysingerebf04a42021-02-23 20:48:04 -0500564 if not success:
565 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
566 finish = time.time()
567 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700568
Mike Frysingerebf04a42021-02-23 20:48:04 -0500569 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700570 """Checkout projects listed in all_projects
571
572 Args:
573 all_projects: List of all projects that should be checked out.
574 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500575 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700576 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500577 # Only checkout projects with worktrees.
578 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700579
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500580 def _ProcessResults(pool, pm, results):
581 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500582 for (success, project, start, finish) in results:
583 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
584 start, finish, success)
585 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500586 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500587 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500588 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500589 err_results.append(project.relpath)
590 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500591 if pool:
592 pool.close()
593 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500594 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500595 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700596
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500597 return self.ExecuteInParallel(
598 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
599 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
600 all_projects,
601 callback=_ProcessResults,
602 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500603
Mike Frysinger5a033082019-09-23 19:21:20 -0400604 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400605 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400606 pm.update(inc=0, msg='prescan')
607
Allen Webb4ee4a452021-10-07 10:42:38 -0500608 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700609 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500610 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500611 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900612 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100613 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400614 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100615 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500616 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500617 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500618 else:
619 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400620 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500621 'versions of git; please upgrade to git-2.7.0+.'
622 % (project.relpath,),
623 file=sys.stderr)
624 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500625 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500626 # Only call git gc once per objdir, but call pack-refs for the remainder.
627 if project.objdir not in tidy_dirs:
628 tidy_dirs[project.objdir] = (
629 True, # Run a full gc.
630 project.bare_git,
631 )
632 elif project.gitdir not in tidy_dirs:
633 tidy_dirs[project.gitdir] = (
634 False, # Do not run a full gc; just run pack-refs.
635 project.bare_git,
636 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400637
638 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700639 jobs = min(self.jobs, cpu_count)
640
641 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500642 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400643 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500644 if run_gc:
645 bare_git.gc('--auto')
646 else:
647 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400648 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700649 return
650
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400651 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700652
653 threads = set()
654 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700655
Allen Webb4ee4a452021-10-07 10:42:38 -0500656 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400657 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700658 try:
659 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500660 if run_gc:
661 bare_git.gc('--auto', config=config)
662 else:
663 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700664 except GitError:
665 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900666 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700667 err_event.set()
668 raise
669 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400670 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700671 sem.release()
672
Allen Webb4ee4a452021-10-07 10:42:38 -0500673 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500674 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700675 break
676 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500677 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700678 t.daemon = True
679 threads.add(t)
680 t.start()
681
682 for t in threads:
683 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400684 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700685
Raman Tennetifeb28912021-05-02 19:47:29 -0700686 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
687 """Reload the manfiest from the file specified by the |manifest_name|.
688
689 It unloads the manifest if |manifest_name| is None.
690
691 Args:
692 manifest_name: Manifest file to be reloaded.
693 load_local_manifests: Whether to load local manifests.
694 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800695 if manifest_name:
696 # Override calls _Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700697 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800698 else:
699 self.manifest._Unload()
700
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500701 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700702 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700703 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700704 if project.relpath:
705 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700706 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500707 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700708 old_project_paths = []
709
710 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500711 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700712 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800713 # In reversed order, so subfolders are deleted before parent folder.
714 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700715 if not path:
716 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700717 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900718 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700719 gitdir = os.path.join(self.manifest.topdir, path, '.git')
720 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900721 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900722 manifest=self.manifest,
723 name=path,
724 remote=RemoteSpec('origin'),
725 gitdir=gitdir,
726 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500727 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900728 worktree=os.path.join(self.manifest.topdir, path),
729 relpath=path,
730 revisionExpr='HEAD',
731 revisionId=None,
732 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500733 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900734 quiet=opt.quiet,
735 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400736 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700737
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700738 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500739 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700740 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700741 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700742 return 0
743
jiajia tanga590e642021-04-25 20:02:02 +0800744 def UpdateCopyLinkfileList(self):
745 """Save all dests of copyfile and linkfile, and update them if needed.
746
747 Returns:
748 Whether update was successful.
749 """
750 new_paths = {}
751 new_linkfile_paths = []
752 new_copyfile_paths = []
753 for project in self.GetProjects(None, missing_ok=True):
754 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
755 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
756
757 new_paths = {
758 'linkfile': new_linkfile_paths,
759 'copyfile': new_copyfile_paths,
760 }
761
762 copylinkfile_name = 'copy-link-files.json'
763 copylinkfile_path = os.path.join(self.manifest.repodir, copylinkfile_name)
764 old_copylinkfile_paths = {}
765
766 if os.path.exists(copylinkfile_path):
767 with open(copylinkfile_path, 'rb') as fp:
768 try:
769 old_copylinkfile_paths = json.load(fp)
770 except:
771 print('error: %s is not a json formatted file.' %
772 copylinkfile_path, file=sys.stderr)
773 platform_utils.remove(copylinkfile_path)
774 return False
775
776 need_remove_files = []
777 need_remove_files.extend(
778 set(old_copylinkfile_paths.get('linkfile', [])) -
779 set(new_linkfile_paths))
780 need_remove_files.extend(
781 set(old_copylinkfile_paths.get('copyfile', [])) -
782 set(new_copyfile_paths))
783
784 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400785 # Try to remove the updated copyfile or linkfile.
786 # So, if the file is not exist, nothing need to do.
787 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800788
789 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
790 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
791 json.dump(new_paths, fp)
792 return True
793
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400794 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
795 if not self.manifest.manifest_server:
796 print('error: cannot smart sync: no manifest server defined in '
797 'manifest', file=sys.stderr)
798 sys.exit(1)
799
800 manifest_server = self.manifest.manifest_server
801 if not opt.quiet:
802 print('Using manifest server %s' % manifest_server)
803
David Pursehouseeeff3532020-02-12 11:24:10 +0900804 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400805 username = None
806 password = None
807 if opt.manifest_server_username and opt.manifest_server_password:
808 username = opt.manifest_server_username
809 password = opt.manifest_server_password
810 else:
811 try:
812 info = netrc.netrc()
813 except IOError:
814 # .netrc file does not exist or could not be opened
815 pass
816 else:
817 try:
818 parse_result = urllib.parse.urlparse(manifest_server)
819 if parse_result.hostname:
820 auth = info.authenticators(parse_result.hostname)
821 if auth:
822 username, _account, password = auth
823 else:
824 print('No credentials found for %s in .netrc'
825 % parse_result.hostname, file=sys.stderr)
826 except netrc.NetrcParseError as e:
827 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
828
829 if (username and password):
830 manifest_server = manifest_server.replace('://', '://%s:%s@' %
831 (username, password),
832 1)
833
834 transport = PersistentTransport(manifest_server)
835 if manifest_server.startswith('persistent-'):
836 manifest_server = manifest_server[len('persistent-'):]
837
838 try:
839 server = xmlrpc.client.Server(manifest_server, transport=transport)
840 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800841 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400842
Mike Frysinger56ce3462019-12-04 19:30:48 -0500843 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500844 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400845 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500846 elif ('TARGET_PRODUCT' in os.environ and
847 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500848 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
849 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400850 [success, manifest_str] = server.GetApprovedManifest(branch, target)
851 else:
852 [success, manifest_str] = server.GetApprovedManifest(branch)
853 else:
854 assert(opt.smart_tag)
855 [success, manifest_str] = server.GetManifest(opt.smart_tag)
856
857 if success:
858 manifest_name = os.path.basename(smart_sync_manifest_path)
859 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500860 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400861 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400862 except IOError as e:
863 print('error: cannot write manifest to %s:\n%s'
864 % (smart_sync_manifest_path, e),
865 file=sys.stderr)
866 sys.exit(1)
867 self._ReloadManifest(manifest_name)
868 else:
869 print('error: manifest server RPC call failed: %s' %
870 manifest_str, file=sys.stderr)
871 sys.exit(1)
872 except (socket.error, IOError, xmlrpc.client.Fault) as e:
873 print('error: cannot connect to manifest server %s:\n%s'
874 % (self.manifest.manifest_server, e), file=sys.stderr)
875 sys.exit(1)
876 except xmlrpc.client.ProtocolError as e:
877 print('error: cannot connect to manifest server %s:\n%d %s'
878 % (self.manifest.manifest_server, e.errcode, e.errmsg),
879 file=sys.stderr)
880 sys.exit(1)
881
882 return manifest_name
883
Mike Frysingerfb527e32019-08-27 02:34:32 -0400884 def _UpdateManifestProject(self, opt, mp, manifest_name):
885 """Fetch & update the local manifest project."""
886 if not opt.local_only:
887 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500888 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700889 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200890 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500891 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400892 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600893 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400894 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700895 clone_filter=self.manifest.CloneFilter,
896 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400897 finish = time.time()
898 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
899 start, finish, success)
900
901 if mp.HasChanges:
902 syncbuf = SyncBuffer(mp.config)
903 start = time.time()
904 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
905 clean = syncbuf.Finish()
906 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
907 start, time.time(), clean)
908 if not clean:
909 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400910 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400911 if opt.jobs is None:
912 self.jobs = self.manifest.default.sync_j
913
Mike Frysingerae6cb082019-08-27 01:10:59 -0400914 def ValidateOptions(self, opt, args):
915 if opt.force_broken:
916 print('warning: -f/--force-broken is now the default behavior, and the '
917 'options are deprecated', file=sys.stderr)
918 if opt.network_only and opt.detach_head:
919 self.OptionParser.error('cannot combine -n and -d')
920 if opt.network_only and opt.local_only:
921 self.OptionParser.error('cannot combine -n and -l')
922 if opt.manifest_name and opt.smart_sync:
923 self.OptionParser.error('cannot combine -m and -s')
924 if opt.manifest_name and opt.smart_tag:
925 self.OptionParser.error('cannot combine -m and -t')
926 if opt.manifest_server_username or opt.manifest_server_password:
927 if not (opt.smart_sync or opt.smart_tag):
928 self.OptionParser.error('-u and -p may only be combined with -s or -t')
929 if None in [opt.manifest_server_username, opt.manifest_server_password]:
930 self.OptionParser.error('both -u and -p must be given')
931
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700932 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800933 if opt.jobs:
934 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700935 if self.jobs > 1:
936 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400937 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700938
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500939 if opt.manifest_name:
940 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700941
Chirayu Desaia892b102013-06-11 14:18:46 +0530942 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900943 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900944 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530945
Xin Lid79a4bc2020-05-20 16:03:45 -0700946 if opt.clone_bundle is None:
947 opt.clone_bundle = self.manifest.CloneBundle
948
Victor Boivie08c880d2011-04-19 10:32:52 +0200949 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400950 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
951 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900952 if os.path.isfile(smart_sync_manifest_path):
953 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800954 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900955 except OSError as e:
956 print('error: failed to remove existing smart sync override manifest: %s' %
957 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700958
Mike Frysingerc99322a2021-05-04 15:32:43 -0400959 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400960
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700961 rp = self.manifest.repoProject
962 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500963 cb = rp.CurrentBranch
964 if cb:
965 base = rp.GetBranch(cb).merge
966 if not base or not base.startswith('refs/heads/'):
967 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400968 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500969 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700970
971 mp = self.manifest.manifestProject
Jack Neus03ff2762021-10-15 15:43:19 +0000972 is_standalone_manifest = mp.config.GetString('manifest.standalone')
973 if not is_standalone_manifest:
974 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700975
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800976 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700977 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800978
Fredrik de Grootcc960972019-11-22 09:04:31 +0100979 if not opt.mp_update:
980 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +0000981 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +0100982 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700983
Raman Tennetifeb28912021-05-02 19:47:29 -0700984 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti7954de12021-07-28 14:36:49 -0700985 use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
986 superproject_logging_data = {
987 'superproject': use_superproject,
988 'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Raman Tennetib55769a2021-08-13 11:47:24 -0700989 'hassuperprojecttag': bool(self.manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -0700990 }
991 if use_superproject:
992 manifest_name = self._UpdateProjectsRevisionId(
993 opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800994
Simran Basib9a1b732015-08-20 12:19:28 -0700995 if self.gitc_manifest:
996 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700997 missing_ok=True)
998 gitc_projects = []
999 opened_projects = []
1000 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001001 if project.relpath in self.gitc_manifest.paths and \
1002 self.gitc_manifest.paths[project.relpath].old_revision:
1003 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001004 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001005 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001006
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001007 if not args:
1008 gitc_projects = None
1009
1010 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001011 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001012 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1013 if manifest_name:
1014 manifest.Override(manifest_name)
1015 else:
1016 manifest.Override(self.manifest.manifestFile)
1017 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1018 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001019 gitc_projects)
1020 print('GITC client successfully synced.')
1021
1022 # The opened projects need to be synced as normal, therefore we
1023 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001024 # TODO: make this more reliable -- if there's a project name/path overlap,
1025 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +09001026 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
1027 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001028 if not args:
1029 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001030 all_projects = self.GetProjects(args,
1031 missing_ok=True,
1032 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033
Mike Frysinger5a033082019-09-23 19:21:20 -04001034 err_network_sync = False
1035 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001036
Dave Borowitz67700e92012-10-23 15:00:54 -07001037 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001038 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001039 with multiprocessing.Manager() as manager:
1040 with ssh.ProxyManager(manager) as ssh_proxy:
1041 # Initialize the socket dir once in the parent.
1042 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001043 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1044 manifest_name, load_local_manifests,
1045 ssh_proxy)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001046
1047 if opt.network_only:
1048 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001049
1050 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001051 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001052 err_network_sync = True
1053 if opt.fail_fast:
1054 print('\nerror: Exited sync due to fetch errors.\n'
1055 'Local checkouts *not* updated. Resolve network issues & '
1056 'retry.\n'
1057 '`repo sync -l` will update some local checkouts.',
1058 file=sys.stderr)
1059 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001060
Julien Campergue335f5ef2013-10-16 11:02:35 +02001061 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001062 # bail out now, we have no working tree
1063 return
1064
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001065 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001066 err_event.set()
1067 err_update_projects = True
1068 if opt.fail_fast:
1069 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1070 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001071
Mike Frysinger14208f42021-05-04 15:31:51 -04001072 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1073 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001074 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001075 if opt.fail_fast:
1076 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1077 sys.exit(1)
1078
Mike Frysinger5a033082019-09-23 19:21:20 -04001079 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001080 # NB: We don't exit here because this is the last step.
1081 err_checkout = not self._Checkout(all_projects, opt, err_results)
1082 if err_checkout:
1083 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001085 # If there's a notice that's supposed to print at the end of the sync, print
1086 # it now...
1087 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001088 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001089
Mike Frysinger5a033082019-09-23 19:21:20 -04001090 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001091 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001092 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1093 if err_network_sync:
1094 print('error: Downloading network changes failed.', file=sys.stderr)
1095 if err_update_projects:
1096 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001097 if err_update_linkfiles:
1098 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001099 if err_checkout:
1100 print('error: Checking out local projects failed.', file=sys.stderr)
1101 if err_results:
1102 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1103 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1104 file=sys.stderr)
1105 sys.exit(1)
1106
Raman Tenneti7954de12021-07-28 14:36:49 -07001107 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001108 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1109 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001110
1111 # Update and log with the new sync analysis state.
1112 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001113 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1114 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001115
Mike Frysingere19d9e12020-02-12 11:23:32 -05001116 if not opt.quiet:
1117 print('repo sync has finished successfully.')
1118
David Pursehouse819827a2020-02-12 15:20:19 +09001119
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001120def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001121 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001122 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001123 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001124 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001125 if project.Exists:
1126 project.PostRepoUpgrade()
1127
David Pursehouse819827a2020-02-12 15:20:19 +09001128
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001129def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001130 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001131 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001132 wrapper = Wrapper()
1133 try:
1134 rev = rp.bare_git.describe(rp.GetRevisionId())
1135 except GitError:
1136 rev = None
1137 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1138 # See if we're held back due to missing signed tag.
1139 current_revid = rp.bare_git.rev_parse('HEAD')
1140 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1141 if current_revid != new_revid:
1142 # We want to switch to the new rev, but also not trash any uncommitted
1143 # changes. This helps with local testing/hacking.
1144 # If a local change has been made, we will throw that away.
1145 # We also have to make sure this will switch to an older commit if that's
1146 # the latest tag in order to support release rollback.
1147 try:
1148 rp.work_git.reset('--keep', new_rev)
1149 except GitError as e:
1150 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001151 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001152 raise RepoChangedException(['--repo-upgraded'])
1153 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001154 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001155 else:
1156 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001157 print('repo version %s is current' % rp.work_git.describe(HEAD),
1158 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001159
David Pursehouse819827a2020-02-12 15:20:19 +09001160
Dave Borowitz67700e92012-10-23 15:00:54 -07001161class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001162 _ALPHA = 0.5
1163
Dave Borowitz67700e92012-10-23 15:00:54 -07001164 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001165 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001166 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001167 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001168
1169 def Get(self, project):
1170 self._Load()
1171 return self._times.get(project.name, _ONE_DAY_S)
1172
1173 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001174 self._Load()
1175 name = project.name
1176 old = self._times.get(name, t)
1177 self._seen.add(name)
1178 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001179 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001180
1181 def _Load(self):
1182 if self._times is None:
1183 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001184 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001185 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001186 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001187 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001188 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001189
1190 def Save(self):
1191 if self._times is None:
1192 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001193
1194 to_delete = []
1195 for name in self._times:
1196 if name not in self._seen:
1197 to_delete.append(name)
1198 for name in to_delete:
1199 del self._times[name]
1200
Dave Borowitz67700e92012-10-23 15:00:54 -07001201 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001202 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001203 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001204 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001205 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001206
1207# This is a replacement for xmlrpc.client.Transport using urllib2
1208# and supporting persistent-http[s]. It cannot change hosts from
1209# request to request like the normal transport, the real url
1210# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001211
1212
Dan Willemsen0745bb22015-08-17 13:41:45 -07001213class PersistentTransport(xmlrpc.client.Transport):
1214 def __init__(self, orig_host):
1215 self.orig_host = orig_host
1216
1217 def request(self, host, handler, request_body, verbose=False):
1218 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1219 # Python doesn't understand cookies with the #HttpOnly_ prefix
1220 # Since we're only using them for HTTP, copy the file temporarily,
1221 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001222 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001223 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001224 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001225 try:
1226 with open(cookiefile) as f:
1227 for line in f:
1228 if line.startswith("#HttpOnly_"):
1229 line = line[len("#HttpOnly_"):]
1230 tmpcookiefile.write(line)
1231 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001232
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001233 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001234 try:
1235 cookiejar.load()
1236 except cookielib.LoadError:
1237 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001238 finally:
1239 tmpcookiefile.close()
1240 else:
1241 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001242
1243 proxyhandler = urllib.request.ProxyHandler
1244 if proxy:
1245 proxyhandler = urllib.request.ProxyHandler({
1246 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001247 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001248
1249 opener = urllib.request.build_opener(
1250 urllib.request.HTTPCookieProcessor(cookiejar),
1251 proxyhandler)
1252
1253 url = urllib.parse.urljoin(self.orig_host, handler)
1254 parse_results = urllib.parse.urlparse(url)
1255
1256 scheme = parse_results.scheme
1257 if scheme == 'persistent-http':
1258 scheme = 'http'
1259 if scheme == 'persistent-https':
1260 # If we're proxying through persistent-https, use http. The
1261 # proxy itself will do the https.
1262 if proxy:
1263 scheme = 'http'
1264 else:
1265 scheme = 'https'
1266
1267 # Parse out any authentication information using the base class
1268 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1269
1270 url = urllib.parse.urlunparse((
1271 scheme,
1272 host,
1273 parse_results.path,
1274 parse_results.params,
1275 parse_results.query,
1276 parse_results.fragment))
1277
1278 request = urllib.request.Request(url, request_body)
1279 if extra_headers is not None:
1280 for (name, header) in extra_headers:
1281 request.add_header(name, header)
1282 request.add_header('Content-Type', 'text/xml')
1283 try:
1284 response = opener.open(request)
1285 except urllib.error.HTTPError as e:
1286 if e.code == 501:
1287 # We may have been redirected through a login process
1288 # but our POST turned into a GET. Retry.
1289 response = opener.open(request)
1290 else:
1291 raise
1292
1293 p, u = xmlrpc.client.getparser()
1294 while 1:
1295 data = response.read(1024)
1296 if not data:
1297 break
1298 p.feed(data)
1299 p.close()
1300 return u.close()
1301
1302 def close(self):
1303 pass