blob: 2e6afccc4f9d749c53839b70716b6da007ef2dcc [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 [...]
72"""
73 helpDescription = """
74The '%prog' command synchronizes local project directories
75with the remote repositories specified in the manifest. If a local
76project does not yet exist, it will clone a new local directory from
77the remote repository and set up tracking branches as specified in
78the manifest. If the local project already exists, '%prog'
79will update the remote branches and rebase any new local changes
80on top of the new remote changes.
81
82'%prog' will synchronize all projects listed at the command
83line. Projects can be specified either by name, or by a relative
84or absolute path to the project's local directory. If no projects
85are specified, '%prog' will synchronize all projects listed in
86the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070087
88The -d/--detach option can be used to switch specified projects
89back to the manifest revision. This option is especially helpful
90if the project is currently on a topic branch, but the manifest
91revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070092
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070093The -s/--smart-sync option can be used to sync to a known good
94build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020095manifest. The -t/--smart-tag option is similar and allows you to
96specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070097
David Pursehousecf76b1b2012-09-14 10:31:42 +090098The -u/--manifest-server-username and -p/--manifest-server-password
99options can be used to specify a username and password to authenticate
100with the manifest server when using the -s or -t option.
101
102If -u and -p are not specified when using the -s or -t option, '%prog'
103will attempt to read authentication credentials for the manifest server
104from the user's .netrc file.
105
106'%prog' will not use authentication credentials from -u/-p or .netrc
107if the manifest server specified in the manifest file already includes
108credentials.
109
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400110By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400111to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500112
Kevin Degiabaa7f32014-11-12 11:27:45 -0700113The --force-sync option can be used to overwrite existing git
114directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900115object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700116refs may be removed when overwriting.
117
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500118The --force-remove-dirty option can be used to remove previously used
119projects with uncommitted changes. WARNING: This may cause data to be
120lost since uncommitted changes may be removed with projects that no longer
121exist in the manifest.
122
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700123The --no-clone-bundle option disables any attempt to use
124$URL/clone.bundle to bootstrap a new Git repository from a
125resumeable bundle file on a content delivery network. This
126may be necessary if there are problems with the local Python
127HTTP client or proxy configuration, but the Git binary works.
128
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800129The --fetch-submodules option enables fetching Git submodules
130of a project from server.
131
David Pursehousef2fad612015-01-29 14:36:28 +0900132The -c/--current-branch option can be used to only fetch objects that
133are on the branch specified by a project's revision.
134
David Pursehouseb1553542014-09-04 21:28:09 +0900135The --optimized-fetch option can be used to only fetch projects that
136are fixed to a sha1 revision if the sha1 revision does not already
137exist locally.
138
David Pursehouse74cfd272015-10-14 10:50:15 +0900139The --prune option can be used to remove any refs that no longer
140exist on the remote.
141
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400142# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700143
144If at least one project remote URL uses an SSH connection (ssh://,
145git+ssh://, or user@host:path syntax) repo will automatically
146enable the SSH ControlMaster option when connecting to that host.
147This feature permits other projects in the same '%prog' session to
148reuse the same SSH tunnel, saving connection setup overheads.
149
150To disable this behavior on UNIX platforms, set the GIT_SSH
151environment variable to 'ssh'. For example:
152
153 export GIT_SSH=ssh
154 %prog
155
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400156# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700157
158This feature is automatically disabled on Windows, due to the lack
159of UNIX domain socket support.
160
161This feature is not compatible with url.insteadof rewrites in the
162user's ~/.gitconfig. '%prog' is currently not able to perform the
163rewrite early enough to establish the ControlMaster tunnel.
164
165If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
166later is required to fix a server side protocol bug.
167
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700168"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500169 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170
Mike Frysinger9180a072021-04-13 14:57:40 -0400171 def _CommonOptions(self, p):
Mike Frysingerc177f942021-05-04 08:06:36 -0400172 if self.manifest:
173 try:
174 self.PARALLEL_JOBS = self.manifest.default.sync_j
175 except ManifestParseError:
176 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400177 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700178
Mike Frysinger9180a072021-04-13 14:57:40 -0400179 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400180 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
181 help='number of network jobs to run in parallel (defaults to --jobs)')
182 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
183 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
184
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500185 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200186 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400187 help='obsolete option (to be deleted in the future)')
188 p.add_option('--fail-fast',
189 dest='fail_fast', action='store_true',
190 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700191 p.add_option('--force-sync',
192 dest='force_sync', action='store_true',
193 help="overwrite an existing git directory if it needs to "
194 "point to a different object directory. WARNING: this "
195 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500196 p.add_option('--force-remove-dirty',
197 dest='force_remove_dirty', action='store_true',
198 help="force remove projects with uncommitted modifications if "
199 "projects no longer exist in the manifest. "
200 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900201 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700202 dest='local_only', action='store_true',
203 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900204 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100205 dest='mp_update', action='store_false', default='true',
206 help='use the existing manifest checkout as-is. '
207 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900208 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700209 dest='network_only', action='store_true',
210 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700212 dest='detach_head', action='store_true',
213 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700215 dest='current_branch_only', action='store_true',
216 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400217 p.add_option('--no-current-branch',
218 dest='current_branch_only', action='store_false',
219 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500220 p.add_option('-m', '--manifest-name',
221 dest='manifest_name',
222 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700223 p.add_option('--clone-bundle', action='store_true',
224 help='enable use of /clone.bundle on HTTP/HTTPS')
225 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700226 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800227 p.add_option('-u', '--manifest-server-username', action='store',
228 dest='manifest_server_username',
229 help='username to authenticate with the manifest server')
230 p.add_option('-p', '--manifest-server-password', action='store',
231 dest='manifest_server_password',
232 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800233 p.add_option('--fetch-submodules',
234 dest='fetch_submodules', action='store_true',
235 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800236 p.add_option('--use-superproject', action='store_true',
Raman Tenneti62517292021-11-01 14:49:16 -0700237 help='use the manifest superproject to sync projects; implies -c')
Raman Tenneti23ea7542021-05-07 14:01:54 -0700238 p.add_option('--no-use-superproject', action='store_false',
239 dest='use_superproject',
240 help='disable use of manifest superprojects')
Mike Frysinger2273f462021-11-05 15:10:33 -0400241 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400242 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700243 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400244 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400245 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900246 p.add_option('--optimized-fetch',
247 dest='optimized_fetch', action='store_true',
248 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600249 p.add_option('--retry-fetches',
250 default=0, action='store', type='int',
251 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400252 p.add_option('--prune', action='store_true',
253 help='delete refs that no longer exist on the remote (default)')
254 p.add_option('--no-prune', dest='prune', action='store_false',
255 help='do not 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
Mike Frysinger0531a622021-11-05 15:22:01 -0400932 if opt.prune is None:
933 opt.prune = True
934
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800936 if opt.jobs:
937 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700938 if self.jobs > 1:
939 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400940 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700941
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500942 if opt.manifest_name:
943 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700944
Chirayu Desaia892b102013-06-11 14:18:46 +0530945 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900946 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900947 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530948
Xin Lid79a4bc2020-05-20 16:03:45 -0700949 if opt.clone_bundle is None:
950 opt.clone_bundle = self.manifest.CloneBundle
951
Victor Boivie08c880d2011-04-19 10:32:52 +0200952 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400953 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
954 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900955 if os.path.isfile(smart_sync_manifest_path):
956 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800957 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900958 except OSError as e:
959 print('error: failed to remove existing smart sync override manifest: %s' %
960 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700961
Mike Frysingerc99322a2021-05-04 15:32:43 -0400962 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400963
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964 rp = self.manifest.repoProject
965 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500966 cb = rp.CurrentBranch
967 if cb:
968 base = rp.GetBranch(cb).merge
969 if not base or not base.startswith('refs/heads/'):
970 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400971 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500972 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700973
974 mp = self.manifest.manifestProject
Jack Neus03ff2762021-10-15 15:43:19 +0000975 is_standalone_manifest = mp.config.GetString('manifest.standalone')
976 if not is_standalone_manifest:
977 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700978
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800979 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700980 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800981
Fredrik de Grootcc960972019-11-22 09:04:31 +0100982 if not opt.mp_update:
983 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +0000984 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +0100985 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700986
Raman Tennetifeb28912021-05-02 19:47:29 -0700987 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti7954de12021-07-28 14:36:49 -0700988 use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
989 superproject_logging_data = {
990 'superproject': use_superproject,
991 'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Raman Tennetib55769a2021-08-13 11:47:24 -0700992 'hassuperprojecttag': bool(self.manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -0700993 }
994 if use_superproject:
995 manifest_name = self._UpdateProjectsRevisionId(
996 opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800997
Simran Basib9a1b732015-08-20 12:19:28 -0700998 if self.gitc_manifest:
999 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -07001000 missing_ok=True)
1001 gitc_projects = []
1002 opened_projects = []
1003 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001004 if project.relpath in self.gitc_manifest.paths and \
1005 self.gitc_manifest.paths[project.relpath].old_revision:
1006 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001007 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001008 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001009
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001010 if not args:
1011 gitc_projects = None
1012
1013 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001014 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001015 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1016 if manifest_name:
1017 manifest.Override(manifest_name)
1018 else:
1019 manifest.Override(self.manifest.manifestFile)
1020 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1021 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001022 gitc_projects)
1023 print('GITC client successfully synced.')
1024
1025 # The opened projects need to be synced as normal, therefore we
1026 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001027 # TODO: make this more reliable -- if there's a project name/path overlap,
1028 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +09001029 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
1030 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001031 if not args:
1032 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001033 all_projects = self.GetProjects(args,
1034 missing_ok=True,
1035 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036
Mike Frysinger5a033082019-09-23 19:21:20 -04001037 err_network_sync = False
1038 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001039
Dave Borowitz67700e92012-10-23 15:00:54 -07001040 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001041 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001042 with multiprocessing.Manager() as manager:
1043 with ssh.ProxyManager(manager) as ssh_proxy:
1044 # Initialize the socket dir once in the parent.
1045 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001046 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1047 manifest_name, load_local_manifests,
1048 ssh_proxy)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001049
1050 if opt.network_only:
1051 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001052
1053 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001054 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001055 err_network_sync = True
1056 if opt.fail_fast:
1057 print('\nerror: Exited sync due to fetch errors.\n'
1058 'Local checkouts *not* updated. Resolve network issues & '
1059 'retry.\n'
1060 '`repo sync -l` will update some local checkouts.',
1061 file=sys.stderr)
1062 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001063
Julien Campergue335f5ef2013-10-16 11:02:35 +02001064 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001065 # bail out now, we have no working tree
1066 return
1067
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001068 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001069 err_event.set()
1070 err_update_projects = True
1071 if opt.fail_fast:
1072 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1073 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001074
Mike Frysinger14208f42021-05-04 15:31:51 -04001075 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1076 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001077 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001078 if opt.fail_fast:
1079 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1080 sys.exit(1)
1081
Mike Frysinger5a033082019-09-23 19:21:20 -04001082 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001083 # NB: We don't exit here because this is the last step.
1084 err_checkout = not self._Checkout(all_projects, opt, err_results)
1085 if err_checkout:
1086 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001088 # If there's a notice that's supposed to print at the end of the sync, print
1089 # it now...
1090 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001091 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001092
Mike Frysinger5a033082019-09-23 19:21:20 -04001093 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001094 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001095 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1096 if err_network_sync:
1097 print('error: Downloading network changes failed.', file=sys.stderr)
1098 if err_update_projects:
1099 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001100 if err_update_linkfiles:
1101 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001102 if err_checkout:
1103 print('error: Checking out local projects failed.', file=sys.stderr)
1104 if err_results:
1105 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1106 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1107 file=sys.stderr)
1108 sys.exit(1)
1109
Raman Tenneti7954de12021-07-28 14:36:49 -07001110 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001111 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1112 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001113
1114 # Update and log with the new sync analysis state.
1115 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001116 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1117 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001118
Mike Frysingere19d9e12020-02-12 11:23:32 -05001119 if not opt.quiet:
1120 print('repo sync has finished successfully.')
1121
David Pursehouse819827a2020-02-12 15:20:19 +09001122
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001123def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -08001124 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001125 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001126 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001127 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001128 if project.Exists:
1129 project.PostRepoUpgrade()
1130
David Pursehouse819827a2020-02-12 15:20:19 +09001131
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001132def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001133 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001134 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001135 wrapper = Wrapper()
1136 try:
1137 rev = rp.bare_git.describe(rp.GetRevisionId())
1138 except GitError:
1139 rev = None
1140 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1141 # See if we're held back due to missing signed tag.
1142 current_revid = rp.bare_git.rev_parse('HEAD')
1143 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1144 if current_revid != new_revid:
1145 # We want to switch to the new rev, but also not trash any uncommitted
1146 # changes. This helps with local testing/hacking.
1147 # If a local change has been made, we will throw that away.
1148 # We also have to make sure this will switch to an older commit if that's
1149 # the latest tag in order to support release rollback.
1150 try:
1151 rp.work_git.reset('--keep', new_rev)
1152 except GitError as e:
1153 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001154 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001155 raise RepoChangedException(['--repo-upgraded'])
1156 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001157 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001158 else:
1159 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001160 print('repo version %s is current' % rp.work_git.describe(HEAD),
1161 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001162
David Pursehouse819827a2020-02-12 15:20:19 +09001163
Dave Borowitz67700e92012-10-23 15:00:54 -07001164class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001165 _ALPHA = 0.5
1166
Dave Borowitz67700e92012-10-23 15:00:54 -07001167 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001168 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001169 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001170 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001171
1172 def Get(self, project):
1173 self._Load()
1174 return self._times.get(project.name, _ONE_DAY_S)
1175
1176 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001177 self._Load()
1178 name = project.name
1179 old = self._times.get(name, t)
1180 self._seen.add(name)
1181 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001182 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001183
1184 def _Load(self):
1185 if self._times is None:
1186 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001187 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001188 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001189 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001190 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001191 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001192
1193 def Save(self):
1194 if self._times is None:
1195 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001196
1197 to_delete = []
1198 for name in self._times:
1199 if name not in self._seen:
1200 to_delete.append(name)
1201 for name in to_delete:
1202 del self._times[name]
1203
Dave Borowitz67700e92012-10-23 15:00:54 -07001204 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001205 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001206 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001207 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001208 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001209
1210# This is a replacement for xmlrpc.client.Transport using urllib2
1211# and supporting persistent-http[s]. It cannot change hosts from
1212# request to request like the normal transport, the real url
1213# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001214
1215
Dan Willemsen0745bb22015-08-17 13:41:45 -07001216class PersistentTransport(xmlrpc.client.Transport):
1217 def __init__(self, orig_host):
1218 self.orig_host = orig_host
1219
1220 def request(self, host, handler, request_body, verbose=False):
1221 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1222 # Python doesn't understand cookies with the #HttpOnly_ prefix
1223 # Since we're only using them for HTTP, copy the file temporarily,
1224 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001225 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001226 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001227 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001228 try:
1229 with open(cookiefile) as f:
1230 for line in f:
1231 if line.startswith("#HttpOnly_"):
1232 line = line[len("#HttpOnly_"):]
1233 tmpcookiefile.write(line)
1234 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001235
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001236 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001237 try:
1238 cookiejar.load()
1239 except cookielib.LoadError:
1240 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001241 finally:
1242 tmpcookiefile.close()
1243 else:
1244 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001245
1246 proxyhandler = urllib.request.ProxyHandler
1247 if proxy:
1248 proxyhandler = urllib.request.ProxyHandler({
1249 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001250 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001251
1252 opener = urllib.request.build_opener(
1253 urllib.request.HTTPCookieProcessor(cookiejar),
1254 proxyhandler)
1255
1256 url = urllib.parse.urljoin(self.orig_host, handler)
1257 parse_results = urllib.parse.urlparse(url)
1258
1259 scheme = parse_results.scheme
1260 if scheme == 'persistent-http':
1261 scheme = 'http'
1262 if scheme == 'persistent-https':
1263 # If we're proxying through persistent-https, use http. The
1264 # proxy itself will do the https.
1265 if proxy:
1266 scheme = 'http'
1267 else:
1268 scheme = 'https'
1269
1270 # Parse out any authentication information using the base class
1271 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1272
1273 url = urllib.parse.urlunparse((
1274 scheme,
1275 host,
1276 parse_results.path,
1277 parse_results.params,
1278 parse_results.query,
1279 parse_results.fragment))
1280
1281 request = urllib.request.Request(url, request_body)
1282 if extra_headers is not None:
1283 for (name, header) in extra_headers:
1284 request.add_header(name, header)
1285 request.add_header('Content-Type', 'text/xml')
1286 try:
1287 response = opener.open(request)
1288 except urllib.error.HTTPError as e:
1289 if e.code == 501:
1290 # We may have been redirected through a login process
1291 # but our POST turned into a GET. Retry.
1292 response = opener.open(request)
1293 else:
1294 raise
1295
1296 p, u = xmlrpc.client.getparser()
1297 while 1:
1298 data = response.read(1024)
1299 if not data:
1300 break
1301 p.feed(data)
1302 p.close()
1303 return u.close()
1304
1305 def close(self):
1306 pass