blob: 9a66e48bbcb76971cae1768b5db1a6609efdeee0 [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
LaMont Jonescc879a92021-11-18 22:40:18 +000069 MULTI_MANIFEST_SUPPORT = False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070070 helpSummary = "Update working tree to the latest revision"
71 helpUsage = """
72%prog [...]
73"""
74 helpDescription = """
75The '%prog' command synchronizes local project directories
76with the remote repositories specified in the manifest. If a local
77project does not yet exist, it will clone a new local directory from
78the remote repository and set up tracking branches as specified in
79the manifest. If the local project already exists, '%prog'
80will update the remote branches and rebase any new local changes
81on top of the new remote changes.
82
83'%prog' will synchronize all projects listed at the command
84line. Projects can be specified either by name, or by a relative
85or absolute path to the project's local directory. If no projects
86are specified, '%prog' will synchronize all projects listed in
87the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070088
89The -d/--detach option can be used to switch specified projects
90back to the manifest revision. This option is especially helpful
91if the project is currently on a topic branch, but the manifest
92revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070093
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070094The -s/--smart-sync option can be used to sync to a known good
95build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020096manifest. The -t/--smart-tag option is similar and allows you to
97specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070098
David Pursehousecf76b1b2012-09-14 10:31:42 +090099The -u/--manifest-server-username and -p/--manifest-server-password
100options can be used to specify a username and password to authenticate
101with the manifest server when using the -s or -t option.
102
103If -u and -p are not specified when using the -s or -t option, '%prog'
104will attempt to read authentication credentials for the manifest server
105from the user's .netrc file.
106
107'%prog' will not use authentication credentials from -u/-p or .netrc
108if the manifest server specified in the manifest file already includes
109credentials.
110
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400111By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400112to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500113
Kevin Degiabaa7f32014-11-12 11:27:45 -0700114The --force-sync option can be used to overwrite existing git
115directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900116object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700117refs may be removed when overwriting.
118
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500119The --force-remove-dirty option can be used to remove previously used
120projects with uncommitted changes. WARNING: This may cause data to be
121lost since uncommitted changes may be removed with projects that no longer
122exist in the manifest.
123
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700124The --no-clone-bundle option disables any attempt to use
125$URL/clone.bundle to bootstrap a new Git repository from a
126resumeable bundle file on a content delivery network. This
127may be necessary if there are problems with the local Python
128HTTP client or proxy configuration, but the Git binary works.
129
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800130The --fetch-submodules option enables fetching Git submodules
131of a project from server.
132
David Pursehousef2fad612015-01-29 14:36:28 +0900133The -c/--current-branch option can be used to only fetch objects that
134are on the branch specified by a project's revision.
135
David Pursehouseb1553542014-09-04 21:28:09 +0900136The --optimized-fetch option can be used to only fetch projects that
137are fixed to a sha1 revision if the sha1 revision does not already
138exist locally.
139
David Pursehouse74cfd272015-10-14 10:50:15 +0900140The --prune option can be used to remove any refs that no longer
141exist on the remote.
142
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400143# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700144
145If at least one project remote URL uses an SSH connection (ssh://,
146git+ssh://, or user@host:path syntax) repo will automatically
147enable the SSH ControlMaster option when connecting to that host.
148This feature permits other projects in the same '%prog' session to
149reuse the same SSH tunnel, saving connection setup overheads.
150
151To disable this behavior on UNIX platforms, set the GIT_SSH
152environment variable to 'ssh'. For example:
153
154 export GIT_SSH=ssh
155 %prog
156
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400157# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700158
159This feature is automatically disabled on Windows, due to the lack
160of UNIX domain socket support.
161
162This feature is not compatible with url.insteadof rewrites in the
163user's ~/.gitconfig. '%prog' is currently not able to perform the
164rewrite early enough to establish the ControlMaster tunnel.
165
166If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
167later is required to fix a server side protocol bug.
168
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500170 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171
Mike Frysinger9180a072021-04-13 14:57:40 -0400172 def _CommonOptions(self, p):
LaMont Jonesa46047a2022-04-07 21:57:06 +0000173 if self.outer_client and self.outer_client.manifest:
Mike Frysingerc177f942021-05-04 08:06:36 -0400174 try:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000175 self.PARALLEL_JOBS = self.outer_client.manifest.default.sync_j
Mike Frysingerc177f942021-05-04 08:06:36 -0400176 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',
Raman Tenneti62517292021-11-01 14:49:16 -0700238 help='use the manifest superproject to sync projects; implies -c')
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 Frysinger2273f462021-11-05 15:10:33 -0400242 p.add_option('--tags', action='store_true',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400243 help='fetch tags')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
Mike Frysingerd68ed632021-05-03 01:21:35 -0400245 dest='tags', action='store_false',
Mike Frysinger2273f462021-11-05 15:10:33 -0400246 help="don't fetch tags (default)")
David Pursehouseb1553542014-09-04 21:28:09 +0900247 p.add_option('--optimized-fetch',
248 dest='optimized_fetch', action='store_true',
249 help='only fetch projects fixed to sha1 if revision does not exist locally')
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600250 p.add_option('--retry-fetches',
251 default=0, action='store', type='int',
252 help='number of times to retry fetches on transient errors')
Mike Frysinger0531a622021-11-05 15:22:01 -0400253 p.add_option('--prune', action='store_true',
254 help='delete refs that no longer exist on the remote (default)')
255 p.add_option('--no-prune', dest='prune', action='store_false',
256 help='do not delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700257 if show_smart:
258 p.add_option('-s', '--smart-sync',
259 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900260 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200261 p.add_option('-t', '--smart-tag',
262 dest='smart_tag', action='store',
263 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700264
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700265 g = p.add_option_group('repo Version options')
266 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500267 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700268 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700269 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800270 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700271 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700272
LaMont Jonesa46047a2022-04-07 21:57:06 +0000273 def _GetBranch(self, manifest_project):
274 """Returns the branch name for getting the approved smartsync manifest.
275
276 Args:
277 manifest_project: the manifestProject to query.
278 """
279 b = manifest_project.GetBranch(manifest_project.CurrentBranch)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800280 branch = b.merge
281 if branch.startswith(R_HEADS):
282 branch = branch[len(R_HEADS):]
283 return branch
284
LaMont Jonesa46047a2022-04-07 21:57:06 +0000285 def _GetCurrentBranchOnly(self, opt, manifest):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200286 """Returns whether current-branch or use-superproject options are enabled.
287
LaMont Jonesa46047a2022-04-07 21:57:06 +0000288 Args:
289 opt: Program options returned from optparse. See _Options().
290 manifest: The manifest to use.
291
Daniel Anderssond52ca422022-04-01 12:55:38 +0200292 Returns:
293 True if a superproject is requested, otherwise the value of the
294 current_branch option (True, False or None).
295 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000296 return git_superproject.UseSuperproject(opt.use_superproject, manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700297
LaMont Jonesa46047a2022-04-07 21:57:06 +0000298 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data, manifest):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800299 """Update revisionId of every project with the SHA from superproject.
300
301 This function updates each project's revisionId with SHA from superproject.
302 It writes the updated manifest into a file and reloads the manifest from it.
303
304 Args:
305 opt: Program options returned from optparse. See _Options().
306 args: Arguments to pass to GetProjects. See the GetProjects
307 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700308 load_local_manifests: Whether to load local manifests.
Raman Tenneti7954de12021-07-28 14:36:49 -0700309 superproject_logging_data: A dictionary of superproject data that is to be logged.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000310 manifest: The manifest to use.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800311
312 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700313 Returns path to the overriding manifest file instead of None.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800314 """
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000315 superproject = self.manifest.superproject
316 superproject.SetQuiet(opt.quiet)
LaMont Jones5fa912b2022-04-14 14:41:13 +0000317 print_messages = git_superproject.PrintMessages(opt.use_superproject,
318 self.manifest)
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000319 superproject.SetPrintMessages(print_messages)
Raman Tennetiae86a462021-07-27 08:54:59 -0700320 if opt.local_only:
321 manifest_path = superproject.manifest_path
322 if manifest_path:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000323 self._ReloadManifest(manifest_path, manifest, load_local_manifests)
Raman Tennetiae86a462021-07-27 08:54:59 -0700324 return manifest_path
325
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800326 all_projects = self.GetProjects(args,
327 missing_ok=True,
328 submodules_ok=opt.fetch_submodules)
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000329 update_result = superproject.UpdateProjectsRevisionId(
330 all_projects, git_event_log=self.git_event_log)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700331 manifest_path = update_result.manifest_path
Raman Tenneti7954de12021-07-28 14:36:49 -0700332 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700333 if manifest_path:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000334 self._ReloadManifest(manifest_path, manifest, load_local_manifests)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700335 else:
Raman Tennetib55769a2021-08-13 11:47:24 -0700336 if print_messages:
337 print('warning: Update of revisionId from superproject has failed, '
338 'repo sync will not use superproject to fetch the source. ',
339 'Please resync with the --no-use-superproject option to avoid this repo warning.',
340 file=sys.stderr)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700341 if update_result.fatal and opt.use_superproject is not None:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700342 sys.exit(1)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800343 return manifest_path
344
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500345 def _FetchProjectList(self, opt, projects):
346 """Main function of the fetch worker.
347
348 The projects we're given share the same underlying git object store, so we
349 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800350
David James8d201162013-10-11 17:03:19 -0700351 Delegates most of the work to _FetchHelper.
352
353 Args:
354 opt: Program options returned from optparse. See _Options().
355 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700356 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500357 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700358
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500359 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700360 """Fetch git objects for a single project.
361
David Pursehousec1b86a22012-11-14 11:36:51 +0900362 Args:
363 opt: Program options returned from optparse. See _Options().
364 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700365
366 Returns:
367 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900368 """
David Rileye0684ad2017-04-05 00:02:59 -0700369 start = time.time()
370 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500371 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900372 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500373 success = project.Sync_NetworkHalf(
374 quiet=opt.quiet,
375 verbose=opt.verbose,
376 output_redir=buf,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000377 current_branch_only=self._GetCurrentBranchOnly(opt, project.manifest),
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500378 force_sync=opt.force_sync,
379 clone_bundle=opt.clone_bundle,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000380 tags=opt.tags, archive=project.manifest.IsArchive,
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500381 optimized_fetch=opt.optimized_fetch,
382 retry_fetches=opt.retry_fetches,
383 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400384 ssh_proxy=self.ssh_proxy,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000385 clone_filter=project.manifest.CloneFilter,
386 partial_clone_exclude=project.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700387
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500388 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400389 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500390 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700391
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500392 if not success:
393 print('error: Cannot fetch %s from %s'
394 % (project.name, project.remote.url),
395 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700396 except GitError as e:
397 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500398 except Exception as e:
399 print('error: Cannot fetch %s (%s: %s)'
400 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
401 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500402
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500403 finish = time.time()
404 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700405
Mike Frysinger339f2df2021-05-06 00:44:42 -0400406 @classmethod
407 def _FetchInitChild(cls, ssh_proxy):
408 cls.ssh_proxy = ssh_proxy
409
410 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500411 ret = True
412
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400413 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700414 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400415 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800416
David James89ece422014-01-09 18:51:58 -0800417 objdir_project_map = dict()
418 for project in projects:
419 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500420 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700421
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500422 def _ProcessResults(results_sets):
423 ret = True
424 for results in results_sets:
425 for (success, project, start, finish) in results:
426 self._fetch_times.Set(project, finish - start)
427 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
428 start, finish, success)
429 # Check for any errors before running any more tasks.
430 # ...we'll let existing jobs finish, though.
431 if not success:
432 ret = False
433 else:
434 fetched.add(project.gitdir)
435 pm.update(msg=project.name)
436 if not ret and opt.fail_fast:
437 break
438 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700439
Mike Frysinger339f2df2021-05-06 00:44:42 -0400440 # We pass the ssh proxy settings via the class. This allows multiprocessing
441 # to pickle it up when spawning children. We can't pass it as an argument
442 # to _FetchProjectList below as multiprocessing is unable to pickle those.
443 Sync.ssh_proxy = None
444
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500445 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400446 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400447 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500448 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
449 ret = False
450 else:
451 # Favor throughput over responsiveness when quiet. It seems that imap()
452 # will yield results in batches relative to chunksize, so even as the
453 # children finish a sync, we won't see the result until one child finishes
454 # ~chunksize jobs. When using a large --jobs with large chunksize, this
455 # can be jarring as there will be a large initial delay where repo looks
456 # like it isn't doing anything and sits at 0%, but then suddenly completes
457 # a lot of jobs all at once. Since this code is more network bound, we
458 # can accept a bit more CPU overhead with a smaller chunksize so that the
459 # user sees more immediate & continuous feedback.
460 if opt.quiet:
461 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800462 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500463 pm.update(inc=0, msg='warming up')
464 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800465 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
466 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500467 results = pool.imap_unordered(
468 functools.partial(self._FetchProjectList, opt),
469 projects_list,
470 chunksize=chunksize)
471 if not _ProcessResults(results):
472 ret = False
473 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800474
Mike Frysinger339f2df2021-05-06 00:44:42 -0400475 # Cleanup the reference now that we're done with it, and we're going to
476 # release any resources it points to. If we don't, later multiprocessing
477 # usage (e.g. checkouts) will try to pickle and then crash.
478 del Sync.ssh_proxy
479
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700480 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700481 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700482
LaMont Jonesa46047a2022-04-07 21:57:06 +0000483 if not self.outer_client.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400484 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200485
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500486 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700487
Mike Frysingerb4429432021-05-05 20:03:26 -0400488 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000489 load_local_manifests, ssh_proxy, manifest):
Mike Frysingerb4429432021-05-05 20:03:26 -0400490 """The main network fetch loop.
491
492 Args:
493 opt: Program options returned from optparse. See _Options().
494 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200495 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400496 err_event: Whether an error was hit while processing.
497 manifest_name: Manifest file to be reloaded.
498 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400499 ssh_proxy: SSH manager for clients & masters.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000500 manifest: The manifest to use.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200501
502 Returns:
503 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400504 """
LaMont Jonesa46047a2022-04-07 21:57:06 +0000505 rp = manifest.repoProject
Mike Frysingerb4429432021-05-05 20:03:26 -0400506
507 to_fetch = []
508 now = time.time()
509 if _ONE_DAY_S <= (now - rp.LastFetch):
510 to_fetch.append(rp)
511 to_fetch.extend(all_projects)
512 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
513
Mike Frysinger339f2df2021-05-06 00:44:42 -0400514 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400515 if not success:
516 err_event.set()
517
518 _PostRepoFetch(rp, opt.repo_verify)
519 if opt.network_only:
520 # bail out now; the rest touches the working tree
521 if err_event.is_set():
522 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
523 sys.exit(1)
524 return
525
526 # Iteratively fetch missing and/or nested unregistered submodules
527 previously_missing_set = set()
528 while True:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000529 self._ReloadManifest(manifest_name, self.manifest, load_local_manifests)
Mike Frysingerb4429432021-05-05 20:03:26 -0400530 all_projects = self.GetProjects(args,
531 missing_ok=True,
532 submodules_ok=opt.fetch_submodules)
533 missing = []
534 for project in all_projects:
535 if project.gitdir not in fetched:
536 missing.append(project)
537 if not missing:
538 break
539 # Stop us from non-stopped fetching actually-missing repos: If set of
540 # missing repos has not been changed from last fetch, we break.
541 missing_set = set(p.name for p in missing)
542 if previously_missing_set == missing_set:
543 break
544 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400545 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400546 if not success:
547 err_event.set()
548 fetched.update(new_fetched)
549
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200550 return all_projects
551
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500552 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700553 """Checkout work tree for one project
554
555 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500556 detach_head: Whether to leave a detached HEAD.
557 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700558 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700559
560 Returns:
561 Whether the fetch was successful.
562 """
Xin Li745be2e2019-06-03 11:24:30 -0700563 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000564 syncbuf = SyncBuffer(project.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500565 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700566 success = False
567 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500568 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500569 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700570 except GitError as e:
571 print('error.GitError: Cannot checkout %s: %s' %
572 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500573 except Exception as e:
574 print('error: Cannot checkout %s: %s: %s' %
575 (project.name, type(e).__name__, str(e)),
576 file=sys.stderr)
577 raise
Xin Li745be2e2019-06-03 11:24:30 -0700578
Mike Frysingerebf04a42021-02-23 20:48:04 -0500579 if not success:
580 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
581 finish = time.time()
582 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700583
Mike Frysingerebf04a42021-02-23 20:48:04 -0500584 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700585 """Checkout projects listed in all_projects
586
587 Args:
588 all_projects: List of all projects that should be checked out.
589 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500590 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700591 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500592 # Only checkout projects with worktrees.
593 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700594
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500595 def _ProcessResults(pool, pm, results):
596 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500597 for (success, project, start, finish) in results:
598 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
599 start, finish, success)
600 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500601 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500602 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500603 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500604 err_results.append(project.relpath)
605 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500606 if pool:
607 pool.close()
608 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500609 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500610 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700611
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500612 return self.ExecuteInParallel(
613 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
614 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
615 all_projects,
616 callback=_ProcessResults,
617 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500618
Mike Frysinger5a033082019-09-23 19:21:20 -0400619 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400620 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400621 pm.update(inc=0, msg='prescan')
622
Allen Webb4ee4a452021-10-07 10:42:38 -0500623 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700624 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500625 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500626 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900627 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100628 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400629 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100630 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500631 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500632 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500633 else:
634 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400635 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500636 'versions of git; please upgrade to git-2.7.0+.'
637 % (project.relpath,),
638 file=sys.stderr)
639 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500640 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500641 # Only call git gc once per objdir, but call pack-refs for the remainder.
642 if project.objdir not in tidy_dirs:
643 tidy_dirs[project.objdir] = (
644 True, # Run a full gc.
645 project.bare_git,
646 )
647 elif project.gitdir not in tidy_dirs:
648 tidy_dirs[project.gitdir] = (
649 False, # Do not run a full gc; just run pack-refs.
650 project.bare_git,
651 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400652
653 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700654 jobs = min(self.jobs, cpu_count)
655
656 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500657 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400658 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500659 if run_gc:
660 bare_git.gc('--auto')
661 else:
662 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400663 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700664 return
665
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400666 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700667
668 threads = set()
669 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700670
Allen Webb4ee4a452021-10-07 10:42:38 -0500671 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400672 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700673 try:
674 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500675 if run_gc:
676 bare_git.gc('--auto', config=config)
677 else:
678 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700679 except GitError:
680 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900681 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700682 err_event.set()
683 raise
684 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400685 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700686 sem.release()
687
Allen Webb4ee4a452021-10-07 10:42:38 -0500688 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500689 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700690 break
691 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500692 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700693 t.daemon = True
694 threads.add(t)
695 t.start()
696
697 for t in threads:
698 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400699 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700700
LaMont Jonesa46047a2022-04-07 21:57:06 +0000701 def _ReloadManifest(self, manifest_name, manifest, load_local_manifests=True):
Raman Tennetifeb28912021-05-02 19:47:29 -0700702 """Reload the manfiest from the file specified by the |manifest_name|.
703
704 It unloads the manifest if |manifest_name| is None.
705
706 Args:
707 manifest_name: Manifest file to be reloaded.
LaMont Jonesa46047a2022-04-07 21:57:06 +0000708 manifest: The manifest to use.
Raman Tennetifeb28912021-05-02 19:47:29 -0700709 load_local_manifests: Whether to load local manifests.
710 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800711 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000712 # Override calls Unload already
LaMont Jonesa46047a2022-04-07 21:57:06 +0000713 manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800714 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000715 manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800716
LaMont Jonesa46047a2022-04-07 21:57:06 +0000717 def UpdateProjectList(self, opt, manifest):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700718 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700719 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700720 if project.relpath:
721 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700722 file_name = 'project.list'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000723 file_path = os.path.join(manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700724 old_project_paths = []
725
726 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500727 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700728 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800729 # In reversed order, so subfolders are deleted before parent folder.
730 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700731 if not path:
732 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700733 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900734 # If the path has already been deleted, we don't need to do it
LaMont Jonesa46047a2022-04-07 21:57:06 +0000735 gitdir = os.path.join(manifest.topdir, path, '.git')
Dan Willemsen43507912016-09-01 16:26:02 -0700736 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900737 project = Project(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000738 manifest=manifest,
David Pursehouseabdf7502020-02-12 14:58:39 +0900739 name=path,
740 remote=RemoteSpec('origin'),
741 gitdir=gitdir,
742 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500743 use_git_worktrees=os.path.isfile(gitdir),
LaMont Jonesa46047a2022-04-07 21:57:06 +0000744 worktree=os.path.join(manifest.topdir, path),
David Pursehouseabdf7502020-02-12 14:58:39 +0900745 relpath=path,
746 revisionExpr='HEAD',
747 revisionId=None,
748 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500749 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900750 quiet=opt.quiet,
751 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400752 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700753
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700754 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500755 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700756 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700757 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700758 return 0
759
LaMont Jonesa46047a2022-04-07 21:57:06 +0000760 def UpdateCopyLinkfileList(self, manifest):
jiajia tanga590e642021-04-25 20:02:02 +0800761 """Save all dests of copyfile and linkfile, and update them if needed.
762
763 Returns:
764 Whether update was successful.
765 """
766 new_paths = {}
767 new_linkfile_paths = []
768 new_copyfile_paths = []
769 for project in self.GetProjects(None, missing_ok=True):
770 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
771 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
772
773 new_paths = {
774 'linkfile': new_linkfile_paths,
775 'copyfile': new_copyfile_paths,
776 }
777
778 copylinkfile_name = 'copy-link-files.json'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000779 copylinkfile_path = os.path.join(manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800780 old_copylinkfile_paths = {}
781
782 if os.path.exists(copylinkfile_path):
783 with open(copylinkfile_path, 'rb') as fp:
784 try:
785 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800786 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800787 print('error: %s is not a json formatted file.' %
788 copylinkfile_path, file=sys.stderr)
789 platform_utils.remove(copylinkfile_path)
790 return False
791
792 need_remove_files = []
793 need_remove_files.extend(
794 set(old_copylinkfile_paths.get('linkfile', [])) -
795 set(new_linkfile_paths))
796 need_remove_files.extend(
797 set(old_copylinkfile_paths.get('copyfile', [])) -
798 set(new_copyfile_paths))
799
800 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400801 # Try to remove the updated copyfile or linkfile.
802 # So, if the file is not exist, nothing need to do.
803 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800804
805 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
806 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
807 json.dump(new_paths, fp)
808 return True
809
LaMont Jonesa46047a2022-04-07 21:57:06 +0000810 def _SmartSyncSetup(self, opt, smart_sync_manifest_path, manifest):
811 if not manifest.manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400812 print('error: cannot smart sync: no manifest server defined in '
813 'manifest', file=sys.stderr)
814 sys.exit(1)
815
LaMont Jonesa46047a2022-04-07 21:57:06 +0000816 manifest_server = manifest.manifest_server
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400817 if not opt.quiet:
818 print('Using manifest server %s' % manifest_server)
819
David Pursehouseeeff3532020-02-12 11:24:10 +0900820 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400821 username = None
822 password = None
823 if opt.manifest_server_username and opt.manifest_server_password:
824 username = opt.manifest_server_username
825 password = opt.manifest_server_password
826 else:
827 try:
828 info = netrc.netrc()
829 except IOError:
830 # .netrc file does not exist or could not be opened
831 pass
832 else:
833 try:
834 parse_result = urllib.parse.urlparse(manifest_server)
835 if parse_result.hostname:
836 auth = info.authenticators(parse_result.hostname)
837 if auth:
838 username, _account, password = auth
839 else:
840 print('No credentials found for %s in .netrc'
841 % parse_result.hostname, file=sys.stderr)
842 except netrc.NetrcParseError as e:
843 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
844
845 if (username and password):
846 manifest_server = manifest_server.replace('://', '://%s:%s@' %
847 (username, password),
848 1)
849
850 transport = PersistentTransport(manifest_server)
851 if manifest_server.startswith('persistent-'):
852 manifest_server = manifest_server[len('persistent-'):]
853
854 try:
855 server = xmlrpc.client.Server(manifest_server, transport=transport)
856 if opt.smart_sync:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000857 branch = self._GetBranch(manifest.manifestProject)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400858
Mike Frysinger56ce3462019-12-04 19:30:48 -0500859 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500860 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400861 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500862 elif ('TARGET_PRODUCT' in os.environ and
863 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500864 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
865 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400866 [success, manifest_str] = server.GetApprovedManifest(branch, target)
867 else:
868 [success, manifest_str] = server.GetApprovedManifest(branch)
869 else:
870 assert(opt.smart_tag)
871 [success, manifest_str] = server.GetManifest(opt.smart_tag)
872
873 if success:
874 manifest_name = os.path.basename(smart_sync_manifest_path)
875 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500876 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400877 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400878 except IOError as e:
879 print('error: cannot write manifest to %s:\n%s'
880 % (smart_sync_manifest_path, e),
881 file=sys.stderr)
882 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +0000883 self._ReloadManifest(manifest_name, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400884 else:
885 print('error: manifest server RPC call failed: %s' %
886 manifest_str, file=sys.stderr)
887 sys.exit(1)
888 except (socket.error, IOError, xmlrpc.client.Fault) as e:
889 print('error: cannot connect to manifest server %s:\n%s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000890 % (manifest.manifest_server, e), file=sys.stderr)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400891 sys.exit(1)
892 except xmlrpc.client.ProtocolError as e:
893 print('error: cannot connect to manifest server %s:\n%d %s'
LaMont Jonesa46047a2022-04-07 21:57:06 +0000894 % (manifest.manifest_server, e.errcode, e.errmsg),
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400895 file=sys.stderr)
896 sys.exit(1)
897
898 return manifest_name
899
Mike Frysingerfb527e32019-08-27 02:34:32 -0400900 def _UpdateManifestProject(self, opt, mp, manifest_name):
901 """Fetch & update the local manifest project."""
902 if not opt.local_only:
903 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500904 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000905 current_branch_only=self._GetCurrentBranchOnly(opt, mp.manifest),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200906 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500907 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400908 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600909 retry_fetches=opt.retry_fetches,
LaMont Jonesa46047a2022-04-07 21:57:06 +0000910 submodules=mp.manifest.HasSubmodules,
911 clone_filter=mp.manifest.CloneFilter,
912 partial_clone_exclude=mp.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400913 finish = time.time()
914 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
915 start, finish, success)
916
917 if mp.HasChanges:
918 syncbuf = SyncBuffer(mp.config)
919 start = time.time()
LaMont Jonesa46047a2022-04-07 21:57:06 +0000920 mp.Sync_LocalHalf(syncbuf, submodules=mp.manifest.HasSubmodules)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400921 clean = syncbuf.Finish()
922 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
923 start, time.time(), clean)
924 if not clean:
925 sys.exit(1)
LaMont Jonesa46047a2022-04-07 21:57:06 +0000926 self._ReloadManifest(manifest_name, mp.manifest)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400927 if opt.jobs is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000928 self.jobs = mp.manifest.default.sync_j
Mike Frysingerfb527e32019-08-27 02:34:32 -0400929
Mike Frysingerae6cb082019-08-27 01:10:59 -0400930 def ValidateOptions(self, opt, args):
931 if opt.force_broken:
932 print('warning: -f/--force-broken is now the default behavior, and the '
933 'options are deprecated', file=sys.stderr)
934 if opt.network_only and opt.detach_head:
935 self.OptionParser.error('cannot combine -n and -d')
936 if opt.network_only and opt.local_only:
937 self.OptionParser.error('cannot combine -n and -l')
938 if opt.manifest_name and opt.smart_sync:
939 self.OptionParser.error('cannot combine -m and -s')
940 if opt.manifest_name and opt.smart_tag:
941 self.OptionParser.error('cannot combine -m and -t')
942 if opt.manifest_server_username or opt.manifest_server_password:
943 if not (opt.smart_sync or opt.smart_tag):
944 self.OptionParser.error('-u and -p may only be combined with -s or -t')
945 if None in [opt.manifest_server_username, opt.manifest_server_password]:
946 self.OptionParser.error('both -u and -p must be given')
947
Mike Frysinger0531a622021-11-05 15:22:01 -0400948 if opt.prune is None:
949 opt.prune = True
950
LaMont Jonesa46047a2022-04-07 21:57:06 +0000951 if self.outer_client.manifest.is_multimanifest and not opt.this_manifest_only and args:
LaMont Jonescc879a92021-11-18 22:40:18 +0000952 self.OptionParser.error('partial syncs must use --this-manifest-only')
953
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800955 if opt.jobs:
956 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700957 if self.jobs > 1:
958 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400959 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700960
LaMont Jonesa46047a2022-04-07 21:57:06 +0000961 manifest = self.outer_manifest
962 if opt.this_manifest_only or not opt.outer_manifest:
963 manifest = self.manifest
964
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500965 if opt.manifest_name:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000966 manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700967
Chirayu Desaia892b102013-06-11 14:18:46 +0530968 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900969 smart_sync_manifest_path = os.path.join(
LaMont Jonesa46047a2022-04-07 21:57:06 +0000970 manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530971
Xin Lid79a4bc2020-05-20 16:03:45 -0700972 if opt.clone_bundle is None:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000973 opt.clone_bundle = manifest.CloneBundle
Xin Lid79a4bc2020-05-20 16:03:45 -0700974
Victor Boivie08c880d2011-04-19 10:32:52 +0200975 if opt.smart_sync or opt.smart_tag:
LaMont Jonesa46047a2022-04-07 21:57:06 +0000976 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path, manifest)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400977 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900978 if os.path.isfile(smart_sync_manifest_path):
979 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800980 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900981 except OSError as e:
982 print('error: failed to remove existing smart sync override manifest: %s' %
983 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700984
Mike Frysingerc99322a2021-05-04 15:32:43 -0400985 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400986
LaMont Jonesa46047a2022-04-07 21:57:06 +0000987 rp = manifest.repoProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700988 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500989 cb = rp.CurrentBranch
990 if cb:
991 base = rp.GetBranch(cb).merge
992 if not base or not base.startswith('refs/heads/'):
993 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400994 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500995 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700996
LaMont Jonesa46047a2022-04-07 21:57:06 +0000997 mp = manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +0000998 is_standalone_manifest = bool(mp.standalone_manifest_url)
Jack Neus03ff2762021-10-15 15:43:19 +0000999 if not is_standalone_manifest:
1000 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001002 if opt.repo_upgraded:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001003 _PostRepoUpgrade(manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001004
Fredrik de Grootcc960972019-11-22 09:04:31 +01001005 if not opt.mp_update:
1006 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +00001007 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +01001008 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -07001009
LaMont Jonesa46047a2022-04-07 21:57:06 +00001010 load_local_manifests = not manifest.HasLocalManifests
1011 use_superproject = git_superproject.UseSuperproject(opt.use_superproject, manifest)
1012 if use_superproject and (manifest.IsMirror or manifest.IsArchive):
Raman Tenneti6bd89aa2021-11-16 11:48:09 -08001013 # Don't use superproject, because we have no working tree.
1014 use_superproject = False
Raman Tenneticf0ba482021-12-06 18:12:59 -08001015 if opt.use_superproject is not None:
1016 print('Defaulting to no-use-superproject because there is no working tree.')
Raman Tenneti7954de12021-07-28 14:36:49 -07001017 superproject_logging_data = {
1018 'superproject': use_superproject,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001019 'haslocalmanifests': bool(manifest.HasLocalManifests),
1020 'hassuperprojecttag': bool(manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -07001021 }
1022 if use_superproject:
1023 manifest_name = self._UpdateProjectsRevisionId(
LaMont Jonesa46047a2022-04-07 21:57:06 +00001024 opt, args, load_local_manifests, superproject_logging_data,
1025 manifest) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001026
Simran Basib9a1b732015-08-20 12:19:28 -07001027 if self.gitc_manifest:
1028 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -07001029 missing_ok=True)
1030 gitc_projects = []
1031 opened_projects = []
1032 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001033 if project.relpath in self.gitc_manifest.paths and \
1034 self.gitc_manifest.paths[project.relpath].old_revision:
1035 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001036 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001037 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001038
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001039 if not args:
1040 gitc_projects = None
1041
1042 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001043 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001044 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1045 if manifest_name:
1046 manifest.Override(manifest_name)
1047 else:
LaMont Jonesa46047a2022-04-07 21:57:06 +00001048 manifest.Override(manifest.manifestFile)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001049 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1050 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001051 gitc_projects)
1052 print('GITC client successfully synced.')
1053
1054 # The opened projects need to be synced as normal, therefore we
1055 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001056 # TODO: make this more reliable -- if there's a project name/path overlap,
1057 # this may choose the wrong project.
LaMont Jonesa46047a2022-04-07 21:57:06 +00001058 args = [os.path.relpath(manifest.paths[path].worktree, os.getcwd())
David Pursehouse3bcd3052017-07-10 22:42:22 +09001059 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001060 if not args:
1061 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001062 all_projects = self.GetProjects(args,
1063 missing_ok=True,
1064 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065
Mike Frysinger5a033082019-09-23 19:21:20 -04001066 err_network_sync = False
1067 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001068
LaMont Jonesa46047a2022-04-07 21:57:06 +00001069 self._fetch_times = _FetchTimes(manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001070 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001071 with multiprocessing.Manager() as manager:
1072 with ssh.ProxyManager(manager) as ssh_proxy:
1073 # Initialize the socket dir once in the parent.
1074 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001075 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1076 manifest_name, load_local_manifests,
LaMont Jonesa46047a2022-04-07 21:57:06 +00001077 ssh_proxy, manifest)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001078
1079 if opt.network_only:
1080 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001081
1082 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001083 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001084 err_network_sync = True
1085 if opt.fail_fast:
1086 print('\nerror: Exited sync due to fetch errors.\n'
1087 'Local checkouts *not* updated. Resolve network issues & '
1088 'retry.\n'
1089 '`repo sync -l` will update some local checkouts.',
1090 file=sys.stderr)
1091 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001092
LaMont Jonesa46047a2022-04-07 21:57:06 +00001093 if manifest.IsMirror or manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001094 # bail out now, we have no working tree
1095 return
1096
LaMont Jonesa46047a2022-04-07 21:57:06 +00001097 if self.UpdateProjectList(opt, manifest):
Mike Frysinger5a033082019-09-23 19:21:20 -04001098 err_event.set()
1099 err_update_projects = True
1100 if opt.fail_fast:
1101 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1102 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001103
LaMont Jonesa46047a2022-04-07 21:57:06 +00001104 err_update_linkfiles = not self.UpdateCopyLinkfileList(manifest)
Mike Frysinger14208f42021-05-04 15:31:51 -04001105 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001106 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001107 if opt.fail_fast:
1108 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1109 sys.exit(1)
1110
Mike Frysinger5a033082019-09-23 19:21:20 -04001111 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001112 # NB: We don't exit here because this is the last step.
1113 err_checkout = not self._Checkout(all_projects, opt, err_results)
1114 if err_checkout:
1115 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001116
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001117 # If there's a notice that's supposed to print at the end of the sync, print
1118 # it now...
LaMont Jonesa46047a2022-04-07 21:57:06 +00001119 if manifest.notice:
1120 print(manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001121
Mike Frysinger5a033082019-09-23 19:21:20 -04001122 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001123 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001124 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1125 if err_network_sync:
1126 print('error: Downloading network changes failed.', file=sys.stderr)
1127 if err_update_projects:
1128 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001129 if err_update_linkfiles:
1130 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001131 if err_checkout:
1132 print('error: Checking out local projects failed.', file=sys.stderr)
1133 if err_results:
1134 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1135 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1136 file=sys.stderr)
1137 sys.exit(1)
1138
Raman Tenneti7954de12021-07-28 14:36:49 -07001139 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001140 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1141 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001142
1143 # Update and log with the new sync analysis state.
1144 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001145 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1146 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001147
Mike Frysingere19d9e12020-02-12 11:23:32 -05001148 if not opt.quiet:
1149 print('repo sync has finished successfully.')
1150
David Pursehouse819827a2020-02-12 15:20:19 +09001151
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001152def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001153 # Link the docs for the internal .repo/ layout for people
1154 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1155 if not platform_utils.islink(link):
1156 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1157 try:
1158 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001159 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001160 pass
1161
Conley Owens094cdbe2014-01-30 15:09:59 -08001162 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001163 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001164 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001165 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001166 if project.Exists:
1167 project.PostRepoUpgrade()
1168
David Pursehouse819827a2020-02-12 15:20:19 +09001169
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001170def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001171 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001172 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001173 wrapper = Wrapper()
1174 try:
1175 rev = rp.bare_git.describe(rp.GetRevisionId())
1176 except GitError:
1177 rev = None
1178 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1179 # See if we're held back due to missing signed tag.
1180 current_revid = rp.bare_git.rev_parse('HEAD')
1181 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1182 if current_revid != new_revid:
1183 # We want to switch to the new rev, but also not trash any uncommitted
1184 # changes. This helps with local testing/hacking.
1185 # If a local change has been made, we will throw that away.
1186 # We also have to make sure this will switch to an older commit if that's
1187 # the latest tag in order to support release rollback.
1188 try:
1189 rp.work_git.reset('--keep', new_rev)
1190 except GitError as e:
1191 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001192 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001193 raise RepoChangedException(['--repo-upgraded'])
1194 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001195 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001196 else:
1197 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001198 print('repo version %s is current' % rp.work_git.describe(HEAD),
1199 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001200
David Pursehouse819827a2020-02-12 15:20:19 +09001201
Dave Borowitz67700e92012-10-23 15:00:54 -07001202class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001203 _ALPHA = 0.5
1204
Dave Borowitz67700e92012-10-23 15:00:54 -07001205 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001206 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001207 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001208 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001209
1210 def Get(self, project):
1211 self._Load()
1212 return self._times.get(project.name, _ONE_DAY_S)
1213
1214 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001215 self._Load()
1216 name = project.name
1217 old = self._times.get(name, t)
1218 self._seen.add(name)
1219 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001220 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001221
1222 def _Load(self):
1223 if self._times is None:
1224 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001225 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001226 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001227 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001228 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001229 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001230
1231 def Save(self):
1232 if self._times is None:
1233 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001234
1235 to_delete = []
1236 for name in self._times:
1237 if name not in self._seen:
1238 to_delete.append(name)
1239 for name in to_delete:
1240 del self._times[name]
1241
Dave Borowitz67700e92012-10-23 15:00:54 -07001242 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001243 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001244 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001245 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001246 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001247
1248# This is a replacement for xmlrpc.client.Transport using urllib2
1249# and supporting persistent-http[s]. It cannot change hosts from
1250# request to request like the normal transport, the real url
1251# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001252
1253
Dan Willemsen0745bb22015-08-17 13:41:45 -07001254class PersistentTransport(xmlrpc.client.Transport):
1255 def __init__(self, orig_host):
1256 self.orig_host = orig_host
1257
1258 def request(self, host, handler, request_body, verbose=False):
1259 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1260 # Python doesn't understand cookies with the #HttpOnly_ prefix
1261 # Since we're only using them for HTTP, copy the file temporarily,
1262 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001263 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001264 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001265 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001266 try:
1267 with open(cookiefile) as f:
1268 for line in f:
1269 if line.startswith("#HttpOnly_"):
1270 line = line[len("#HttpOnly_"):]
1271 tmpcookiefile.write(line)
1272 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001273
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001274 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001275 try:
1276 cookiejar.load()
1277 except cookielib.LoadError:
1278 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001279 finally:
1280 tmpcookiefile.close()
1281 else:
1282 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001283
1284 proxyhandler = urllib.request.ProxyHandler
1285 if proxy:
1286 proxyhandler = urllib.request.ProxyHandler({
1287 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001288 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001289
1290 opener = urllib.request.build_opener(
1291 urllib.request.HTTPCookieProcessor(cookiejar),
1292 proxyhandler)
1293
1294 url = urllib.parse.urljoin(self.orig_host, handler)
1295 parse_results = urllib.parse.urlparse(url)
1296
1297 scheme = parse_results.scheme
1298 if scheme == 'persistent-http':
1299 scheme = 'http'
1300 if scheme == 'persistent-https':
1301 # If we're proxying through persistent-https, use http. The
1302 # proxy itself will do the https.
1303 if proxy:
1304 scheme = 'http'
1305 else:
1306 scheme = 'https'
1307
1308 # Parse out any authentication information using the base class
1309 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1310
1311 url = urllib.parse.urlunparse((
1312 scheme,
1313 host,
1314 parse_results.path,
1315 parse_results.params,
1316 parse_results.query,
1317 parse_results.fragment))
1318
1319 request = urllib.request.Request(url, request_body)
1320 if extra_headers is not None:
1321 for (name, header) in extra_headers:
1322 request.add_header(name, header)
1323 request.add_header('Content-Type', 'text/xml')
1324 try:
1325 response = opener.open(request)
1326 except urllib.error.HTTPError as e:
1327 if e.code == 501:
1328 # We may have been redirected through a login process
1329 # but our POST turned into a GET. Retry.
1330 response = opener.open(request)
1331 else:
1332 raise
1333
1334 p, u = xmlrpc.client.getparser()
1335 while 1:
1336 data = response.read(1024)
1337 if not data:
1338 break
1339 p.feed(data)
1340 p.close()
1341 return u.close()
1342
1343 def close(self):
1344 pass